From 0ff6f1d668446501a3f8b3eac5fc35186ebe30a5 Mon Sep 17 00:00:00 2001 From: matinzd Date: Mon, 20 Sep 2021 08:50:43 +0200 Subject: [PATCH 1/2] change textVariants to typography --- CHANGELOG.md | 2 +- README.md | 6 +++--- src/createText.ts | 4 ++-- src/test/createVariant.test.ts | 22 +++++++++++----------- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 945acc23..357dfa05 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ and adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). * Add 2-dimensional breakpoints [#70](https://github.com/Shopify/restyle/pull/70) by [@Johan-duitot](https://github.com/Johan-dutoit) ## 1.3.1 - 2020-10-26 -* Silently ignore any errors caused by missing a variant definition in the theme (e.g. textVariants), to preserve backwards compatibility. [#64](https://github.com/Shopify/restyle/pull/64) by [@jonogreenz](https://github.com/jonogreenz) +* Silently ignore any errors caused by missing a variant definition in the theme (e.g. typography), to preserve backwards compatibility. [#64](https://github.com/Shopify/restyle/pull/64) by [@jonogreenz](https://github.com/jonogreenz) * Disallow creating a variant with the existing base theme keys of `colors, spacing, breakpoints, zIndices, borderRadii`. [#64](https://github.com/Shopify/restyle/pull/64) by [@jonogreenz](https://github.com/jonogreenz) * Improve rendering performance by removing unnecessary uses of the spread operator. [#63](https://github.com/Shopify/restyle/pull/63) by [@JoelBesada](https://github.com/JoelBesada) * ~~Add a more descriptive error when theme is missing a key for a used variant.~~ [#59](https://github.com/Shopify/restyle/pull/59) by [@Charly6596](https://github.com/Charly6596) (*Replaced by [PR #64](https://github.com/Shopify/restyle/pull/64)*) diff --git a/README.md b/README.md index d5b6b101..e80d91a3 100644 --- a/README.md +++ b/README.md @@ -252,13 +252,13 @@ const Text = createText(); export default Text; ``` -The Text component comes with the following [Restyle functions](#predefined-restyle-functions): `color`, `opacity`, `visible`, `typography`, `textShadow`, `spacing`. It also includes a [variant](#Variants) that picks up styles under the `textVariants` key in your theme: +The Text component comes with the following [Restyle functions](#predefined-restyle-functions): `color`, `opacity`, `visible`, `typography`, `textShadow`, `spacing`. It also includes a [variant](#Variants) that picks up styles under the `typography` key in your theme: ```tsx // In your theme const theme = createTheme({ ..., - textVariants: { + typography: { header: { fontFamily: 'ShopifySans-Bold', fontWeight: 'bold', @@ -589,7 +589,7 @@ const theme = createTheme({ secondaryCardText: palette.black, }, breakpoints: {}, - textVariants: { + typography: { body: { fontSize: 16, lineHeight: 24, diff --git a/src/createText.ts b/src/createText.ts index 8407ef91..fc68601c 100644 --- a/src/createText.ts +++ b/src/createText.ts @@ -27,7 +27,7 @@ type BaseTextProps = ColorProps & TypographyProps & SpacingProps & TextShadowProps & - VariantProps; + VariantProps; export type TextProps< Theme extends BaseTheme, @@ -44,7 +44,7 @@ export const textRestyleFunctions = [ spacing, spacingShorthand, textShadow, - createVariant({themeKey: 'textVariants'}), + createVariant({themeKey: 'typography'}), ]; const createText = < diff --git a/src/test/createVariant.test.ts b/src/test/createVariant.test.ts index 2f2619ec..d44cbcfc 100644 --- a/src/test/createVariant.test.ts +++ b/src/test/createVariant.test.ts @@ -18,7 +18,7 @@ const theme = { height: 400, }, }, - textVariants: { + typography: { body: { fontSize: 14, lineHeight: 18, @@ -65,7 +65,7 @@ const dimensions = { describe('createVariant', () => { it('expands a variant to the given values in the theme', () => { - const variant = createVariant({themeKey: 'textVariants'}); + const variant = createVariant({themeKey: 'typography'}); expect(variant.func({variant: 'body'}, {theme, dimensions})).toStrictEqual({ fontSize: 14, lineHeight: 18, @@ -74,7 +74,7 @@ describe('createVariant', () => { it('accepts defaults', () => { const variant = createVariant({ - themeKey: 'textVariants', + themeKey: 'typography', defaults: { fontSize: 10, opacity: 0.5, @@ -147,7 +147,7 @@ describe('createVariant', () => { it('correctly overrides default values', () => { const variant = createVariant({ - themeKey: 'textVariants', + themeKey: 'typography', defaults: { fontSize: 10, opacity: 0.5, @@ -160,10 +160,10 @@ describe('createVariant', () => { }); }); - it('correctly creates textVariants without key in theme', () => { + it('correctly creates typography without key in theme', () => { const themeSubset = {...theme}; - delete themeSubset.textVariants; - const variant = createVariant({themeKey: 'textVariants'}); + delete themeSubset.typography; + const variant = createVariant({themeKey: 'typography'}); expect(variant.func({}, {theme: themeSubset, dimensions})).toStrictEqual( {}, ); @@ -175,7 +175,7 @@ describe('createVariant', () => { }); it('supports referencing other theme values in the variant', () => { - const variant = createVariant({themeKey: 'textVariants'}); + const variant = createVariant({themeKey: 'typography'}); expect( variant.func({variant: 'subheader'}, {theme, dimensions}), ).toStrictEqual({ @@ -185,7 +185,7 @@ describe('createVariant', () => { }); it('supports responsive values', () => { - const variant = createVariant({themeKey: 'textVariants'}); + const variant = createVariant({themeKey: 'typography'}); expect( variant.func( {variant: 'header'}, @@ -200,7 +200,7 @@ describe('createVariant', () => { }); it('supports more complex responsive values', () => { - const variant = createVariant({themeKey: 'textVariants'}); + const variant = createVariant({themeKey: 'typography'}); expect( variant.func( {variant: 'header'}, @@ -215,7 +215,7 @@ describe('createVariant', () => { }); it('falls back to the closest width', () => { - const variant = createVariant({themeKey: 'textVariants'}); + const variant = createVariant({themeKey: 'typography'}); expect( variant.func( {variant: 'header'}, From 7786241dac8f1c314a250ae658bb826d8369422d Mon Sep 17 00:00:00 2001 From: matinzd Date: Mon, 20 Sep 2021 14:23:27 +0200 Subject: [PATCH 2/2] feat: add make styles function --- README.md | 157 ++++++++++++++++++++++++++++------------------ src/index.ts | 1 + src/makeStyles.ts | 25 ++++++++ src/types.ts | 8 ++- 4 files changed, 129 insertions(+), 62 deletions(-) create mode 100644 src/makeStyles.ts diff --git a/README.md b/README.md index e80d91a3..d62b3ade 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,8 @@ const Welcome = () => { flex={1} backgroundColor="mainBackground" paddingVertical="xl" - paddingHorizontal="m"> + paddingHorizontal="m" + > Welcome { ![Restyle Component Workflow](https://user-images.githubusercontent.com/688415/75268259-95346900-57f7-11ea-8f1f-22aec4bb4f39.gif) - ## Installation + ### Yarn + ```bash $ yarn add @shopify/restyle ``` + ### NPM + ```bash $ npm install @shopify/restyle ``` @@ -85,7 +89,7 @@ Any project using this library should have a global theme object. It specifies s Below is an example of how a basic theme could look. Make sure to read the sections below for more details on how to set up your different theme values. ```ts -import { createTheme } from '@shopify/restyle' +import {createTheme} from '@shopify/restyle'; const palette = { purpleLight: '#8C6FF7', @@ -100,7 +104,6 @@ const palette = { white: '#F0F2F3', }; - const theme = createTheme({ colors: { mainBackground: palette.white, @@ -122,7 +125,7 @@ export type Theme = typeof theme; export default theme; ``` -*Note: `createTheme` doesn't do anything except enforcing the theme to have the same shape as the BaseTheme, but it preserves the types of your user specific values (e.g. what colors the theme has) so you don't lose typesafety as a result of the `{ [key:string]: any }` in BaseTheme* +_Note: `createTheme` doesn't do anything except enforcing the theme to have the same shape as the BaseTheme, but it preserves the types of your user specific values (e.g. what colors the theme has) so you don't lose typesafety as a result of the `{ [key:string]: any }` in BaseTheme_ This theme should be passed to a `ThemeProvider` at the top of your React tree: @@ -177,7 +180,6 @@ Taking the time to define these semantic meanings comes with a number of benefit Spacing tends to follow multiples of a given base spacing number, for example `8`. We prefer using the t-shirt size naming convention, because of the scalability of it (any number of `x`'s can be prepended for smaller and larger sizes): ```ts - const theme = createTheme({ spacing: { s: 8, @@ -198,7 +200,7 @@ const theme = createTheme({ phone: 0, longPhone: { width: 0, - height: 812 + height: 812, }, tablet: 768, largeTablet: 1024, @@ -286,6 +288,30 @@ const theme = createTheme({ Header ``` +### Making styles with `useStyles` hook + +If you want to create theme based on your theme you can use `makeStyles` function to create a hook for your component and then use it inside your component like this: + +```ts +import {View} from 'react-native'; +import {makeStyles} from '@shopify/restyle'; + +const Layout = () => { + const styles = useStyles(); + + return ; +}; + +const useStyles = makeStyle(theme => ({ + container: { + flex: 1, + backgroundColor: theme.colors.background, + }, +})); +``` + +You can either pass style object or function to it for your use case. + ### Custom Components If you want to create your own component similar to `Box` or `Text`, but decide @@ -298,17 +324,17 @@ import { createVariant, spacing, SpacingProps, - VariantProps + VariantProps, } from '@shopify/restyle'; -import {Theme} from './theme' +import {Theme} from './theme'; -type Props = SpacingProps & VariantProps +type Props = SpacingProps & VariantProps; const Card = createRestyleComponent([ spacing, - createVariant({themeKey: 'cardVariants'}) -]) + createVariant({themeKey: 'cardVariants'}), +]); -export default Card +export default Card; ``` For more advanced components, you may want to instead use the `useRestyle` hook: @@ -356,24 +382,24 @@ Restyle functions are the bread and butter of Restyle. They specify how props sh The Restyle library comes with a number of predefined Restyle functions for your convenience. Properties within brackets are aliases / shorthands for the preceding prop name. -| Restyle Function | Props | Theme Key | -| ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | -| backgroundColor | backgroundColor [bg] | colors | -| color | color | colors | -| opacity | opacity | _none_ | -| visible | display (maps `true` / `false` to `flex` / `none`) | _none_ | -| spacing | margin [m], marginTop [mt], marginRight [mr], marginBottom [mb], marginLeft [ml], marginStart [ms], marginEnd[me], marginHorizontal [mx], marginVertical [my], padding [p], paddingTop [pt], paddingRight [pr], paddingBottom [pb], paddingLeft [pl], paddingStart [ps], paddingEnd [pe], paddingHorizontal [px], paddingVertical [py] | spacing | -| layout | width, height, minWidth, maxWidth, minHeight, maxHeight, overflow, aspectRatio, alignContent, alignItems, alignSelf, justifyContent, flex, flexBasis, flexDirection, flexGrow, flexShrink, flexWrap | _none_ | -| position | position, top, right, bottom, left, start, end | _none_ | -| position | zIndex | zIndices | -| border | borderBottomWidth, borderLeftWidth, borderRightWidth, borderStartWidth, borderEndWidth, borderStyle, borderTopWidth, borderWidth | _none_ | -| border | borderColor, borderTopColor, borderRightColor, borderLeftColor, borderStartColor, borderEndColor, borderBottomColor | colors | -| border | borderRadius, borderBottomLeftRadius, borderBottomRightRadius, borderBottomStartRadius, borderBottomEndRadius, borderTopLeftRadius, borderTopRightRadius, borderTopStartRadius, borderTopEndRadius | borderRadii | -| shadow | shadowOpacity, shadowOffset, shadowRadius, elevation | _none_ | -| shadow | shadowColor | colors | -| textShadow | textShadowOffset, textShadowRadius | _none_ | -| textShadow | textShadowColor | colors | -| typography | fontFamily, fontSize, fontStyle, fontWeight, letterSpacing, lineHeight, textAlign, textDecorationLine, textDecorationStyle, textTransform | _none_ | +| Restyle Function | Props | Theme Key | +| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | +| backgroundColor | backgroundColor [bg] | colors | +| color | color | colors | +| opacity | opacity | _none_ | +| visible | display (maps `true` / `false` to `flex` / `none`) | _none_ | +| spacing | margin [m], marginTop [mt], marginRight [mr], marginBottom [mb], marginLeft [ml], marginStart [ms], marginEnd[me], marginHorizontal [mx], marginVertical [my], padding [p], paddingTop [pt], paddingRight [pr], paddingBottom [pb], paddingLeft [pl], paddingStart [ps], paddingEnd [pe], paddingHorizontal [px], paddingVertical [py] | spacing | +| layout | width, height, minWidth, maxWidth, minHeight, maxHeight, overflow, aspectRatio, alignContent, alignItems, alignSelf, justifyContent, flex, flexBasis, flexDirection, flexGrow, flexShrink, flexWrap | _none_ | +| position | position, top, right, bottom, left, start, end | _none_ | +| position | zIndex | zIndices | +| border | borderBottomWidth, borderLeftWidth, borderRightWidth, borderStartWidth, borderEndWidth, borderStyle, borderTopWidth, borderWidth | _none_ | +| border | borderColor, borderTopColor, borderRightColor, borderLeftColor, borderStartColor, borderEndColor, borderBottomColor | colors | +| border | borderRadius, borderBottomLeftRadius, borderBottomRightRadius, borderBottomStartRadius, borderBottomEndRadius, borderTopLeftRadius, borderTopRightRadius, borderTopStartRadius, borderTopEndRadius | borderRadii | +| shadow | shadowOpacity, shadowOffset, shadowRadius, elevation | _none_ | +| shadow | shadowColor | colors | +| textShadow | textShadowOffset, textShadowRadius | _none_ | +| textShadow | textShadowColor | colors | +| typography | fontFamily, fontSize, fontStyle, fontWeight, letterSpacing, lineHeight, textAlign, textDecorationLine, textDecorationStyle, textTransform | _none_ | #### Custom Restyle Functions @@ -487,7 +513,6 @@ const theme = createTheme({ ``` - If you need to extract the value of a responsive prop in a custom component (e.g. to use it outside of component styles), you can use the `useResponsiveProp` hook: ```tsx @@ -498,10 +523,14 @@ import { useTheme, } from '@shopify/restyle'; import React from 'react'; -import { ActivityIndicator, TouchableOpacity, TouchableOpacityProps } from 'react-native'; +import { + ActivityIndicator, + TouchableOpacity, + TouchableOpacityProps, +} from 'react-native'; import Text from './Text'; -import { Theme } from './theme'; +import {Theme} from './theme'; const BaseButton = createBox(TouchableOpacity); @@ -514,7 +543,7 @@ type Props = React.ComponentProps & const Button = ({ label, isLoading, - color = { phone: 'purple', tablet: 'blue' }, + color = {phone: 'purple', tablet: 'blue'}, ...props }: Props) => { const theme = useTheme(); @@ -540,7 +569,6 @@ const Button = ({ ); }; - ``` ### Overriding Styles @@ -564,7 +592,12 @@ Of course, no app is complete without a dark mode. Here a simple example of how ```tsx import React, {useState} from 'react'; import {Switch} from 'react-native'; -import {ThemeProvider, createBox, createText, createTheme} from '@shopify/restyle'; +import { + ThemeProvider, + createBox, + createText, + createTheme, +} from '@shopify/restyle'; export const palette = { purple: '#5A31F4', @@ -669,11 +702,12 @@ export default App; To start using Shopify style assets we can leverage [Polaris tokens](https://github.com/Shopify/polaris-tokens). You can see all of the tokens [here](https://polaris-tokens.herokuapp.com/). #### Installation -Using [npm](https://www.npmjs.com/): + +Using [npm](https://www.npmjs.com/): `npm install @shopify/polaris-tokens --save` -Using [yarn](https://yarnpkg.com/en/): +Using [yarn](https://yarnpkg.com/en/): `yarn add @shopify/polaris-tokens` @@ -682,7 +716,7 @@ Using [yarn](https://yarnpkg.com/en/): ```tsx // In theme import tokens from '@shopify/polaris-tokens'; -import { createTheme } from '@shopify/restyle' +import {createTheme} from '@shopify/restyle'; const pxToNumber = (px: string) => { return parseInt(px.replace('px', ''), 10); @@ -690,39 +724,40 @@ const pxToNumber = (px: string) => { const theme = createTheme({ colors: { - body: tokens.colorBlack, - backgroundRegular: tokens.colorWhite, - backgroundSubdued: tokens.colorSkyLighter, + body: tokens.colorBlack, + backgroundRegular: tokens.colorWhite, + backgroundSubdued: tokens.colorSkyLighter, - foregroundRegular: tokens.colorBlack, - foregroundOff: tokens.colorInkLight, - foregroundSubdued: tokens.colorInkLightest, - foregroundContrasting: tokens.colorWhite, - foregroundSuccess: tokens.colorGreenDark, + foregroundRegular: tokens.colorBlack, + foregroundOff: tokens.colorInkLight, + foregroundSubdued: tokens.colorInkLightest, + foregroundContrasting: tokens.colorWhite, + foregroundSuccess: tokens.colorGreenDark, - highlightPrimary: tokens.colorIndigo, - highlightPrimaryDisabled: tokens.colorIndigoLight, + highlightPrimary: tokens.colorIndigo, + highlightPrimaryDisabled: tokens.colorIndigoLight, - buttonBackgroundPlain: tokens.colorSky, - errorPrimary: tokens.colorRed, + buttonBackgroundPlain: tokens.colorSky, + errorPrimary: tokens.colorRed, - iconBackgroundDark: tokens.colorBlueDarker, + iconBackgroundDark: tokens.colorBlueDarker, }, spacing: { - none: tokens.spacingNone, - xxs: pxToNumber(tokens.spacingExtraTight), - xs: pxToNumber(tokens.spacingTight), - s: pxToNumber(tokens.spacingBaseTight), - m: pxToNumber(tokens.spacingBase), - l: pxToNumber(tokens.spacingLoose), - xl: pxToNumber(tokens.spacingExtraLoose), - xxl: 2 * pxToNumber(tokens.spacingExtraLoose), + none: tokens.spacingNone, + xxs: pxToNumber(tokens.spacingExtraTight), + xs: pxToNumber(tokens.spacingTight), + s: pxToNumber(tokens.spacingBaseTight), + m: pxToNumber(tokens.spacingBase), + l: pxToNumber(tokens.spacingLoose), + xl: pxToNumber(tokens.spacingExtraLoose), + xxl: 2 * pxToNumber(tokens.spacingExtraLoose), }, }); export type Theme = typeof theme; export default theme; ``` + Now you can easily style your components with [Shopify Polaris](https://polaris.shopify.com/). ### Inspiration diff --git a/src/index.ts b/src/index.ts index 10329e02..c817ce88 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,3 +14,4 @@ export {default as useResponsiveProp} from './hooks/useResponsiveProp'; export {default as createTheme} from './createTheme'; export {default as createRestyleFunction} from './createRestyleFunction'; export {default as createRestyleComponent} from './createRestyleComponent'; +export {default as makeStyles} from './makeStyles'; diff --git a/src/makeStyles.ts b/src/makeStyles.ts new file mode 100644 index 00000000..475754e3 --- /dev/null +++ b/src/makeStyles.ts @@ -0,0 +1,25 @@ +import {StyleSheet} from 'react-native'; +import {BaseTheme, CreateStyleFn, RNStyleSheetType} from 'types'; + +import useTheme from './hooks/useTheme'; + +function styleCreator>( + stylesOrCreator: CreateStyleFn, +) { + return (appTheme: U) => + typeof stylesOrCreator === 'function' + ? StyleSheet.create(stylesOrCreator(appTheme)) + : StyleSheet.create(stylesOrCreator); +} + +function makeStyles>( + stylesOrCreator: CreateStyleFn, +) { + const useStyles = () => { + const appTheme = useTheme() as U; + return styleCreator(stylesOrCreator)(appTheme); + }; + return useStyles; +} + +export default makeStyles; diff --git a/src/types.ts b/src/types.ts index 957947f5..052872dd 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,4 +1,4 @@ -import {ImageStyle, TextStyle, ViewStyle} from 'react-native'; +import {ImageStyle, StyleSheet, TextStyle, ViewStyle} from 'react-native'; export type ResponsiveValue = | Value @@ -66,3 +66,9 @@ export type RNStyleProperty = | keyof ImageStyle; export type PropValue = string | number | undefined | null; + +export type RNStyleSheetType = StyleSheet.NamedStyles; + +export type CreateStyleFn, U extends BaseTheme> = + | ((theme: U) => T) + | T;