diff --git a/dist/Breakpoints.d.ts b/dist/Breakpoints.d.ts new file mode 100644 index 0000000..489c846 --- /dev/null +++ b/dist/Breakpoints.d.ts @@ -0,0 +1,43 @@ +import { MediaBreakpointProps } from "./Media"; +/** + * A union of possible breakpoint props. + */ +export type BreakpointConstraintKey = keyof MediaBreakpointProps; +type ValueBreakpointPropsTuple = [ + SizeValue, + MediaBreakpointProps +]; +export declare enum BreakpointConstraint { + at = "at", + lessThan = "lessThan", + greaterThan = "greaterThan", + greaterThanOrEqual = "greaterThanOrEqual", + between = "between" +} +/** + * Encapsulates all breakpoint data needed by the Media component. The data is + * generated on initialization so no further runtime work is necessary. + */ +export declare class Breakpoints { + static validKeys(): BreakpointConstraint[]; + private _sortedBreakpoints; + private _breakpoints; + private _mediaQueries; + constructor(breakpoints: { + [key: string]: number; + }); + get sortedBreakpoints(): BreakpointKey[]; + get dynamicResponsiveMediaQueries(): {}; + get largestBreakpoint(): string; + findBreakpointsForWidths: (fromWidth: number, throughWidth: number) => BreakpointKey[] | undefined; + findBreakpointAtWidth: (width: number) => BreakpointKey | undefined; + toVisibleAtBreakpointSet(breakpointProps: MediaBreakpointProps): BreakpointKey[]; + toRuleSets(keys?: BreakpointConstraint[]): string[]; + shouldRenderMediaQuery(breakpointProps: MediaBreakpointProps, onlyRenderAt: string[]): boolean; + valuesWithBreakpointProps: (values: SizeValue[]) => ValueBreakpointPropsTuple[]; + private _normalizeProps; + private _createBreakpointQuery; + private _createBreakpointQueries; + private _findNextBreakpoint; +} +export {}; diff --git a/dist/DynamicResponsive.d.ts b/dist/DynamicResponsive.d.ts new file mode 100644 index 0000000..c7eddac --- /dev/null +++ b/dist/DynamicResponsive.d.ts @@ -0,0 +1,72 @@ +/** + * TODO: This is the deprecated runtime media-query component from Reaction. + * It can probably be simplified somewhat if we’re not going to be using + * it directly any longer. + */ +import React from "react"; +/** TODO */ +export type MediaQueries = { + [K in M]: string; +}; +/** TODO */ +export interface MediaQueryMatchers { + [key: string]: MediaQueryList; +} +/** TODO */ +export type MediaQueryMatches = { + [K in M]: boolean; +}; +/** TODO */ +export interface ResponsiveProviderProps { + mediaQueries: MediaQueries; + initialMatchingMediaQueries?: M[]; + children: React.ReactNode; +} +/** TODO */ +export interface ResponsiveProviderState { + mediaQueryMatchers?: MediaQueryMatchers; + mediaQueryMatches: MediaQueryMatches; +} +/** TODO */ +export declare function createResponsiveComponents(): { + Consumer: React.FunctionComponent>>; + Provider: { + new (props: ResponsiveProviderProps): { + isSupportedEnvironment: () => boolean; + /** + * Create an array of media matchers that can validate each media query + */ + setupMatchers: (mediaQueries: MediaQueries) => MediaQueryMatchers; + /** + * Uses the matchers to build a map of the states of each media query + */ + checkMatchers: (mediaQueryMatchers: MediaQueryMatchers) => MediaQueryMatches; + /** + * The function that will be called any time a media query status changes + */ + mediaQueryStatusChangedCallback: () => void; + componentDidMount(): void; + componentWillUnmount(): void; + shouldComponentUpdate(nextProps: Readonly>, nextState: Readonly): boolean; + render(): JSX.Element; + context: unknown; + setState(state: ResponsiveProviderState | ((prevState: Readonly, props: Readonly>) => ResponsiveProviderState | Pick | null) | Pick | null, callback?: (() => void) | undefined): void; + forceUpdate(callback?: (() => void) | undefined): void; + readonly props: Readonly>; + state: Readonly; + refs: { + [key: string]: React.ReactInstance; + }; + componentDidCatch?(error: Error, errorInfo: React.ErrorInfo): void; + getSnapshotBeforeUpdate?(prevProps: Readonly>, prevState: Readonly): any; + componentDidUpdate?(prevProps: Readonly>, prevState: Readonly, snapshot?: any): void; + componentWillMount?(): void; + UNSAFE_componentWillMount?(): void; + componentWillReceiveProps?(nextProps: Readonly>, nextContext: any): void; + UNSAFE_componentWillReceiveProps?(nextProps: Readonly>, nextContext: any): void; + componentWillUpdate?(nextProps: Readonly>, nextState: Readonly, nextContext: any): void; + UNSAFE_componentWillUpdate?(nextProps: Readonly>, nextState: Readonly, nextContext: any): void; + }; + contextType?: React.Context | undefined; + }; +}; diff --git a/dist/Interactions.d.ts b/dist/Interactions.d.ts new file mode 100644 index 0000000..6260e94 --- /dev/null +++ b/dist/Interactions.d.ts @@ -0,0 +1,18 @@ +export declare enum InteractionKey { + interaction = "interaction" +} +/** + * Encapsulates all interaction data needed by the Media component. The data is + * generated on initialization so no further runtime work is necessary. + */ +export declare class Interactions { + static validKeys(): InteractionKey[]; + private _interactions; + constructor(interactions: { + [name: string]: string; + }); + toRuleSets(): string[]; + get interactions(): string[]; + get dynamicResponsiveMediaQueries(): {}; + shouldRenderMediaQuery(interaction: string, onlyMatch: string[]): boolean; +} diff --git a/dist/Media.d.ts b/dist/Media.d.ts new file mode 100644 index 0000000..6841c2f --- /dev/null +++ b/dist/Media.d.ts @@ -0,0 +1,282 @@ +import React, { CSSProperties } from "react"; +import { BreakpointConstraint } from "./Breakpoints"; +/** + * A render prop that can be used to render a different container element than + * the default `div`. + * + * @see {@link MediaProps.children}. + */ +export type RenderProp = (className: string, renderChildren: boolean) => React.ReactNode; +export interface MediaBreakpointProps { + /** + * Children will only be shown if the viewport matches the specified + * breakpoint. That is, a viewport width that’s higher than the configured + * breakpoint value, but lower than the value of the next breakpoint, if any + * larger breakpoints exist at all. + * + * @example + + ```tsx + // With breakpoints defined like these + { xs: 0, sm: 768, md: 1024 } + + // Matches a viewport that has a width between 0 and 768 + ohai + + // Matches a viewport that has a width between 768 and 1024 + ohai + + // Matches a viewport that has a width over 1024 + ohai + ``` + * + */ + at?: BreakpointKey; + /** + * Children will only be shown if the viewport is smaller than the specified + * breakpoint. + * + * @example + + ```tsx + // With breakpoints defined like these + { xs: 0, sm: 768, md: 1024 } + + // Matches a viewport that has a width from 0 to 767 + ohai + + // Matches a viewport that has a width from 0 to 1023 + ohai + ``` + * + */ + lessThan?: BreakpointKey; + /** + * Children will only be shown if the viewport is greater than the specified + * breakpoint. + * + * @example + + ```tsx + // With breakpoints defined like these + { xs: 0, sm: 768, md: 1024 } + + // Matches a viewport that has a width from 768 to infinity + ohai + + // Matches a viewport that has a width from 1024 to infinity + ohai + ``` + * + */ + greaterThan?: BreakpointKey; + /** + * Children will only be shown if the viewport is greater or equal to the + * specified breakpoint. + * + * @example + + ```tsx + // With breakpoints defined like these + { xs: 0, sm: 768, md: 1024 } + + // Matches a viewport that has a width from 0 to infinity + ohai + + // Matches a viewport that has a width from 768 to infinity + ohai + + // Matches a viewport that has a width from 1024 to infinity + ohai + ``` + * + */ + greaterThanOrEqual?: BreakpointKey; + /** + * Children will only be shown if the viewport is between the specified + * breakpoints. That is, a viewport width that’s higher than or equal to the + * small breakpoint value, but lower than the value of the large breakpoint. + * + * @example + + ```tsx + // With breakpoints defined like these + { xs: 0, sm: 768, md: 1024 } + + // Matches a viewport that has a width from 0 to 767 + ohai + + // Matches a viewport that has a width from 0 to 1023 + ohai + ``` + * + */ + between?: [BreakpointKey, BreakpointKey]; +} +export interface MediaProps extends MediaBreakpointProps { + /** + * Children will only be shown if the interaction query matches. + * + * @example + + ```tsx + // With interactions defined like these + { hover: "(hover: hover)" } + + // Matches an input device that is capable of hovering + ohai + ``` + */ + interaction?: Interaction; + /** + * The component(s) that should conditionally be shown, depending on the media + * query matching. + * + * In case a different element is preferred, a render prop can be provided + * that receives the class-name it should use to have the media query styling + * applied. + * + * Additionally, the render prop receives a boolean that indicates wether or + * not its children should be rendered, which will be `false` if the media + * query is not included in the `onlyMatch` list. Use this flag if your + * component’s children may be expensive to render and you want to avoid any + * unnecessary work. + * (@see {@link MediaContextProviderProps.onlyMatch} for details) + * + * @example + * + ```tsx + const Component = () => ( + + {(className, renderChildren) => ( + + {renderChildren && "ohai"} + + )} + + ) + ``` + * + */ + children: React.ReactNode | RenderProp; + /** + * Additional classNames to passed down and applied to Media container + */ + className?: string; + /** + * Additional styles to passed down and applied to Media container + */ + style?: CSSProperties; +} +export interface MediaContextProviderProps { + /** + * This list of breakpoints and interactions can be used to limit the rendered + * output to these. + * + * For instance, when a server knows for some user-agents that certain + * breakpoints will never apply, omitting them altogether will lower the + * rendered byte size. + */ + onlyMatch?: M[]; + /** + * Disables usage of browser MediaQuery API to only render at the current + * breakpoint. + * + * Use this with caution, as disabling this means React components for all + * breakpoints will be mounted client-side and all associated life-cycle hooks + * will be triggered, which could lead to unintended side-effects. + */ + disableDynamicMediaQueries?: boolean; +} +export interface CreateMediaConfig { + /** + * The breakpoint definitions for your application. Width definitions should + * start at 0. + * + * @see {@link createMedia} + */ + breakpoints: { + [key: string]: number | string; + }; + /** + * The interaction definitions for your application. + */ + interactions?: { + [key: string]: string; + }; +} +export interface CreateMediaResults { + /** + * The React component that you use throughout your application. + * + * @see {@link MediaBreakpointProps} + */ + Media: React.ComponentType>; + /** + * The React Context provider component that you use to constrain rendering of + * breakpoints to a set list and to enable client-side dynamic constraining. + * + * @see {@link MediaContextProviderProps} + */ + MediaContextProvider: React.ComponentType & { + children: React.ReactNode; + }>; + /** + * Generates a set of CSS rules that you should include in your application’s + * styling to enable the hiding behaviour of your `Media` component uses. + */ + createMediaStyle(breakpointKeys?: BreakpointConstraint[]): string; + /** + * A list of your application’s breakpoints sorted from small to large. + */ + SortedBreakpoints: BreakpointKey[]; + /** + * Creates a list of your application’s breakpoints that support the given + * widths and everything in between. + */ + findBreakpointsForWidths(fromWidth: number, throughWidth: number): BreakpointKey[] | undefined; + /** + * Finds the breakpoint that matches the given width. + */ + findBreakpointAtWidth(width: number): BreakpointKey | undefined; + /** + * Maps a list of values for various breakpoints to props that can be used + * with the `Media` component. + * + * The values map to corresponding indices in the sorted breakpoints array. If + * less values are specified than the number of breakpoints your application + * has, the last value will be applied to all subsequent breakpoints. + */ + valuesWithBreakpointProps(values: SizeValue[]): [SizeValue, MediaBreakpointProps][]; +} +/** + * This is used to generate a Media component, its context provider, and CSS + * rules based on your application’s breakpoints and interactions. + * + * Note that the interaction queries are entirely up to you to define and they + * should be written in such a way that they match when you want the element to + * be hidden. + * + * @example + * + ```tsx + const MyAppMedia = createMedia({ + breakpoints: { + xs: 0, + sm: 768, + md: 900 + lg: 1024, + xl: 1192, + }, + interactions: { + hover: `not all and (hover:hover)` + }, + }) + + export const Media = MyAppMedia.Media + export const MediaContextProvider = MyAppMedia.MediaContextProvider + export const createMediaStyle = MyAppMedia.createMediaStyle + ``` + * + */ +export declare function createMedia(config: MediaConfig): CreateMediaResults; diff --git a/dist/Media.js b/dist/Media.js index 96d7d71..c1212ee 100644 --- a/dist/Media.js +++ b/dist/Media.js @@ -199,7 +199,7 @@ function createMedia(config) { return _react.default.createElement("div", { className: ["fresnel-container", className, passedClassName].filter(Boolean).join(" "), style: style, - suppressHydrationWarning: !renderChildren + suppressHydrationWarning: true }, renderChildren ? props.children : null); } }()); diff --git a/dist/Media.js.map b/dist/Media.js.map index f44cef5..c12ae6f 100644 --- a/dist/Media.js.map +++ b/dist/Media.js.map @@ -1 +1 @@ -{"version":3,"sources":["../src/Media.tsx"],"names":["createMedia","config","breakpoints","mediaQueries","MediaQueries","interactions","DynamicResponsive","MediaContext","React","createContext","displayName","MediaParentContext","hasParentMedia","breakpointProps","getMediaContextValue","onlyMatch","MediaContextProvider","disableDynamicMediaQueries","children","MediaContextValue","dynamicResponsiveMediaQueries","mediaQueryTypes","matches","matchingMediaQueries","Object","keys","filter","key","Media","props","validateProps","passedClassName","className","style","interaction","getMediaParentContextValue","useMemo","newBreakpointProps","mediaParentContext","useContext","childMediaParentContext","id","useId","isClient","window","isFirstRender","at","largestBreakpoint","console","warn","type","breakpoint","doesMatchParent","toVisibleAtBreakpointSet","length","renderChildren","undefined","shouldRenderMediaQuery","uniqueComponentId","containerEls","document","getElementsByClassName","Array","from","forEach","el","innerHTML","Function","Boolean","join","createMediaStyle","toStyle","SortedBreakpoints","sortedBreakpoints","findBreakpointAtWidth","findBreakpointsForWidths","valuesWithBreakpointProps","MutuallyExclusiveProps","validKeys","selectedProps","prop","includes","Error"],"mappings":"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","sourcesContent":["// tslint:disable:jsdoc-format\n\nimport React, { CSSProperties } from \"react\"\nimport { createResponsiveComponents } from \"./DynamicResponsive\"\nimport { MediaQueries } from \"./MediaQueries\"\nimport {\n intersection,\n propKey,\n createClassName,\n castBreakpointsToIntegers,\n memoize,\n useIsFirstRender,\n} from \"./Utils\"\nimport { BreakpointConstraint } from \"./Breakpoints\"\n\n/**\n * A render prop that can be used to render a different container element than\n * the default `div`.\n *\n * @see {@link MediaProps.children}.\n */\nexport type RenderProp = (\n className: string,\n renderChildren: boolean\n) => React.ReactNode\n\n// TODO: All of these props should be mutually exclusive. Using a union should\n// probably be made possible by https://github.com/Microsoft/TypeScript/pull/27408.\nexport interface MediaBreakpointProps {\n /**\n * Children will only be shown if the viewport matches the specified\n * breakpoint. That is, a viewport width that’s higher than the configured\n * breakpoint value, but lower than the value of the next breakpoint, if any\n * larger breakpoints exist at all.\n *\n * @example\n\n ```tsx\n // With breakpoints defined like these\n { xs: 0, sm: 768, md: 1024 }\n\n // Matches a viewport that has a width between 0 and 768\n ohai\n\n // Matches a viewport that has a width between 768 and 1024\n ohai\n\n // Matches a viewport that has a width over 1024\n ohai\n ```\n *\n */\n at?: BreakpointKey\n\n /**\n * Children will only be shown if the viewport is smaller than the specified\n * breakpoint.\n *\n * @example\n\n ```tsx\n // With breakpoints defined like these\n { xs: 0, sm: 768, md: 1024 }\n\n // Matches a viewport that has a width from 0 to 767\n ohai\n\n // Matches a viewport that has a width from 0 to 1023\n ohai\n ```\n *\n */\n lessThan?: BreakpointKey\n\n /**\n * Children will only be shown if the viewport is greater than the specified\n * breakpoint.\n *\n * @example\n\n ```tsx\n // With breakpoints defined like these\n { xs: 0, sm: 768, md: 1024 }\n\n // Matches a viewport that has a width from 768 to infinity\n ohai\n\n // Matches a viewport that has a width from 1024 to infinity\n ohai\n ```\n *\n */\n greaterThan?: BreakpointKey\n\n /**\n * Children will only be shown if the viewport is greater or equal to the\n * specified breakpoint.\n *\n * @example\n\n ```tsx\n // With breakpoints defined like these\n { xs: 0, sm: 768, md: 1024 }\n\n // Matches a viewport that has a width from 0 to infinity\n ohai\n\n // Matches a viewport that has a width from 768 to infinity\n ohai\n\n // Matches a viewport that has a width from 1024 to infinity\n ohai\n ```\n *\n */\n greaterThanOrEqual?: BreakpointKey\n\n /**\n * Children will only be shown if the viewport is between the specified\n * breakpoints. That is, a viewport width that’s higher than or equal to the\n * small breakpoint value, but lower than the value of the large breakpoint.\n *\n * @example\n\n ```tsx\n // With breakpoints defined like these\n { xs: 0, sm: 768, md: 1024 }\n\n // Matches a viewport that has a width from 0 to 767\n ohai\n\n // Matches a viewport that has a width from 0 to 1023\n ohai\n ```\n *\n */\n between?: [BreakpointKey, BreakpointKey]\n}\n\nexport interface MediaProps\n extends MediaBreakpointProps {\n /**\n * Children will only be shown if the interaction query matches.\n *\n * @example\n\n ```tsx\n // With interactions defined like these\n { hover: \"(hover: hover)\" }\n\n // Matches an input device that is capable of hovering\n ohai\n ```\n */\n interaction?: Interaction\n\n /**\n * The component(s) that should conditionally be shown, depending on the media\n * query matching.\n *\n * In case a different element is preferred, a render prop can be provided\n * that receives the class-name it should use to have the media query styling\n * applied.\n *\n * Additionally, the render prop receives a boolean that indicates wether or\n * not its children should be rendered, which will be `false` if the media\n * query is not included in the `onlyMatch` list. Use this flag if your\n * component’s children may be expensive to render and you want to avoid any\n * unnecessary work.\n * (@see {@link MediaContextProviderProps.onlyMatch} for details)\n *\n * @example\n *\n ```tsx\n const Component = () => (\n \n {(className, renderChildren) => (\n \n {renderChildren && \"ohai\"}\n \n )}\n \n )\n ```\n *\n */\n children: React.ReactNode | RenderProp\n\n /**\n * Additional classNames to passed down and applied to Media container\n */\n className?: string\n\n /**\n * Additional styles to passed down and applied to Media container\n */\n style?: CSSProperties\n}\n\nexport interface MediaContextProviderProps {\n /**\n * This list of breakpoints and interactions can be used to limit the rendered\n * output to these.\n *\n * For instance, when a server knows for some user-agents that certain\n * breakpoints will never apply, omitting them altogether will lower the\n * rendered byte size.\n */\n onlyMatch?: M[]\n\n /**\n * Disables usage of browser MediaQuery API to only render at the current\n * breakpoint.\n *\n * Use this with caution, as disabling this means React components for all\n * breakpoints will be mounted client-side and all associated life-cycle hooks\n * will be triggered, which could lead to unintended side-effects.\n */\n disableDynamicMediaQueries?: boolean\n}\n\nexport interface CreateMediaConfig {\n /**\n * The breakpoint definitions for your application. Width definitions should\n * start at 0.\n *\n * @see {@link createMedia}\n */\n breakpoints: { [key: string]: number | string }\n\n /**\n * The interaction definitions for your application.\n */\n interactions?: { [key: string]: string }\n}\n\nexport interface CreateMediaResults {\n /**\n * The React component that you use throughout your application.\n *\n * @see {@link MediaBreakpointProps}\n */\n Media: React.ComponentType>\n\n /**\n * The React Context provider component that you use to constrain rendering of\n * breakpoints to a set list and to enable client-side dynamic constraining.\n *\n * @see {@link MediaContextProviderProps}\n */\n MediaContextProvider: React.ComponentType<\n MediaContextProviderProps & {\n children: React.ReactNode\n }\n >\n\n /**\n * Generates a set of CSS rules that you should include in your application’s\n * styling to enable the hiding behaviour of your `Media` component uses.\n */\n createMediaStyle(breakpointKeys?: BreakpointConstraint[]): string\n\n /**\n * A list of your application’s breakpoints sorted from small to large.\n */\n SortedBreakpoints: BreakpointKey[]\n\n /**\n * Creates a list of your application’s breakpoints that support the given\n * widths and everything in between.\n */\n findBreakpointsForWidths(\n fromWidth: number,\n throughWidth: number\n ): BreakpointKey[] | undefined\n\n /**\n * Finds the breakpoint that matches the given width.\n */\n findBreakpointAtWidth(width: number): BreakpointKey | undefined\n\n /**\n * Maps a list of values for various breakpoints to props that can be used\n * with the `Media` component.\n *\n * The values map to corresponding indices in the sorted breakpoints array. If\n * less values are specified than the number of breakpoints your application\n * has, the last value will be applied to all subsequent breakpoints.\n */\n valuesWithBreakpointProps(\n values: SizeValue[]\n ): [SizeValue, MediaBreakpointProps][]\n}\n\n/**\n * This is used to generate a Media component, its context provider, and CSS\n * rules based on your application’s breakpoints and interactions.\n *\n * Note that the interaction queries are entirely up to you to define and they\n * should be written in such a way that they match when you want the element to\n * be hidden.\n *\n * @example\n *\n ```tsx\n const MyAppMedia = createMedia({\n breakpoints: {\n xs: 0,\n sm: 768,\n md: 900\n lg: 1024,\n xl: 1192,\n },\n interactions: {\n hover: `not all and (hover:hover)`\n },\n })\n\n export const Media = MyAppMedia.Media\n export const MediaContextProvider = MyAppMedia.MediaContextProvider\n export const createMediaStyle = MyAppMedia.createMediaStyle\n ```\n *\n */\nexport function createMedia<\n MediaConfig extends CreateMediaConfig,\n BreakpointKey extends keyof MediaConfig[\"breakpoints\"],\n Interaction extends keyof MediaConfig[\"interactions\"]\n>(config: MediaConfig): CreateMediaResults {\n const breakpoints = castBreakpointsToIntegers(config.breakpoints)\n\n const mediaQueries = new MediaQueries(\n breakpoints,\n config.interactions || {}\n )\n\n const DynamicResponsive = createResponsiveComponents()\n\n const MediaContext = React.createContext<\n MediaContextProviderProps\n >({})\n MediaContext.displayName = \"Media.Context\"\n\n type MediaParentContextValue = {\n hasParentMedia: boolean\n breakpointProps: MediaBreakpointProps\n }\n\n const MediaParentContext = React.createContext({\n hasParentMedia: false,\n breakpointProps: {},\n })\n MediaContext.displayName = \"MediaParent.Context\"\n\n const getMediaContextValue = memoize(onlyMatch => ({\n onlyMatch,\n }))\n\n const MediaContextProvider: React.FunctionComponent<\n MediaContextProviderProps & {\n children?: React.ReactNode\n }\n > = ({ disableDynamicMediaQueries, onlyMatch, children }) => {\n if (disableDynamicMediaQueries) {\n const MediaContextValue = getMediaContextValue(onlyMatch)\n\n return (\n \n {children}\n \n )\n } else {\n return (\n \n \n {matches => {\n const matchingMediaQueries = Object.keys(matches).filter(\n key => matches[key]\n )\n\n const MediaContextValue = getMediaContextValue(\n intersection(matchingMediaQueries, onlyMatch)\n )\n\n return (\n \n {children}\n \n )\n }}\n \n \n )\n }\n }\n\n const Media = (props: MediaProps) => {\n validateProps(props)\n\n const {\n children,\n className: passedClassName,\n style,\n interaction,\n ...breakpointProps\n } = props\n\n const getMediaParentContextValue = React.useMemo(() => {\n return memoize(\n (newBreakpointProps: MediaBreakpointProps) => ({\n hasParentMedia: true,\n breakpointProps: newBreakpointProps,\n })\n )\n }, [])\n\n const mediaParentContext = React.useContext(MediaParentContext)\n const childMediaParentContext = getMediaParentContextValue(breakpointProps)\n const { onlyMatch } = React.useContext(MediaContext)\n\n const id = React.useId()\n const isClient = typeof window !== \"undefined\"\n const isFirstRender = useIsFirstRender()\n\n let className: string | null\n if (props.interaction) {\n className = createClassName(\"interaction\", props.interaction)\n } else {\n if (props.at) {\n const largestBreakpoint = mediaQueries.breakpoints.largestBreakpoint\n if (props.at === largestBreakpoint) {\n console.warn(\n \"[@artsy/fresnel] \" +\n \"`at` is being used with the largest breakpoint. \" +\n \"Consider using `\\` to account for future ` +\n `breakpoint definitions outside of this range.`\n )\n }\n }\n\n const type = propKey(breakpointProps)\n const breakpoint = breakpointProps[type]!\n className = createClassName(type, breakpoint)\n }\n\n const doesMatchParent =\n !mediaParentContext.hasParentMedia ||\n intersection(\n mediaQueries.breakpoints.toVisibleAtBreakpointSet(\n mediaParentContext.breakpointProps\n ),\n mediaQueries.breakpoints.toVisibleAtBreakpointSet(breakpointProps)\n ).length > 0\n\n const renderChildren =\n doesMatchParent &&\n (onlyMatch === undefined ||\n mediaQueries.shouldRenderMediaQuery(\n { ...breakpointProps, interaction },\n onlyMatch\n ))\n\n // Append a unique id to the className (consistent on server and client)\n const uniqueComponentId = ` fresnel-${id}`\n className += uniqueComponentId\n\n /**\n * SPECIAL CASE:\n * If we're on the client, this is the first render, and we are not going\n * to render the children, we need to cleanup the the server-rendered HTML\n * to avoid a hydration mismatch on React 18+. We do this by grabbing the\n * already-existing element(s) directly from the DOM using the unique class\n * id and clearing its contents. This solution follows one of the\n * suggestions from Dan Abromov here:\n *\n * https://github.com/facebook/react/issues/23381#issuecomment-1096899474\n *\n * This will not have a negative impact on client-only rendering because\n * either 1) isFirstRender will be false OR 2) the element won't exist yet\n * so there will be nothing to clean up. It will only apply on SSR'd HTML\n * on initial hydration.\n */\n if (isClient && isFirstRender && !renderChildren) {\n const containerEls = document.getElementsByClassName(uniqueComponentId)\n Array.from(containerEls).forEach(el => (el.innerHTML = \"\"))\n }\n\n return (\n \n {(() => {\n if (props.children instanceof Function) {\n return props.children(className, renderChildren)\n } else {\n return (\n \n {renderChildren ? props.children : null}\n \n )\n }\n })()}\n \n )\n }\n\n return {\n Media,\n MediaContextProvider,\n createMediaStyle: mediaQueries.toStyle,\n SortedBreakpoints: [...mediaQueries.breakpoints.sortedBreakpoints],\n findBreakpointAtWidth: mediaQueries.breakpoints.findBreakpointAtWidth,\n findBreakpointsForWidths: mediaQueries.breakpoints.findBreakpointsForWidths,\n valuesWithBreakpointProps:\n mediaQueries.breakpoints.valuesWithBreakpointProps,\n }\n}\n\nconst MutuallyExclusiveProps: string[] = MediaQueries.validKeys()\n\nfunction validateProps(props) {\n const selectedProps = Object.keys(props).filter(prop =>\n MutuallyExclusiveProps.includes(prop)\n )\n if (selectedProps.length < 1) {\n throw new Error(`1 of ${MutuallyExclusiveProps.join(\", \")} is required.`)\n } else if (selectedProps.length > 1) {\n throw new Error(\n `Only 1 of ${selectedProps.join(\", \")} is allowed at a time.`\n )\n }\n}\n"],"file":"Media.js"} \ No newline at end of file +{"version":3,"sources":["../src/Media.tsx"],"names":["createMedia","config","breakpoints","mediaQueries","MediaQueries","interactions","DynamicResponsive","MediaContext","React","createContext","displayName","MediaParentContext","hasParentMedia","breakpointProps","getMediaContextValue","onlyMatch","MediaContextProvider","disableDynamicMediaQueries","children","MediaContextValue","dynamicResponsiveMediaQueries","mediaQueryTypes","matches","matchingMediaQueries","Object","keys","filter","key","Media","props","validateProps","passedClassName","className","style","interaction","getMediaParentContextValue","useMemo","newBreakpointProps","mediaParentContext","useContext","childMediaParentContext","id","useId","isClient","window","isFirstRender","at","largestBreakpoint","console","warn","type","breakpoint","doesMatchParent","toVisibleAtBreakpointSet","length","renderChildren","undefined","shouldRenderMediaQuery","uniqueComponentId","containerEls","document","getElementsByClassName","Array","from","forEach","el","innerHTML","Function","Boolean","join","createMediaStyle","toStyle","SortedBreakpoints","sortedBreakpoints","findBreakpointAtWidth","findBreakpointsForWidths","valuesWithBreakpointProps","MutuallyExclusiveProps","validKeys","selectedProps","prop","includes","Error"],"mappings":"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","sourcesContent":["// tslint:disable:jsdoc-format\n\nimport React, { CSSProperties } from \"react\"\nimport { createResponsiveComponents } from \"./DynamicResponsive\"\nimport { MediaQueries } from \"./MediaQueries\"\nimport {\n intersection,\n propKey,\n createClassName,\n castBreakpointsToIntegers,\n memoize,\n useIsFirstRender,\n} from \"./Utils\"\nimport { BreakpointConstraint } from \"./Breakpoints\"\n\n/**\n * A render prop that can be used to render a different container element than\n * the default `div`.\n *\n * @see {@link MediaProps.children}.\n */\nexport type RenderProp = (\n className: string,\n renderChildren: boolean\n) => React.ReactNode\n\n// TODO: All of these props should be mutually exclusive. Using a union should\n// probably be made possible by https://github.com/Microsoft/TypeScript/pull/27408.\nexport interface MediaBreakpointProps {\n /**\n * Children will only be shown if the viewport matches the specified\n * breakpoint. That is, a viewport width that’s higher than the configured\n * breakpoint value, but lower than the value of the next breakpoint, if any\n * larger breakpoints exist at all.\n *\n * @example\n\n ```tsx\n // With breakpoints defined like these\n { xs: 0, sm: 768, md: 1024 }\n\n // Matches a viewport that has a width between 0 and 768\n ohai\n\n // Matches a viewport that has a width between 768 and 1024\n ohai\n\n // Matches a viewport that has a width over 1024\n ohai\n ```\n *\n */\n at?: BreakpointKey\n\n /**\n * Children will only be shown if the viewport is smaller than the specified\n * breakpoint.\n *\n * @example\n\n ```tsx\n // With breakpoints defined like these\n { xs: 0, sm: 768, md: 1024 }\n\n // Matches a viewport that has a width from 0 to 767\n ohai\n\n // Matches a viewport that has a width from 0 to 1023\n ohai\n ```\n *\n */\n lessThan?: BreakpointKey\n\n /**\n * Children will only be shown if the viewport is greater than the specified\n * breakpoint.\n *\n * @example\n\n ```tsx\n // With breakpoints defined like these\n { xs: 0, sm: 768, md: 1024 }\n\n // Matches a viewport that has a width from 768 to infinity\n ohai\n\n // Matches a viewport that has a width from 1024 to infinity\n ohai\n ```\n *\n */\n greaterThan?: BreakpointKey\n\n /**\n * Children will only be shown if the viewport is greater or equal to the\n * specified breakpoint.\n *\n * @example\n\n ```tsx\n // With breakpoints defined like these\n { xs: 0, sm: 768, md: 1024 }\n\n // Matches a viewport that has a width from 0 to infinity\n ohai\n\n // Matches a viewport that has a width from 768 to infinity\n ohai\n\n // Matches a viewport that has a width from 1024 to infinity\n ohai\n ```\n *\n */\n greaterThanOrEqual?: BreakpointKey\n\n /**\n * Children will only be shown if the viewport is between the specified\n * breakpoints. That is, a viewport width that’s higher than or equal to the\n * small breakpoint value, but lower than the value of the large breakpoint.\n *\n * @example\n\n ```tsx\n // With breakpoints defined like these\n { xs: 0, sm: 768, md: 1024 }\n\n // Matches a viewport that has a width from 0 to 767\n ohai\n\n // Matches a viewport that has a width from 0 to 1023\n ohai\n ```\n *\n */\n between?: [BreakpointKey, BreakpointKey]\n}\n\nexport interface MediaProps\n extends MediaBreakpointProps {\n /**\n * Children will only be shown if the interaction query matches.\n *\n * @example\n\n ```tsx\n // With interactions defined like these\n { hover: \"(hover: hover)\" }\n\n // Matches an input device that is capable of hovering\n ohai\n ```\n */\n interaction?: Interaction\n\n /**\n * The component(s) that should conditionally be shown, depending on the media\n * query matching.\n *\n * In case a different element is preferred, a render prop can be provided\n * that receives the class-name it should use to have the media query styling\n * applied.\n *\n * Additionally, the render prop receives a boolean that indicates wether or\n * not its children should be rendered, which will be `false` if the media\n * query is not included in the `onlyMatch` list. Use this flag if your\n * component’s children may be expensive to render and you want to avoid any\n * unnecessary work.\n * (@see {@link MediaContextProviderProps.onlyMatch} for details)\n *\n * @example\n *\n ```tsx\n const Component = () => (\n \n {(className, renderChildren) => (\n \n {renderChildren && \"ohai\"}\n \n )}\n \n )\n ```\n *\n */\n children: React.ReactNode | RenderProp\n\n /**\n * Additional classNames to passed down and applied to Media container\n */\n className?: string\n\n /**\n * Additional styles to passed down and applied to Media container\n */\n style?: CSSProperties\n}\n\nexport interface MediaContextProviderProps {\n /**\n * This list of breakpoints and interactions can be used to limit the rendered\n * output to these.\n *\n * For instance, when a server knows for some user-agents that certain\n * breakpoints will never apply, omitting them altogether will lower the\n * rendered byte size.\n */\n onlyMatch?: M[]\n\n /**\n * Disables usage of browser MediaQuery API to only render at the current\n * breakpoint.\n *\n * Use this with caution, as disabling this means React components for all\n * breakpoints will be mounted client-side and all associated life-cycle hooks\n * will be triggered, which could lead to unintended side-effects.\n */\n disableDynamicMediaQueries?: boolean\n}\n\nexport interface CreateMediaConfig {\n /**\n * The breakpoint definitions for your application. Width definitions should\n * start at 0.\n *\n * @see {@link createMedia}\n */\n breakpoints: { [key: string]: number | string }\n\n /**\n * The interaction definitions for your application.\n */\n interactions?: { [key: string]: string }\n}\n\nexport interface CreateMediaResults {\n /**\n * The React component that you use throughout your application.\n *\n * @see {@link MediaBreakpointProps}\n */\n Media: React.ComponentType>\n\n /**\n * The React Context provider component that you use to constrain rendering of\n * breakpoints to a set list and to enable client-side dynamic constraining.\n *\n * @see {@link MediaContextProviderProps}\n */\n MediaContextProvider: React.ComponentType<\n MediaContextProviderProps & {\n children: React.ReactNode\n }\n >\n\n /**\n * Generates a set of CSS rules that you should include in your application’s\n * styling to enable the hiding behaviour of your `Media` component uses.\n */\n createMediaStyle(breakpointKeys?: BreakpointConstraint[]): string\n\n /**\n * A list of your application’s breakpoints sorted from small to large.\n */\n SortedBreakpoints: BreakpointKey[]\n\n /**\n * Creates a list of your application’s breakpoints that support the given\n * widths and everything in between.\n */\n findBreakpointsForWidths(\n fromWidth: number,\n throughWidth: number\n ): BreakpointKey[] | undefined\n\n /**\n * Finds the breakpoint that matches the given width.\n */\n findBreakpointAtWidth(width: number): BreakpointKey | undefined\n\n /**\n * Maps a list of values for various breakpoints to props that can be used\n * with the `Media` component.\n *\n * The values map to corresponding indices in the sorted breakpoints array. If\n * less values are specified than the number of breakpoints your application\n * has, the last value will be applied to all subsequent breakpoints.\n */\n valuesWithBreakpointProps(\n values: SizeValue[]\n ): [SizeValue, MediaBreakpointProps][]\n}\n\n/**\n * This is used to generate a Media component, its context provider, and CSS\n * rules based on your application’s breakpoints and interactions.\n *\n * Note that the interaction queries are entirely up to you to define and they\n * should be written in such a way that they match when you want the element to\n * be hidden.\n *\n * @example\n *\n ```tsx\n const MyAppMedia = createMedia({\n breakpoints: {\n xs: 0,\n sm: 768,\n md: 900\n lg: 1024,\n xl: 1192,\n },\n interactions: {\n hover: `not all and (hover:hover)`\n },\n })\n\n export const Media = MyAppMedia.Media\n export const MediaContextProvider = MyAppMedia.MediaContextProvider\n export const createMediaStyle = MyAppMedia.createMediaStyle\n ```\n *\n */\nexport function createMedia<\n MediaConfig extends CreateMediaConfig,\n BreakpointKey extends keyof MediaConfig[\"breakpoints\"],\n Interaction extends keyof MediaConfig[\"interactions\"]\n>(config: MediaConfig): CreateMediaResults {\n const breakpoints = castBreakpointsToIntegers(config.breakpoints)\n\n const mediaQueries = new MediaQueries(\n breakpoints,\n config.interactions || {}\n )\n\n const DynamicResponsive = createResponsiveComponents()\n\n const MediaContext = React.createContext<\n MediaContextProviderProps\n >({})\n MediaContext.displayName = \"Media.Context\"\n\n type MediaParentContextValue = {\n hasParentMedia: boolean\n breakpointProps: MediaBreakpointProps\n }\n\n const MediaParentContext = React.createContext({\n hasParentMedia: false,\n breakpointProps: {},\n })\n MediaContext.displayName = \"MediaParent.Context\"\n\n const getMediaContextValue = memoize(onlyMatch => ({\n onlyMatch,\n }))\n\n const MediaContextProvider: React.FunctionComponent<\n MediaContextProviderProps & {\n children?: React.ReactNode\n }\n > = ({ disableDynamicMediaQueries, onlyMatch, children }) => {\n if (disableDynamicMediaQueries) {\n const MediaContextValue = getMediaContextValue(onlyMatch)\n\n return (\n \n {children}\n \n )\n } else {\n return (\n \n \n {matches => {\n const matchingMediaQueries = Object.keys(matches).filter(\n key => matches[key]\n )\n\n const MediaContextValue = getMediaContextValue(\n intersection(matchingMediaQueries, onlyMatch)\n )\n\n return (\n \n {children}\n \n )\n }}\n \n \n )\n }\n }\n\n const Media = (props: MediaProps) => {\n validateProps(props)\n\n const {\n children,\n className: passedClassName,\n style,\n interaction,\n ...breakpointProps\n } = props\n\n const getMediaParentContextValue = React.useMemo(() => {\n return memoize(\n (newBreakpointProps: MediaBreakpointProps) => ({\n hasParentMedia: true,\n breakpointProps: newBreakpointProps,\n })\n )\n }, [])\n\n const mediaParentContext = React.useContext(MediaParentContext)\n const childMediaParentContext = getMediaParentContextValue(breakpointProps)\n const { onlyMatch } = React.useContext(MediaContext)\n\n const id = React.useId()\n const isClient = typeof window !== \"undefined\"\n const isFirstRender = useIsFirstRender()\n\n let className: string | null\n if (props.interaction) {\n className = createClassName(\"interaction\", props.interaction)\n } else {\n if (props.at) {\n const largestBreakpoint = mediaQueries.breakpoints.largestBreakpoint\n if (props.at === largestBreakpoint) {\n console.warn(\n \"[@artsy/fresnel] \" +\n \"`at` is being used with the largest breakpoint. \" +\n \"Consider using `\\` to account for future ` +\n `breakpoint definitions outside of this range.`\n )\n }\n }\n\n const type = propKey(breakpointProps)\n const breakpoint = breakpointProps[type]!\n className = createClassName(type, breakpoint)\n }\n\n const doesMatchParent =\n !mediaParentContext.hasParentMedia ||\n intersection(\n mediaQueries.breakpoints.toVisibleAtBreakpointSet(\n mediaParentContext.breakpointProps\n ),\n mediaQueries.breakpoints.toVisibleAtBreakpointSet(breakpointProps)\n ).length > 0\n\n const renderChildren =\n doesMatchParent &&\n (onlyMatch === undefined ||\n mediaQueries.shouldRenderMediaQuery(\n { ...breakpointProps, interaction },\n onlyMatch\n ))\n\n // Append a unique id to the className (consistent on server and client)\n const uniqueComponentId = ` fresnel-${id}`\n className += uniqueComponentId\n\n /**\n * SPECIAL CASE:\n * If we're on the client, this is the first render, and we are not going\n * to render the children, we need to cleanup the the server-rendered HTML\n * to avoid a hydration mismatch on React 18+. We do this by grabbing the\n * already-existing element(s) directly from the DOM using the unique class\n * id and clearing its contents. This solution follows one of the\n * suggestions from Dan Abromov here:\n *\n * https://github.com/facebook/react/issues/23381#issuecomment-1096899474\n *\n * This will not have a negative impact on client-only rendering because\n * either 1) isFirstRender will be false OR 2) the element won't exist yet\n * so there will be nothing to clean up. It will only apply on SSR'd HTML\n * on initial hydration.\n */\n if (isClient && isFirstRender && !renderChildren) {\n const containerEls = document.getElementsByClassName(uniqueComponentId)\n Array.from(containerEls).forEach(el => (el.innerHTML = \"\"))\n }\n\n return (\n \n {(() => {\n if (props.children instanceof Function) {\n return props.children(className, renderChildren)\n } else {\n return (\n \n {renderChildren ? props.children : null}\n \n )\n }\n })()}\n \n )\n }\n\n return {\n Media,\n MediaContextProvider,\n createMediaStyle: mediaQueries.toStyle,\n SortedBreakpoints: [...mediaQueries.breakpoints.sortedBreakpoints],\n findBreakpointAtWidth: mediaQueries.breakpoints.findBreakpointAtWidth,\n findBreakpointsForWidths: mediaQueries.breakpoints.findBreakpointsForWidths,\n valuesWithBreakpointProps:\n mediaQueries.breakpoints.valuesWithBreakpointProps,\n }\n}\n\nconst MutuallyExclusiveProps: string[] = MediaQueries.validKeys()\n\nfunction validateProps(props) {\n const selectedProps = Object.keys(props).filter(prop =>\n MutuallyExclusiveProps.includes(prop)\n )\n if (selectedProps.length < 1) {\n throw new Error(`1 of ${MutuallyExclusiveProps.join(\", \")} is required.`)\n } else if (selectedProps.length > 1) {\n throw new Error(\n `Only 1 of ${selectedProps.join(\", \")} is allowed at a time.`\n )\n }\n}\n"],"file":"Media.js"} \ No newline at end of file diff --git a/dist/MediaQueries.d.ts b/dist/MediaQueries.d.ts new file mode 100644 index 0000000..e392fb2 --- /dev/null +++ b/dist/MediaQueries.d.ts @@ -0,0 +1,24 @@ +import { Breakpoints, BreakpointConstraint } from "./Breakpoints"; +import { MediaBreakpointProps } from "./Media"; +/** + * Encapsulates all interaction data (and breakpoint data in the superclass) + * needed by the Media component. The data is generated on initialization so no + * further runtime work is necessary. + */ +export declare class MediaQueries { + static validKeys(): (import("./Interactions").InteractionKey | BreakpointConstraint)[]; + private _breakpoints; + private _interactions; + constructor(breakpoints: { + [key: string]: number; + }, interactions: { + [name: string]: string; + }); + get breakpoints(): Breakpoints; + toStyle: (breakpointKeys?: BreakpointConstraint[]) => string; + get mediaQueryTypes(): string[]; + get dynamicResponsiveMediaQueries(): {}; + shouldRenderMediaQuery(mediaQueryProps: { + interaction?: string; + } & MediaBreakpointProps, onlyMatch: string[]): boolean; +} diff --git a/dist/Utils.d.ts b/dist/Utils.d.ts new file mode 100644 index 0000000..2112682 --- /dev/null +++ b/dist/Utils.d.ts @@ -0,0 +1,34 @@ +import { MediaBreakpointProps } from "./Media"; +/** + * Extracts the single breakpoint prop from the props object. + */ +export declare function propKey(breakpointProps: MediaBreakpointProps): keyof MediaBreakpointProps; +/** + * Returns the intersection of two arrays. + */ +export declare function intersection(a1: ReadonlyArray, a2?: ReadonlyArray): any[]; +/** + * Generate a style rule for a given class name that will hide the element + * when the given query matches. + */ +export declare function createRuleSet(className: string, query: string): string; +/** + * Given a list of strings, or string tuples, generates a class name. + */ +export declare function createClassName(...components: Array): string; +/** + * Returns an object with every values casted to integers. + */ +export declare function castBreakpointsToIntegers(breakpoints: { + [key: string]: number | string; +}): { + [key: string]: number; +}; +/** + * Use this function to memoize any function + */ +export declare function memoize void>(func: F): (...args: any[]) => any; +/** + * Hook to determine if the current render is the first render. + */ +export declare function useIsFirstRender(): boolean; diff --git a/dist/index.d.ts b/dist/index.d.ts new file mode 100644 index 0000000..6aaeab1 --- /dev/null +++ b/dist/index.d.ts @@ -0,0 +1,2 @@ +export { createMedia } from "./Media"; +export { BreakpointConstraint as BreakpointKey } from "./Breakpoints"; diff --git a/src/Media.tsx b/src/Media.tsx index bca9b4a..11ba34e 100644 --- a/src/Media.tsx +++ b/src/Media.tsx @@ -500,9 +500,11 @@ export function createMedia< } else { return (
{renderChildren ? props.children : null}
diff --git a/src/__test__/Media.test.tsx b/src/__test__/Media.test.tsx index bdf1dca..ab4b5fa 100644 --- a/src/__test__/Media.test.tsx +++ b/src/__test__/Media.test.tsx @@ -520,14 +520,6 @@ describe("Media", () => { query.find(e => e.props.className.includes("extra-small")).props .suppressHydrationWarning ).toEqual(true) - expect( - query.find(e => e.props.className.includes("medium")).props - .suppressHydrationWarning - ).toEqual(false) - expect( - query.find(e => e.props.className.includes("large")).props - .suppressHydrationWarning - ).toEqual(true) }) })