Skip to content

Commit

Permalink
Expand generic names to be more descriptive
Browse files Browse the repository at this point in the history
  • Loading branch information
zephraph committed Sep 12, 2020
1 parent b828429 commit f21ffc7
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 69 deletions.
75 changes: 40 additions & 35 deletions src/Breakpoints.ts
Expand Up @@ -4,17 +4,20 @@ import { createRuleSet, createClassName } from "./Utils"
/**
* A union of possible breakpoint props.
*/
export type MediaBreakpointKey = keyof MediaBreakpointProps
export type BreakpointConstraintKey = keyof MediaBreakpointProps

type ValueBreakpointPropsTuple<T, B> = [T, MediaBreakpointProps<B>]
type ValueBreakpointPropsTuple<SizeValue, BreakpointKey> = [
SizeValue,
MediaBreakpointProps<BreakpointKey>
]

type Tuple = [string, string]

function breakpointKey(breakpoint: string | Tuple) {
return Array.isArray(breakpoint) ? breakpoint.join("-") : breakpoint
}

export enum BreakpointKey {
export enum BreakpointConstraint {
at = "at",
lessThan = "lessThan",
greaterThan = "greaterThan",
Expand All @@ -26,20 +29,20 @@ export enum BreakpointKey {
* Encapsulates all breakpoint data needed by the Media component. The data is
* generated on initialization so no further runtime work is necessary.
*/
export class Breakpoints<B extends string> {
export class Breakpoints<BreakpointKey extends string> {
static validKeys() {
return [
BreakpointKey.at,
BreakpointKey.lessThan,
BreakpointKey.greaterThan,
BreakpointKey.greaterThanOrEqual,
BreakpointKey.between,
BreakpointConstraint.at,
BreakpointConstraint.lessThan,
BreakpointConstraint.greaterThan,
BreakpointConstraint.greaterThanOrEqual,
BreakpointConstraint.between,
]
}

private _sortedBreakpoints: ReadonlyArray<string>
private _breakpoints: Record<string, number>
private _mediaQueries: Record<BreakpointKey, Map<string, string>>
private _mediaQueries: Record<BreakpointConstraint, Map<string, string>>

constructor(breakpoints: { [key: string]: number }) {
this._breakpoints = breakpoints
Expand All @@ -61,38 +64,37 @@ export class Breakpoints<B extends string> {
)

this._mediaQueries = {
[BreakpointKey.at]: this._createBreakpointQueries(
BreakpointKey.at,
[BreakpointConstraint.at]: this._createBreakpointQueries(
BreakpointConstraint.at,
this._sortedBreakpoints
),
[BreakpointKey.lessThan]: this._createBreakpointQueries(
BreakpointKey.lessThan,
[BreakpointConstraint.lessThan]: this._createBreakpointQueries(
BreakpointConstraint.lessThan,
this._sortedBreakpoints.slice(1)
),
[BreakpointKey.greaterThan]: this._createBreakpointQueries(
BreakpointKey.greaterThan,
[BreakpointConstraint.greaterThan]: this._createBreakpointQueries(
BreakpointConstraint.greaterThan,
this._sortedBreakpoints.slice(0, -1)
),
[BreakpointKey.greaterThanOrEqual]: this._createBreakpointQueries(
BreakpointKey.greaterThanOrEqual,
[BreakpointConstraint.greaterThanOrEqual]: this._createBreakpointQueries(
BreakpointConstraint.greaterThanOrEqual,
this._sortedBreakpoints
),
[BreakpointKey.between]: this._createBreakpointQueries(
BreakpointKey.between,
[BreakpointConstraint.between]: this._createBreakpointQueries(
BreakpointConstraint.between,
betweenCombinations
),
}
}

public get sortedBreakpoints() {
return this._sortedBreakpoints as B[]
return this._sortedBreakpoints as BreakpointKey[]
}

public get dynamicResponsiveMediaQueries() {
return Array.from(this._mediaQueries[BreakpointKey.at].entries()).reduce(
(acc, [k, v]) => ({ ...acc, [k]: v }),
{}
)
return Array.from(
this._mediaQueries[BreakpointConstraint.at].entries()
).reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {})
}

public get largestBreakpoint() {
Expand All @@ -109,12 +111,12 @@ export class Breakpoints<B extends string> {
}
const throughBreakpoint = this.findBreakpointAtWidth(throughWidth)
if (!throughBreakpoint || fromBreakpoint === throughBreakpoint) {
return [fromBreakpoint] as B[]
return [fromBreakpoint] as BreakpointKey[]
} else {
return this._sortedBreakpoints.slice(
this._sortedBreakpoints.indexOf(fromBreakpoint),
this._sortedBreakpoints.indexOf(throughBreakpoint) + 1
) as B[]
) as BreakpointKey[]
}
}

Expand All @@ -129,7 +131,7 @@ export class Breakpoints<B extends string> {
} else {
return width >= this._breakpoints[breakpoint]
}
}) as B | undefined
}) as BreakpointKey | undefined
}

public toRuleSets(keys = Breakpoints.validKeys()) {
Expand All @@ -138,7 +140,7 @@ export class Breakpoints<B extends string> {
mediaQueries[query] = this._mediaQueries[query]
return mediaQueries
},
{} as Record<BreakpointKey, Map<string, string>>
{} as Record<BreakpointConstraint, Map<string, string>>
)

return Object.entries(selectedMediaQueries).reduce(
Expand Down Expand Up @@ -201,10 +203,10 @@ export class Breakpoints<B extends string> {
return false
}

public valuesWithBreakpointProps = <T>(
values: T[]
): Array<ValueBreakpointPropsTuple<T, B>> => {
type ValueBreakpoints = [T, string[]]
public valuesWithBreakpointProps = <SizeValue>(
values: SizeValue[]
): Array<ValueBreakpointPropsTuple<SizeValue, BreakpointKey>> => {
type ValueBreakpoints = [SizeValue, string[]]
const max = values.length
const valueBreakpoints: ValueBreakpoints[] = []
let lastTuple: ValueBreakpoints
Expand All @@ -229,7 +231,10 @@ export class Breakpoints<B extends string> {
// prop, which unlike `between` is inclusive.
props.between = [breakpoints[0], valueBreakpoints[i + 1][1][0]]
}
return [value, props] as ValueBreakpointPropsTuple<T, B>
return [value, props] as ValueBreakpointPropsTuple<
SizeValue,
BreakpointKey
>
})
}

Expand Down Expand Up @@ -275,7 +280,7 @@ export class Breakpoints<B extends string> {
}

private _createBreakpointQueries(
key: MediaBreakpointKey,
key: BreakpointConstraintKey,
forBreakpoints: ReadonlyArray<string | [string, string]>
) {
return forBreakpoints.reduce<Map<string, string>>((map, breakpoint) => {
Expand Down
65 changes: 36 additions & 29 deletions src/Media.tsx
Expand Up @@ -4,7 +4,7 @@ import React from "react"
import { createResponsiveComponents } from "./DynamicResponsive"
import { MediaQueries } from "./MediaQueries"
import { intersection, propKey, createClassName } from "./Utils"
import { BreakpointKey } from "./Breakpoints"
import { BreakpointConstraint } from "./Breakpoints"

/**
* A render prop that can be used to render a different container element than
Expand All @@ -19,7 +19,7 @@ export type RenderProp = (

// TODO: All of these props should be mutually exclusive. Using a union should
// probably be made possible by https://github.com/Microsoft/TypeScript/pull/27408.
export interface MediaBreakpointProps<B = string> {
export interface MediaBreakpointProps<BreakpointKey = string> {
/**
* Children will only be shown if the viewport matches the specified
* breakpoint. That is, a viewport width that’s higher than the configured
Expand All @@ -43,7 +43,7 @@ export interface MediaBreakpointProps<B = string> {
```
*
*/
at?: B
at?: BreakpointKey

/**
* Children will only be shown if the viewport is smaller than the specified
Expand All @@ -63,7 +63,7 @@ export interface MediaBreakpointProps<B = string> {
```
*
*/
lessThan?: B
lessThan?: BreakpointKey

/**
* Children will only be shown if the viewport is greater than the specified
Expand All @@ -83,7 +83,7 @@ export interface MediaBreakpointProps<B = string> {
```
*
*/
greaterThan?: B
greaterThan?: BreakpointKey

/**
* Children will only be shown if the viewport is greater or equal to the
Expand All @@ -106,7 +106,7 @@ export interface MediaBreakpointProps<B = string> {
```
*
*/
greaterThanOrEqual?: B
greaterThanOrEqual?: BreakpointKey

/**
* Children will only be shown if the viewport is between the specified
Expand All @@ -127,10 +127,11 @@ export interface MediaBreakpointProps<B = string> {
```
*
*/
between?: [B, B]
between?: [BreakpointKey, BreakpointKey]
}

export interface MediaProps<B, I> extends MediaBreakpointProps<B> {
export interface MediaProps<BreakpointKey, Interaction>
extends MediaBreakpointProps<BreakpointKey> {
/**
* Children will only be shown if the interaction query matches.
*
Expand All @@ -144,7 +145,7 @@ export interface MediaProps<B, I> extends MediaBreakpointProps<B> {
<Media interaction="hover">ohai</Media>
```
*/
interaction?: I
interaction?: Interaction

/**
* The component(s) that should conditionally be shown, depending on the media
Expand Down Expand Up @@ -221,32 +222,34 @@ export interface CreateMediaConfig {
interactions?: { [key: string]: string }
}

export interface CreateMediaResults<B, I> {
export interface CreateMediaResults<BreakpointKey, Interactions> {
/**
* The React component that you use throughout your application.
*
* @see {@link MediaBreakpointProps}
*/
Media: React.ComponentType<MediaProps<B, I>>
Media: React.ComponentType<MediaProps<BreakpointKey, Interactions>>

/**
* 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<MediaContextProviderProps<B | I>>
MediaContextProvider: React.ComponentType<
MediaContextProviderProps<BreakpointKey | Interactions>
>

/**
* 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?: BreakpointKey[]): string
createMediaStyle(breakpointKeys?: BreakpointConstraint[]): string

/**
* A list of your application’s breakpoints sorted from small to large.
*/
SortedBreakpoints: B[]
SortedBreakpoints: BreakpointKey[]

/**
* Creates a list of your application’s breakpoints that support the given
Expand All @@ -255,12 +258,12 @@ export interface CreateMediaResults<B, I> {
findBreakpointsForWidths(
fromWidth: number,
throughWidth: number
): B[] | undefined
): BreakpointKey[] | undefined

/**
* Finds the breakpoint that matches the given width.
*/
findBreakpointAtWidth(width: number): B | undefined
findBreakpointAtWidth(width: number): BreakpointKey | undefined

/**
* Maps a list of values for various breakpoints to props that can be used
Expand All @@ -270,7 +273,9 @@ export interface CreateMediaResults<B, I> {
* less values are specified than the number of breakpoints your application
* has, the last value will be applied to all subsequent breakpoints.
*/
valuesWithBreakpointProps<T>(values: T[]): Array<[T, MediaBreakpointProps<B>]>
valuesWithBreakpointProps<SizeValue>(
values: SizeValue[]
): Array<[SizeValue, MediaBreakpointProps<BreakpointKey>]>
}

/**
Expand Down Expand Up @@ -304,25 +309,25 @@ export interface CreateMediaResults<B, I> {
*
*/
export function createMedia<
C extends CreateMediaConfig,
B extends keyof C["breakpoints"],
I extends keyof C["interactions"]
>(config: C): CreateMediaResults<B, I> {
const mediaQueries = new MediaQueries<B>(
MediaConfig extends CreateMediaConfig,
BreakpointKey extends keyof MediaConfig["breakpoints"],
Interaction extends keyof MediaConfig["interactions"]
>(config: MediaConfig): CreateMediaResults<BreakpointKey, Interaction> {
const mediaQueries = new MediaQueries<BreakpointKey>(
config.breakpoints,
config.interactions || {}
)

const DynamicResponsive = createResponsiveComponents()

const MediaContext = React.createContext<MediaContextProviderProps<B | I>>({})
const MediaContext = React.createContext<
MediaContextProviderProps<BreakpointKey | Interaction>
>({})
MediaContext.displayName = "Media.Context"

const MediaContextProvider: React.SFC<MediaContextProviderProps<B | I>> = ({
disableDynamicMediaQueries,
onlyMatch,
children,
}) => {
const MediaContextProvider: React.SFC<
MediaContextProviderProps<BreakpointKey | Interaction>
> = ({ disableDynamicMediaQueries, onlyMatch, children }) => {
if (disableDynamicMediaQueries) {
return (
<MediaContext.Provider
Expand Down Expand Up @@ -363,7 +368,9 @@ export function createMedia<
}
}

const Media = class extends React.Component<MediaProps<B, I>> {
const Media = class extends React.Component<
MediaProps<BreakpointKey, Interaction>
> {
constructor(props) {
super(props)
validateProps(props)
Expand Down
4 changes: 2 additions & 2 deletions src/MediaQueries.ts
@@ -1,4 +1,4 @@
import { Breakpoints, BreakpointKey } from "./Breakpoints"
import { Breakpoints, BreakpointConstraint } from "./Breakpoints"
import { Interactions } from "./Interactions"
import { intersection } from "./Utils"
import { MediaBreakpointProps } from "./Media"
Expand Down Expand Up @@ -28,7 +28,7 @@ export class MediaQueries<B extends string> {
return this._breakpoints
}

public toStyle = (breakpointKeys?: BreakpointKey[]) => {
public toStyle = (breakpointKeys?: BreakpointConstraint[]) => {
return [
// Don’t add any size to the layout
".fresnel-container{margin:0;padding:0;}",
Expand Down
4 changes: 2 additions & 2 deletions src/Utils.ts
@@ -1,11 +1,11 @@
import { MediaBreakpointProps } from "./Media"
import { MediaBreakpointKey } from "./Breakpoints"
import { BreakpointConstraintKey } from "./Breakpoints"

/**
* Extracts the single breakpoint prop from the props object.
*/
export function propKey(breakpointProps: MediaBreakpointProps) {
return Object.keys(breakpointProps)[0] as MediaBreakpointKey
return Object.keys(breakpointProps)[0] as BreakpointConstraintKey
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/index.tsx
@@ -1,2 +1,2 @@
export { createMedia } from "./Media"
export { BreakpointKey } from "./Breakpoints"
export { BreakpointConstraint as BreakpointKey } from "./Breakpoints"

0 comments on commit f21ffc7

Please sign in to comment.