Skip to content

Commit

Permalink
a near rewrite after updating the build tools and added an examples page
Browse files Browse the repository at this point in the history
  • Loading branch information
James Newell committed Feb 5, 2018
1 parent c0282be commit 185f3df
Show file tree
Hide file tree
Showing 7 changed files with 231 additions and 96 deletions.
2 changes: 2 additions & 0 deletions .flowconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[ignore]
<PROJECT_ROOT>/dist
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
node_modules
dist
yarn.lock
package-lock.json
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,12 +219,16 @@ These are the default breakpoints provided:

## Change log

### 2.0.0

- break: removed support for non-numeric breakpoint values (so we can perform numerical operations on the values)

### 1.0.3

- changed how the package is built
- added a demo page

### 1.0.
### 1.0.2

- updated `peerDependency` for `styled-components` to support v3 - 👏 Thanks [@ApacheEx](https://github.com/ApacheEx) ([#10](https://github.com/jameslnewell/styled-components-breakpoint/pull/10))
- fixed a bug in the `map()` fn
Expand Down
120 changes: 79 additions & 41 deletions example/components/App.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import createComponentFromTagProp from 'react-create-component-from-tag-prop';
import styled, { injectGlobal, ThemeProvider } from 'styled-components';

// eslint-disable-next-line import/no-extraneous-dependencies, import/no-unresolved
import breakpoint, { map } from 'styled-components-breakpoint';

Expand Down Expand Up @@ -28,17 +28,22 @@ injectGlobal`
${breakpoint('tablet') `
font-size: 1em;
`}
`({})}
${breakpoint('desktop') `
font-size: 1.1em;
`}
`({})}
}
`;
/* eslint-enable no-unused-expressions */

const whitelistedDiv = createComponentFromTagProp({
tag: 'div',
propsToOmit: ['name', 'color', 'operator', 'visible']
});

const Main = styled.main`
`;

Expand All @@ -50,56 +55,64 @@ const H2 = styled.h2`
margin: 1em 0;
`;

