Skip to content

Commit

Permalink
feat: rewrite media queries engine
Browse files Browse the repository at this point in the history
  • Loading branch information
jpudysz committed Nov 8, 2023
1 parent 296e184 commit 2d37841
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 284 deletions.
2 changes: 1 addition & 1 deletion examples/expo/src/examples/MediaQueriesWidthHeight.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ const stylesheet = createStyleSheet(theme => ({
paddingHorizontal: 20,
backgroundColor: {
':w[, 500]:h[, 1000]': theme.colors.backgroundColor,
':w[900]': theme.colors.aloes
':w[932]': theme.colors.aloes
},
rowGap: 20
},
Expand Down
10 changes: 10 additions & 0 deletions examples/expo/src/examples/WithBreakpointsScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export const WithBreakpointsScreen: React.FunctionComponent = () => {
<Text style={styles.text}>
The current breakpoint is: {breakpoint}
</Text>
<View style={styles.box} />
</View>
</DemoScreen>
)
Expand All @@ -47,5 +48,14 @@ const stylesheet = createStyleSheet(theme => ({
text: {
textAlign: 'center',
color: theme.colors.typography
},
box: {
width: 100,
height: 100,
borderRadius: {
sm: 50,
lg: 0
},
backgroundColor: theme.colors.accent
}
}))
6 changes: 6 additions & 0 deletions src/UnistylesEngine.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import type { UnistyleRegistry } from './UnistyleRegistry'
import type { UnistylesRuntime } from './UnistylesRuntime'
import { getKeyForCustomMediaQuery } from './utils'
import type { UnistylesBreakpoints } from './global'
import type { MediaQueries } from './types'

// todo implement engine
export class UnistylesEngine {
Expand All @@ -9,6 +12,9 @@ export class UnistylesEngine {
this.runtime = runtime
}

public parseCustomMediaQuery = (mediaQueries: Array<[keyof UnistylesBreakpoints | MediaQueries, string | number | undefined]>) =>
getKeyForCustomMediaQuery(mediaQueries, this.runtime.screen, this.registry.breakpoints)

// UnistylesEngine.parseStyleSheet
// UnistylesEngine.parseStyle
// UnistylesEngine.proxify
Expand Down
81 changes: 13 additions & 68 deletions src/utils/breakpoints.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,10 @@
import { unistyles } from '../Unistyles'
import { isMobile, Orientation, throwError } from './common'
import type { ScreenSize, MediaQueries } from '../types'
import type { MediaQueries } from '../types'
import { ScreenOrientation } from '../types'
import { getKeyForCustomMediaQuery, isMediaQuery } from './mediaQueries'
import { isMediaQuery } from './mediaQueries'
import type { UnistylesBreakpoints } from '../global'

/**
* Sorts the breakpoints object based on its numeric values in ascending order and validates them.
*
* This function takes an object where keys represent breakpoint names and values are numeric.
* It returns a new object with the same keys but sorted based on their corresponding numeric values.
* Additionally, it validates that:
* 1. The first breakpoint starts with a value of 0.
* 2. No duplicate breakpoint values exist.
*
* If the validation fails, appropriate error messages are logged to the console.
*
* @template B - An object type where keys are strings and values are numbers.
* @param {B} breakpoints - The breakpoints object to be sorted and validated.
* @returns {B} A new object with sorted and validated breakpoints.
*
* @example
* const input = { md: 768, lg: 1024, sm: 0 }
* sortAndValidateBreakpoints(input) // returns { sm: 0, md: 768, lg: 1024 }
*/
export const sortAndValidateBreakpoints = (breakpoints: UnistylesBreakpoints): UnistylesBreakpoints => {
const sortedPairs = Object
.entries(breakpoints)
Expand All @@ -49,21 +30,6 @@ export const sortAndValidateBreakpoints = (breakpoints: UnistylesBreakpoints): U
return sortedBreakpoints
}

/**
* Determines the appropriate breakpoint key for a given screen width based on provided breakpoints.
*
* This function takes a screen width and an object of breakpoints. It returns the key of the breakpoint
* that the screen width falls into. The breakpoints are assumed to be sorted in ascending order.
*
* @template B - An object type where keys are strings and values are numbers representing screen widths.
* @param {number} width - The screen width to determine the breakpoint for.
* @param breakpointEntries - sorted pairs of breakpoints
* @returns {keyof B & string} The key of the breakpoint that the screen width falls into.
*
* @example
* const breakpoints = { sm: 0, md: 768, lg: 1024 }
* getBreakpointFromScreenWidth(800, breakpoints) // returns 'md'
*/
export const getBreakpointFromScreenWidth = (width: number, breakpointEntries: Array<[keyof UnistylesBreakpoints, UnistylesBreakpoints[keyof UnistylesBreakpoints]]>): keyof UnistylesBreakpoints & string => {
const [key] = breakpointEntries
.find(([, value], index, otherBreakpoints) => {
Expand All @@ -80,40 +46,13 @@ export const getBreakpointFromScreenWidth = (width: number, breakpointEntries: A
return key
}

/**
* Retrieves the value associated with a given breakpoint or custom media query based on the provided screen size.
*
* The function first checks for custom media queries. If a matching custom media query is found, its associated value is returned.
* If no custom media query matches, the function then checks for a direct breakpoint match.
* If there's no direct breakpoint match, the function simulates CSS cascading to find the closest matching breakpoint.
*
* @template B - An object type where keys represent breakpoint names and values represent breakpoint values.
*
* @param {Record<keyof B & string, string | number>} value - An object containing values associated with breakpoints or custom media queries.
* @param {keyof B & string} breakpoint - The breakpoint name to check against.
* @param {ScreenSize} screenSize - An object representing the screen size to be checked against the media queries.
* @param breakpointPairs - sorted pairs of breakpoints
*
* @returns {string | number | undefined} Returns the value associated with the matching breakpoint or custom media query, or `undefined` if no match is found.
*
* @example
*
* const values = { ':w[200]': 'value1', sm: 'value2', md: 'value3' }
* const screenSize = { width: 250, height: 400 }
* const breakpoints = { sm: 300, md: 600, lg: 900 }
*
* getValueForBreakpoint(values, 'sm', screenSize, breakpoints); // 'value1'
*/
export const getValueForBreakpoint = (
value: Record<keyof UnistylesBreakpoints | MediaQueries, string | number | undefined>,
breakpoint: keyof UnistylesBreakpoints,
screenSize: ScreenSize
): string | number | undefined => {
export const getValueForBreakpoint = (value: Record<keyof UnistylesBreakpoints | MediaQueries, string | number | undefined>): string | number | undefined => {
// the highest priority is for custom media queries
const customMediaQueries = Object
.entries(value)
.filter(([key]) => isMediaQuery(key))
const customMediaQueryKey = getKeyForCustomMediaQuery(customMediaQueries, screenSize) as keyof typeof value
.filter(([key]) => isMediaQuery(key)) as Array<[keyof UnistylesBreakpoints | MediaQueries, string | number | undefined]>
// const customMediaQueryKey = getKeyForCustomMediaQuery(customMediaQueries, screenSize) as keyof typeof value
const customMediaQueryKey = unistyles.engine.parseCustomMediaQuery(customMediaQueries) as keyof typeof value

if (customMediaQueryKey && customMediaQueryKey in value) {
return value[customMediaQueryKey]
Expand All @@ -132,8 +71,14 @@ export const getValueForBreakpoint = (
]
}

const breakpoint = unistyles.runtime.breakpoint

if (!breakpoint) {
return undefined
}

// if user defined breakpoints, then we look for the valid one
const unifiedKey = breakpoint?.toLowerCase() as keyof typeof value
const unifiedKey = breakpoint.toLowerCase() as keyof typeof value
const directBreakpoint = value[unifiedKey]

// if there is a direct key like 'sm' or 'md', or value for this key exists but its undefined
Expand Down
Loading

0 comments on commit 2d37841

Please sign in to comment.