const Breakpoint = styled.div`
const P = styled.p`
margin: 1em 0;
`;

const Instruction = styled.blockquote`
margin: 1em 3em;
font-size: 0.9em;
font-weight: bold;
text-align: right;
`;

const Breakpoint = styled(whitelistedDiv) `
display: flex;
align-items: center;
padding: 1em;
:after {
content: '❌';
text-align: right;
}
${({ name, theme }) => breakpoint(name, theme.breakpoints) `
${({ gte, lt }) => breakpoint(gte, lt) `
background-color: ${({ color }) => color};
:after {
content: '✅';
}
`}
`;

const BreakpointMap = styled.div`
const BreakpointMap = styled(whitelistedDiv) `
display: flex;
align-items: center;
padding: 1em;
:after {
content: '✅';
text-align: right;
}
${({ visible, color }) => map(visible, (value = false) => {
if (value) {
return `
background-color: ${color};
:after {
content: '✅';
}
`;
} else {
return `
background-color: transparent;
:after {
content: '❌';
}
`;
}
${({ color }) => map(color, (val = 'transparent') => {
return `background-color: ${val};`;
})}
`;

const BreakpointTitle = styled.div`
flex-shrink: 0;
width: 5em;
font-weight: bold;
${({ text = {} }) => map(text, (val = '') => {
return `:before {
content: '${val}';
}`;
})}
`;

const BreakpointCode = styled.pre`
margin-right: 1em;
flex-grow: 1;
color: #666;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
`;

export default function App() {
Expand All @@ -108,57 +121,82 @@ export default function App() {
<H1>styled-components-breakpoint</H1>

<H2 id="default-breakpoints">Default Breakpoints</H2>
<Breakpoint name="mobile" color="#FFE7AC">
<Breakpoint gte="mobile" color="#FFE7AC">
<BreakpointTitle>Mobile</BreakpointTitle>
<BreakpointCode>{"${breakpoint('mobile') `/* styles go here */`}"}</BreakpointCode>
</Breakpoint>
<Breakpoint name="tablet" color="#EAFFAC">
<Breakpoint gte="tablet" color="#EAFFAC">
<BreakpointTitle>Tablet</BreakpointTitle>
<BreakpointCode>{"${breakpoint('tablet') `/* styles go here */`}"}</BreakpointCode>
</Breakpoint>
<Breakpoint name="desktop" color="#AFEBFF">
<Breakpoint gte="desktop" color="#AFEBFF">
<BreakpointTitle>Desktop</BreakpointTitle>
<BreakpointCode>{"${breakpoint('desktop') `/* styles go here */`}"}</BreakpointCode>
</Breakpoint>
<blockquote>Try resizing the page. 👉</blockquote>
<Instruction>Try resizing the page. 👉</Instruction>

<H2 id="custom-breakpoints">Custom Breakpoints</H2>
<ThemeProvider theme={{ breakpoints: BOOTSTRAP_BREAKPOINTS }}>
<div>
<Breakpoint name="xs" color="FFE7AC">
<Breakpoint gte="xs" color="FFE7AC">
<BreakpointTitle>XS</BreakpointTitle>
<BreakpointCode>{"${breakpoint('xs') `/* styles go here */`}"}</BreakpointCode>
</Breakpoint>
<Breakpoint name="sm" color="#EAFFAC">
<Breakpoint gte="sm" color="#EAFFAC">
<BreakpointTitle>SM</BreakpointTitle>
<BreakpointCode>{"${breakpoint('sm') `/* styles go here */`}"}</BreakpointCode>
</Breakpoint>
<Breakpoint name="lg" color="#AFEBFF">
<Breakpoint gte="lg" color="#AFEBFF">
<BreakpointTitle>LG</BreakpointTitle>
<BreakpointCode>{"${breakpoint('lg') `/* styles go here */`}"}</BreakpointCode>
</Breakpoint>
<Breakpoint name="xl" color="#FFACE8">
<Breakpoint gte="xl" color="#FFACE8">
<BreakpointTitle>XL</BreakpointTitle>
<BreakpointCode>{"${breakpoint('xl') `/* styles go here */`}"}</BreakpointCode>
</Breakpoint>
<blockquote>Try resizing the page. 👉</blockquote>
<Instruction>Try resizing the page. 👉</Instruction>
</div>
</ThemeProvider>

<H2 id="map">Map</H2>
<BreakpointMap visible={{ mobile: true, tablet: false }} color="#FFE7AC">
<H2 id="gte">gte</H2>
<P>Greater than or equal to.</P>
<Breakpoint gte="mobile" color="FFE7AC">
<BreakpointTitle>Mobile</BreakpointTitle>
<BreakpointCode>{"${map({ mobile: true, tablet: false }, val => `/* styles go here */`)}"}</BreakpointCode>
</BreakpointMap>
<BreakpointMap visible={{ tablet: true, desktop: false }} color="#EAFFAC">
<BreakpointCode>{"${breakpoint('mobile') `/* styles go here */`}"}</BreakpointCode>
</Breakpoint>
<Breakpoint gte="tablet" color="EAFFAC">
<BreakpointTitle>Tablet</BreakpointTitle>
<BreakpointCode>{"${map({ tablet: true, desktop: false }, val => `/* styles go here */`)}"}</BreakpointCode>
</BreakpointMap>
<BreakpointMap visible={{ desktop: true }} color="#AFEBFF">
<BreakpointCode>{"${breakpoint('tablet') `/* styles go here */`}"}</BreakpointCode>
</Breakpoint>
<Breakpoint gte="desktop" color="AFEBFF">
<BreakpointTitle>Desktop</BreakpointTitle>
<BreakpointCode>{"${map({desktop: true}, val => `/* styles go here */`)}"}</BreakpointCode>
<BreakpointCode>{"${breakpoint('desktop') `/* styles go here */`}"}</BreakpointCode>
</Breakpoint>
<Instruction>Try resizing the page. 👉</Instruction>

<H2 id="gte-and-lt">gte and lt</H2>
<P>Greater than or equal to X but less than Y.</P>
<Breakpoint gte="mobile" lt="tablet" color="FFE7AC">
<BreakpointTitle>Mobile</BreakpointTitle>
<BreakpointCode>{"${breakpoint('mobile', 'tablet') `/* styles go here */`}"}</BreakpointCode>
</Breakpoint>
<Breakpoint gte="tablet" lt="desktop" color="EAFFAC">
<BreakpointTitle>Tablet</BreakpointTitle>
<BreakpointCode>{"${breakpoint('tablet', 'desktop') `/* styles go here */`}"}</BreakpointCode>
</Breakpoint>
<Breakpoint gte="desktop" color="AFEBFF">
<BreakpointTitle>Desktop</BreakpointTitle>
<BreakpointCode>{"${breakpoint('desktop') `/* styles go here */`}"}</BreakpointCode>
</Breakpoint>
<Instruction>Try resizing the page. 👉</Instruction>

<H2 id="map">map</H2>
<P>Map a value to styles for each breakpoint where a value is specified.</P>
<BreakpointMap color={{ mobile: '#FFE7AC', tablet: '#EAFFAC', desktop: '#AFEBFF' }}>
<BreakpointTitle text={{ mobile: 'Mobile', tablet: 'Tablet', desktop: 'Desktop' }} />
<BreakpointCode>{"${map({ mobile: '#FFE7AC', tablet: '#EAFFAC', desktop: '#AFEBFF' }, val => `/* styles go here */`)}"}</BreakpointCode>
</BreakpointMap>
<blockquote>Try resizing the page. 👉</blockquote>
<Instruction>Try resizing the page. 👉</Instruction>

</Main>
);
Expand Down
14 changes: 9 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"name": "styled-components-breakpoint",
"version": "1.0.3",
"repository": "jameslnewell/styled-components-breakpoint",
"version": "2.0.0",
"description": "Utility functions for creating breakpoints in `styled-components` 💅.",
"keywords": [
"react",
Expand All @@ -12,7 +13,6 @@
"tablet",
"desktop"
],
"repository": "jameslnewell/styled-components-breakpoint",
"main": "dist/cjs/index.js",
"jsnext:main": "dist/esm/index.js",
"files": [
Expand All @@ -25,8 +25,9 @@
"@tradie/react-component-scripts": "^1.0.0-alpha.a573b72a",
"gh-pages": "^1.1.0",
"react": "^16.2.0",
"react-create-component-from-tag-prop": "^1.3.1",
"react-dom": "^16.2.0",
"styled-components": "^3.1.5"
"styled-components": "^3.1.6"
},
"scripts": {
"clean": "tradie clean",
Expand All @@ -37,5 +38,8 @@
"prepublishOnly": "yarn run clean && yarn run build && yarn run test",
"postpublish": "yarn run deploy"
},
"license": "MIT"
}
"license": "MIT",
"dependencies": {
"babel-eslint": "^8.2.1"
}
}
100 changes: 100 additions & 0 deletions src/core.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// @flow
/* global process */
import { css } from 'styled-components';

export type BreakpointMap = { [name: string]: number };
export type BreakpointValueMap = string | { [name: string]: any };
export type MapBreakpointValueToCSSFunction = (value: ?any) => string | (...args: any[]) => string;

function convertPxToEm(pixels: number): number {
// @media is always calculated off 16px regardless of whether the root font size is the default or not
return pixels / 16;
}

function getValueFromName(breakpoints: BreakpointMap, name: string): number {
const value = breakpoints[name];
if (process.env.NODE_ENV !== 'production' && typeof value === 'undefined') {
console.error(`A breakpoint named "${name}" does not exist.`);
return 0;
}
return value;
}

function withSingleCriteria(breakpoints: BreakpointMap, name: string, operator: 'min-width' | 'max-width', offset: number = 0) {
const value = getValueFromName(breakpoints, name);

// special case for 0 to avoid wrapping styles in an unnecessary @media block
// FIXME: typings
// if (operator === 'max-width' && value === 0) {
// return () => '';
// }

// special case for 0 to avoid wrapping styles in an unnecessary @media block
if (operator === 'min-width' && value === 0) {
return (...args: any[]) => {
return css(...args);
}
}

return (...args: any) => {
return css`@media (${operator}: ${convertPxToEm(value + offset)}em) {
${css(...args)}
}`;
};
}

export function _gt(breakpoints: BreakpointMap, name: string) {
return withSingleCriteria(breakpoints, name, 'min-width', +1);
}

export function _gte(breakpoints: BreakpointMap, name: string) {
return withSingleCriteria(breakpoints, name, 'min-width');
}

export function _lt(breakpoints: BreakpointMap, name: string) {
return withSingleCriteria(breakpoints, name, 'max-width', -1);
}

export function _lte(breakpoints: BreakpointMap, name: string) {
return withSingleCriteria(breakpoints, name, 'max-width');
}

export function _between(breakpoints: BreakpointMap, gte: string, lt: string) {
const gteValue = getValueFromName(breakpoints, gte);
const ltValue = getValueFromName(breakpoints, lt);
return (...args: any) => {
return css`@media (min-width: ${convertPxToEm(gteValue)}em) and (max-width: ${convertPxToEm(ltValue - 1)}em) {
${css(...args)}
}`;
};
}

export function _breakpoint(breakpoints: BreakpointMap, gte: string, lt?: string) {
if (typeof lt === 'undefined') {
return _gte(breakpoints, gte);
} else {
return _between(breakpoints, gte, lt);
}
};

// TODO: allow the operator to be customised
export function _map(breakpoints: BreakpointMap, value: BreakpointValueMap, mapValueToCSS: MapBreakpointValueToCSSFunction) {
const values = value;

if (typeof values === 'string') {
return mapValueToCSS(value);
}

return [
// eslint-disable-next-line no-undefined
mapValueToCSS(undefined), // set the default value
...Object.keys(values).map((name: string) => {
const tag = _gte(breakpoints, name);
const val = values[name];
const styles = tag([].concat(mapValueToCSS(val)));
return styles;
})
];

};

Loading

0 comments on commit 185f3df

Please sign in to comment.