From 914c8156a13c9b8a975a144f11cbfebe37467a0b Mon Sep 17 00:00:00 2001 From: Artur Yorsh Date: Fri, 17 Jan 2020 12:29:54 +0300 Subject: [PATCH 1/3] refactor(components): deprecate useStyleSheet hook --- docs/src/structure.ts | 2 +- .../theme/application/application.spec.tsx | 2 +- src/components/theme/index.ts | 3 +- ...et.service.ts => evaStyleSheet.service.ts} | 22 +++++++------- src/components/theme/style/style.spec.tsx | 16 +++++----- .../theme/style/styleConsumer.service.ts | 6 ++-- .../theme/style/styleSheet.service.tsx | 29 +++++++++++++++++++ src/components/theme/style/styled.tsx | 2 +- src/components/theme/style/useStyleSheet.tsx | 26 +++++++++++++---- src/components/theme/theme/theme.spec.tsx | 8 ++--- src/components/theme/theme/themeContext.ts | 2 +- .../theme/theme/themeProvider.component.tsx | 6 ++-- src/components/theme/theme/useTheme.tsx | 8 ++++- src/components/theme/theme/withStyles.tsx | 2 +- .../useStyleSheetSimpleUsage.component.tsx | 6 ++-- 15 files changed, 96 insertions(+), 44 deletions(-) rename src/components/theme/style/{styleSheet.service.ts => evaStyleSheet.service.ts} (67%) create mode 100644 src/components/theme/style/styleSheet.service.tsx diff --git a/docs/src/structure.ts b/docs/src/structure.ts index c803577a4..5a8ccae22 100644 --- a/docs/src/structure.ts +++ b/docs/src/structure.ts @@ -235,7 +235,7 @@ export const structure = [ icon: 'withStyles.svg', source: [ 'useTheme', - 'useStyleSheet', + 'StyleSheet', 'withStyles', ], }, diff --git a/src/components/theme/application/application.spec.tsx b/src/components/theme/application/application.spec.tsx index 7f0a0d9ab..00e3d36f2 100644 --- a/src/components/theme/application/application.spec.tsx +++ b/src/components/theme/application/application.spec.tsx @@ -19,7 +19,7 @@ import { theme, themeInverse, } from '../support/tests'; -import { ThemeType } from '../style/styleSheet.service'; +import { ThemeType } from '../style/evaStyleSheet.service'; describe('@app: application wrapper check', () => { diff --git a/src/components/theme/index.ts b/src/components/theme/index.ts index da23e713a..3cd270c8c 100644 --- a/src/components/theme/index.ts +++ b/src/components/theme/index.ts @@ -17,11 +17,12 @@ export { StyledComponentClass, } from './style/styled'; export { useStyleSheet } from './style/useStyleSheet'; +export { StyleSheet } from './style/styleSheet.service'; export { StyleType, Styles, ThemeType, -} from './style/styleSheet.service'; +} from './style/evaStyleSheet.service'; export { Interaction, State, diff --git a/src/components/theme/style/styleSheet.service.ts b/src/components/theme/style/evaStyleSheet.service.ts similarity index 67% rename from src/components/theme/style/styleSheet.service.ts rename to src/components/theme/style/evaStyleSheet.service.ts index 6e4be5e1e..2b68055b5 100644 --- a/src/components/theme/style/styleSheet.service.ts +++ b/src/components/theme/style/evaStyleSheet.service.ts @@ -6,42 +6,42 @@ export type ThemeType = Record; export type StyleType = Record; export type Styles = RNStyleSheet.NamedStyles; -export class StyleSheet { +export class EvaStyleSheet { static createThemedStyles = (styles: Styles, theme: ThemeType): T => { return Object.keys(styles).reduce((acc: T, key: string): T => { - return { ...acc, [key]: StyleSheet.createThemedStyle(styles[key], theme) }; + return { ...acc, [key]: EvaStyleSheet.createThemedStyle(styles[key], theme) }; }, {} as T); }; static createThemedStyle = (style: StyleType, theme: ThemeType): StyleType => { return Object.keys(style).reduce((acc: StyleType, key: string): StyleType => { const value: any = style[key]; - return { ...acc, [key]: StyleSheet.getThemeValue(value, theme, value) }; + return { ...acc, [key]: EvaStyleSheet.getThemeValue(value, theme, value) }; }, {}); }; static createCompiledTheme = (theme: ThemeType): ThemeType => { return Object.keys(theme).reduce((acc: ThemeType, key: string): ThemeType => { - return { ...acc, [key]: StyleSheet.getThemeValue(key, theme, key) }; + return { ...acc, [key]: EvaStyleSheet.getThemeValue(key, theme, key) }; }, {}); }; static getThemeValue = (name: string, theme: ThemeType, fallback?: ThemeValue): ThemeValue | undefined => { - if (StyleSheet.isReference(name)) { - const themeKey: string = StyleSheet.toThemeKey(name); - return StyleSheet.findThemeValue(themeKey, theme) || fallback; + if (EvaStyleSheet.isReference(name)) { + const themeKey: string = EvaStyleSheet.toThemeKey(name); + return EvaStyleSheet.findThemeValue(themeKey, theme) || fallback; } - return StyleSheet.findThemeValue(name, theme) || fallback; + return EvaStyleSheet.findThemeValue(name, theme) || fallback; }; static findThemeValue = (name: string, theme: ThemeType): ThemeValue | undefined => { const value: ThemeValue = theme[name]; - if (StyleSheet.isReference(value)) { - const themeKey: string = StyleSheet.toThemeKey(value); - return StyleSheet.findThemeValue(themeKey, theme); + if (EvaStyleSheet.isReference(value)) { + const themeKey: string = EvaStyleSheet.toThemeKey(value); + return EvaStyleSheet.findThemeValue(themeKey, theme); } return value; diff --git a/src/components/theme/style/style.spec.tsx b/src/components/theme/style/style.spec.tsx index ee675abcd..5c3fbf7d0 100644 --- a/src/components/theme/style/style.spec.tsx +++ b/src/components/theme/style/style.spec.tsx @@ -23,9 +23,9 @@ import { import { StyleConsumerService } from './styleConsumer.service'; import { Interaction } from './type'; import { - StyleSheet, + EvaStyleSheet, ThemeType, -} from './styleSheet.service'; +} from './evaStyleSheet.service'; import { styles, theme, @@ -134,27 +134,27 @@ describe('@style: consumer service methods check', () => { describe('@style-sheet: service checks', () => { it('finds theme value properly', async () => { - const themeValue = StyleSheet.getThemeValue('gray-100', theme); - const undefinedValue = StyleSheet.getThemeValue('undefined', theme); + const themeValue = EvaStyleSheet.getThemeValue('gray-100', theme); + const undefinedValue = EvaStyleSheet.getThemeValue('undefined', theme); expect(themeValue).toEqual(theme['gray-100']); expect(undefinedValue).toBeUndefined(); }); it('finds referencing theme value properly', async () => { - const themeValue = StyleSheet.getThemeValue('referencing', theme); + const themeValue = EvaStyleSheet.getThemeValue('referencing', theme); expect(themeValue).toEqual(theme['gray-100']); }); it('finds multiple referencing theme value properly', async () => { - const themeValue = StyleSheet.getThemeValue('double-referencing', theme); + const themeValue = EvaStyleSheet.getThemeValue('double-referencing', theme); expect(themeValue).toEqual(theme['gray-100']); }); it('finds referencing theme value properly (initial reference)', async () => { - const themeValue = StyleSheet.getThemeValue('referencing', theme); + const themeValue = EvaStyleSheet.getThemeValue('referencing', theme); expect(themeValue).toEqual(theme['gray-100']); }); @@ -167,7 +167,7 @@ describe('@style-sheet: service checks', () => { prop4: 42, }; - const value = StyleSheet.createThemedStyle(mapping as ViewStyle, theme); + const value = EvaStyleSheet.createThemedStyle(mapping as ViewStyle, theme); expect(value).toMatchSnapshot(); }); diff --git a/src/components/theme/style/styleConsumer.service.ts b/src/components/theme/style/styleConsumer.service.ts index bdc0eb1bb..f7f20bb52 100644 --- a/src/components/theme/style/styleConsumer.service.ts +++ b/src/components/theme/style/styleConsumer.service.ts @@ -11,10 +11,10 @@ import { } from '@eva-design/dss'; import { StyledComponentProps } from './styled'; import { - StyleSheet, + EvaStyleSheet, StyleType, ThemeType, -} from './styleSheet.service'; +} from './evaStyleSheet.service'; import { Interaction } from './type'; const SEPARATOR_MAPPING_ENTRY: string = '.'; @@ -84,7 +84,7 @@ export class StyleConsumerService { } const mapping: StyleType = this.withValidParameters(generatedMapping); - const themedStyle: StyleType = StyleSheet.createThemedStyle(mapping, theme); + const themedStyle: StyleType = EvaStyleSheet.createThemedStyle(mapping, theme); return { ...source, theme, themedStyle }; } diff --git a/src/components/theme/style/styleSheet.service.tsx b/src/components/theme/style/styleSheet.service.tsx new file mode 100644 index 000000000..b6e78a1e0 --- /dev/null +++ b/src/components/theme/style/styleSheet.service.tsx @@ -0,0 +1,29 @@ +/** + * @license + * Copyright Akveo. All Rights Reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + */ + +import { EvaStyleSheet, Styles, ThemeType } from './evaStyleSheet.service'; +import { useTheme } from '../theme/useTheme'; + +/** + * Service for creating styles that fit current theme. + * Unlike StyleSheet class exported from React Native package, it allows using Eva theme variables. + * + * @overview-example UseStyleSheetSimpleUsage + */ +export class StyleSheet { + + /** + * @returns a hook function that returns style mapped to current theme from the given object. + */ + static create = >(styles: Styles): () => T => { + return (): T => { + // eslint-disable-next-line react-hooks/rules-of-hooks + const theme: ThemeType = useTheme(); + return EvaStyleSheet.createThemedStyles(styles, theme); + }; + }; +} + diff --git a/src/components/theme/style/styled.tsx b/src/components/theme/style/styled.tsx index 34a78cd6b..cfb3ddcf2 100644 --- a/src/components/theme/style/styled.tsx +++ b/src/components/theme/style/styled.tsx @@ -11,7 +11,7 @@ import { StyleConsumerService } from './styleConsumer.service'; import { StyleType, ThemeType, -} from './styleSheet.service'; +} from './evaStyleSheet.service'; import { Interaction } from './type'; import { MappingContext } from '../mapping/mappingContext'; import { ThemeContext } from '../theme/themeContext'; diff --git a/src/components/theme/style/useStyleSheet.tsx b/src/components/theme/style/useStyleSheet.tsx index 96a534b26..876d20fcb 100644 --- a/src/components/theme/style/useStyleSheet.tsx +++ b/src/components/theme/style/useStyleSheet.tsx @@ -1,20 +1,36 @@ +/** + * @license + * Copyright Akveo. All Rights Reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + */ + import { Styles, - StyleSheet, + EvaStyleSheet, ThemeType, -} from '../style/styleSheet.service'; +} from './evaStyleSheet.service'; import { useTheme } from '../theme/useTheme'; /** - * Takes a theme provided by ApplicationProvider or ThemeProvider and applies it to style. + * @deprecated since version 4.4.0-beta.3. Use `StyleSheet.create` from this package instead. * - * @overview-example UseStyleSheetSimpleUsage + * Takes a theme provided by ApplicationProvider or ThemeProvider and applies it to style. */ export const useStyleSheet = >(styles: Styles) => { + const docRoot: string = 'https://akveo.github.io/react-native-ui-kitten/docs'; + + const message: string = [ + 'useStyleSheet is deprecated and will be removed in a stable version', + 'Consider migrating to a new syntax.', + `📖 Documentation: ${docRoot}/components/themed-component/overview#stylesheet`, + ].join('\n'); + + console.warn(message); + return { create: (): T => { const theme: ThemeType = useTheme(); - return StyleSheet.createThemedStyles(styles, theme); + return EvaStyleSheet.createThemedStyles(styles, theme); }, }; }; diff --git a/src/components/theme/theme/theme.spec.tsx b/src/components/theme/theme/theme.spec.tsx index 828151bf3..0840bc12d 100644 --- a/src/components/theme/theme/theme.spec.tsx +++ b/src/components/theme/theme/theme.spec.tsx @@ -20,9 +20,9 @@ import { withStyles, } from './withStyles'; import { - StyleSheet, + EvaStyleSheet, ThemeType, -} from '../style/styleSheet.service'; +} from '../style/evaStyleSheet.service'; import { theme, themeInverse, @@ -169,8 +169,8 @@ describe('@theme: ui component checks', () => { const { theme: theme1 } = themedComponents[0].props; const { theme: theme2 } = themedComponents[1].props; - expect(theme1).toEqual(StyleSheet.createCompiledTheme(theme)); - expect(theme2).toEqual(StyleSheet.createCompiledTheme(themeInverse)); + expect(theme1).toEqual(EvaStyleSheet.createCompiledTheme(theme)); + expect(theme2).toEqual(EvaStyleSheet.createCompiledTheme(themeInverse)); }); }); diff --git a/src/components/theme/theme/themeContext.ts b/src/components/theme/theme/themeContext.ts index 29aa894eb..3937d4568 100644 --- a/src/components/theme/theme/themeContext.ts +++ b/src/components/theme/theme/themeContext.ts @@ -5,7 +5,7 @@ */ import React from 'react'; -import { ThemeType } from '../style/styleSheet.service'; +import { ThemeType } from '../style/evaStyleSheet.service'; const defaultTheme: ThemeType = {}; diff --git a/src/components/theme/theme/themeProvider.component.tsx b/src/components/theme/theme/themeProvider.component.tsx index 0e5ac9cc2..839cd07a9 100644 --- a/src/components/theme/theme/themeProvider.component.tsx +++ b/src/components/theme/theme/themeProvider.component.tsx @@ -7,9 +7,9 @@ import React from 'react'; import { ThemeContext } from './themeContext'; import { - StyleSheet, + EvaStyleSheet, ThemeType, -} from '../style/styleSheet.service'; +} from '../style/evaStyleSheet.service'; export interface ThemeProviderProps { theme: ThemeType; @@ -32,7 +32,7 @@ export class ThemeProvider extends React.PureComponent { return ( + value={EvaStyleSheet.createCompiledTheme(theme)}> {children} ); diff --git a/src/components/theme/theme/useTheme.tsx b/src/components/theme/theme/useTheme.tsx index 8854f5ab4..e6c0a014e 100644 --- a/src/components/theme/theme/useTheme.tsx +++ b/src/components/theme/theme/useTheme.tsx @@ -1,6 +1,12 @@ +/** + * @license + * Copyright Akveo. All Rights Reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + */ + import React from 'react'; import { ThemeContext } from './themeContext'; -import { ThemeType } from '../style/styleSheet.service'; +import { ThemeType } from '../style/evaStyleSheet.service'; /** * Takes an actual theme provided by ApplicationProvider or ThemeProvider and returns it to a functional component. diff --git a/src/components/theme/theme/withStyles.tsx b/src/components/theme/theme/withStyles.tsx index 3e3a53171..9bd0e4eda 100644 --- a/src/components/theme/theme/withStyles.tsx +++ b/src/components/theme/theme/withStyles.tsx @@ -10,7 +10,7 @@ import { ThemeContext } from './themeContext'; import { Styles, ThemeType, -} from '../style/styleSheet.service'; +} from '../style/evaStyleSheet.service'; interface PrivateProps { forwardedRef?: React.RefObject; diff --git a/src/showcases/components/useStyleSheet/useStyleSheetSimpleUsage.component.tsx b/src/showcases/components/useStyleSheet/useStyleSheetSimpleUsage.component.tsx index 80ad05f01..217995e15 100644 --- a/src/showcases/components/useStyleSheet/useStyleSheetSimpleUsage.component.tsx +++ b/src/showcases/components/useStyleSheet/useStyleSheetSimpleUsage.component.tsx @@ -2,11 +2,11 @@ import React from 'react'; import { Layout, Text, - useStyleSheet, + StyleSheet, } from '@ui-kitten/components'; export const UseStyleSheetSimpleUsageShowcase = () => { - const styles = StyleSheet.create(); + const styles = useStyleSheet(); return ( @@ -17,7 +17,7 @@ export const UseStyleSheetSimpleUsageShowcase = () => { ); }; -const StyleSheet = useStyleSheet({ +const useStyleSheet = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', From f46b3bdf5fd75ff671d1ede4ffa60d399f629715 Mon Sep 17 00:00:00 2001 From: Artur Yorsh Date: Fri, 17 Jan 2020 16:29:05 +0300 Subject: [PATCH 2/3] refactor(components): useStyleSheet hook reimplemented --- docs/src/structure.ts | 2 +- .../theme/application/application.spec.tsx | 2 +- src/components/theme/index.ts | 16 +-- .../theme/style/evaStyleSheet.service.ts | 63 ---------- src/components/theme/style/style.service.tsx | 108 ++++++++++++++++++ src/components/theme/style/style.spec.tsx | 20 ++-- .../theme/style/styleConsumer.service.ts | 13 ++- .../theme/style/styleSheet.service.tsx | 29 ----- src/components/theme/style/styled.tsx | 15 ++- src/components/theme/style/type.ts | 13 --- src/components/theme/style/useStyleSheet.tsx | 36 ------ src/components/theme/theme/theme.service.tsx | 83 ++++++++++++++ src/components/theme/theme/theme.spec.tsx | 8 +- src/components/theme/theme/themeContext.ts | 2 +- .../theme/theme/themeProvider.component.tsx | 6 +- src/components/theme/theme/useTheme.tsx | 18 --- src/components/theme/theme/withStyles.tsx | 6 +- .../useStyleSheetSimpleUsage.component.tsx | 7 +- 18 files changed, 242 insertions(+), 205 deletions(-) delete mode 100644 src/components/theme/style/evaStyleSheet.service.ts create mode 100644 src/components/theme/style/style.service.tsx delete mode 100644 src/components/theme/style/styleSheet.service.tsx delete mode 100644 src/components/theme/style/type.ts delete mode 100644 src/components/theme/style/useStyleSheet.tsx create mode 100644 src/components/theme/theme/theme.service.tsx delete mode 100644 src/components/theme/theme/useTheme.tsx diff --git a/docs/src/structure.ts b/docs/src/structure.ts index 5a8ccae22..c803577a4 100644 --- a/docs/src/structure.ts +++ b/docs/src/structure.ts @@ -235,7 +235,7 @@ export const structure = [ icon: 'withStyles.svg', source: [ 'useTheme', - 'StyleSheet', + 'useStyleSheet', 'withStyles', ], }, diff --git a/src/components/theme/application/application.spec.tsx b/src/components/theme/application/application.spec.tsx index 00e3d36f2..1f46a0d58 100644 --- a/src/components/theme/application/application.spec.tsx +++ b/src/components/theme/application/application.spec.tsx @@ -14,12 +14,12 @@ import { ApplicationProvider, ApplicationProviderProps, } from './applicationProvider.component'; +import { ThemeType } from '../theme/theme.service'; import { mapping, theme, themeInverse, } from '../support/tests'; -import { ThemeType } from '../style/evaStyleSheet.service'; describe('@app: application wrapper check', () => { diff --git a/src/components/theme/index.ts b/src/components/theme/index.ts index 3cd270c8c..5769c3819 100644 --- a/src/components/theme/index.ts +++ b/src/components/theme/index.ts @@ -16,17 +16,16 @@ export { StyledComponentProps, StyledComponentClass, } from './style/styled'; -export { useStyleSheet } from './style/useStyleSheet'; -export { StyleSheet } from './style/styleSheet.service'; +export { + StyleService, + useStyleSheet, +} from './style/style.service'; export { StyleType, Styles, - ThemeType, -} from './style/evaStyleSheet.service'; -export { Interaction, State, -} from './style/type'; +} from './style/style.service'; export { ThemeProvider, ThemeProviderProps, @@ -36,4 +35,7 @@ export { ThemedComponentProps, ThemedComponentClass, } from './theme/withStyles'; -export { useTheme } from './theme/useTheme'; +export { + ThemeType, + useTheme, +} from './theme/theme.service'; diff --git a/src/components/theme/style/evaStyleSheet.service.ts b/src/components/theme/style/evaStyleSheet.service.ts deleted file mode 100644 index 2b68055b5..000000000 --- a/src/components/theme/style/evaStyleSheet.service.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { StyleSheet as RNStyleSheet } from 'react-native'; - -type ThemeValue = string; - -export type ThemeType = Record; -export type StyleType = Record; -export type Styles = RNStyleSheet.NamedStyles; - -export class EvaStyleSheet { - - static createThemedStyles = (styles: Styles, theme: ThemeType): T => { - return Object.keys(styles).reduce((acc: T, key: string): T => { - return { ...acc, [key]: EvaStyleSheet.createThemedStyle(styles[key], theme) }; - }, {} as T); - }; - - static createThemedStyle = (style: StyleType, theme: ThemeType): StyleType => { - return Object.keys(style).reduce((acc: StyleType, key: string): StyleType => { - const value: any = style[key]; - return { ...acc, [key]: EvaStyleSheet.getThemeValue(value, theme, value) }; - }, {}); - }; - - static createCompiledTheme = (theme: ThemeType): ThemeType => { - return Object.keys(theme).reduce((acc: ThemeType, key: string): ThemeType => { - return { ...acc, [key]: EvaStyleSheet.getThemeValue(key, theme, key) }; - }, {}); - }; - - static getThemeValue = (name: string, theme: ThemeType, fallback?: ThemeValue): ThemeValue | undefined => { - if (EvaStyleSheet.isReference(name)) { - const themeKey: string = EvaStyleSheet.toThemeKey(name); - return EvaStyleSheet.findThemeValue(themeKey, theme) || fallback; - } - - return EvaStyleSheet.findThemeValue(name, theme) || fallback; - }; - - static findThemeValue = (name: string, theme: ThemeType): ThemeValue | undefined => { - const value: ThemeValue = theme[name]; - - if (EvaStyleSheet.isReference(value)) { - const themeKey: string = EvaStyleSheet.toThemeKey(value); - return EvaStyleSheet.findThemeValue(themeKey, theme); - } - - return value; - }; - - /** - * @returns true if theme value references to another - */ - static isReference = (value: ThemeValue): boolean => { - return `${value}`.startsWith('$'); - }; - - /** - * Transforms reference key to theme key - */ - static toThemeKey = (value: ThemeValue): string => { - return `${value}`.substring(1); - }; -} diff --git a/src/components/theme/style/style.service.tsx b/src/components/theme/style/style.service.tsx new file mode 100644 index 000000000..6796792bf --- /dev/null +++ b/src/components/theme/style/style.service.tsx @@ -0,0 +1,108 @@ +/** + * @license + * Copyright Akveo. All Rights Reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + */ + +import { StyleSheet } from 'react-native'; +import { + ThemeService, + ThemeType, + useTheme, +} from '../theme/theme.service'; + + +export type StyleType = Record; +export type Styles = StyleSheet.NamedStyles; + +/** + * User interactions that can be handled by Eva. + */ +export enum Interaction { + HOVER = 'hover', + ACTIVE = 'active', + FOCUSED = 'focused', + INDETERMINATE = 'indeterminate', + VISIBLE = 'visible', +} + +/** + * Component states that can be handled by Eva. + */ +export enum State { + CHECKED = 'checked', + SELECTED = 'selected', + DISABLED = 'disabled', +} + +/** + * Takes a theme provided by ApplicationProvider or ThemeProvider and applies it to style. + * Consider not using this function when not using Eva theme variables. + * + * @overview-example UseStyleSheetSimpleUsage + */ +export const useStyleSheet = >(styles: Styles): T => { + const theme: ThemeType = useTheme(); + + return StyleService.createThemed(styles, theme); +}; + +/** + * Service for creating styles that fit current theme. + * Unlike StyleSheet class exported from React Native package, it allows using Eva theme variables. + */ +export class StyleService { + + /** + * Unlike `StyleSheet.create` from RN package, + * this does nothing with `styles` validation because of inability to process Eva theme variables + * and returns styles as it is just to support the syntax we used to. + * + * However, this may be useful to have this function + * because future RN versions may allow pre-processing. + * @see {StyleSheet.setStyleAttributePreprocessor} + * + * Notice it is better to use `StyleSheet.create` from RN package since it does style registering. + * You don't need to use this function if custom variables are not used. + * + * When using Eva theme variables, `useStyleSheet` should be called. + * + * @example + * ``` + * const Component = () => { + * const styles = useStyleSheet(themedStyles); + * return ( + * + * ); + * }; + * + * const themedStyles = StyleService.create({ + * container: { backgroundColor: 'color-primary-default' }, + * }); + * ``` + */ + static create = >(styles: T): T => { + return styles; + }; + + /** + * @returns stylesheet mapped to theme + */ + static createThemed = >(styles: Styles, theme: ThemeType): T => { + return Object.keys(styles).reduce((acc: T, key: string): T => { + return { ...acc, [key]: StyleService.createThemedEntry(styles[key], theme) }; + }, {} as T); + }; + + /** + * @returns a style mapped to theme + */ + static createThemedEntry = (style: StyleType, theme: ThemeType): StyleType => { + return Object.keys(style).reduce((acc: StyleType, key: string): StyleType => { + const value: any = style[key]; + return { ...acc, [key]: ThemeService.getValue(value, theme, value) }; + }, {}); + }; +} + + diff --git a/src/components/theme/style/style.spec.tsx b/src/components/theme/style/style.spec.tsx index 5c3fbf7d0..c2bce744c 100644 --- a/src/components/theme/style/style.spec.tsx +++ b/src/components/theme/style/style.spec.tsx @@ -21,11 +21,11 @@ import { StyledComponentProps, } from './styled'; import { StyleConsumerService } from './styleConsumer.service'; -import { Interaction } from './type'; import { - EvaStyleSheet, - ThemeType, -} from './evaStyleSheet.service'; + StyleService, + Interaction, +} from './style.service'; +import { ThemeType } from '../theme/theme.service'; import { styles, theme, @@ -134,27 +134,27 @@ describe('@style: consumer service methods check', () => { describe('@style-sheet: service checks', () => { it('finds theme value properly', async () => { - const themeValue = EvaStyleSheet.getThemeValue('gray-100', theme); - const undefinedValue = EvaStyleSheet.getThemeValue('undefined', theme); + const themeValue = StyleService.getThemeValue('gray-100', theme); + const undefinedValue = StyleService.getThemeValue('undefined', theme); expect(themeValue).toEqual(theme['gray-100']); expect(undefinedValue).toBeUndefined(); }); it('finds referencing theme value properly', async () => { - const themeValue = EvaStyleSheet.getThemeValue('referencing', theme); + const themeValue = StyleService.getThemeValue('referencing', theme); expect(themeValue).toEqual(theme['gray-100']); }); it('finds multiple referencing theme value properly', async () => { - const themeValue = EvaStyleSheet.getThemeValue('double-referencing', theme); + const themeValue = StyleService.getThemeValue('double-referencing', theme); expect(themeValue).toEqual(theme['gray-100']); }); it('finds referencing theme value properly (initial reference)', async () => { - const themeValue = EvaStyleSheet.getThemeValue('referencing', theme); + const themeValue = StyleService.getThemeValue('referencing', theme); expect(themeValue).toEqual(theme['gray-100']); }); @@ -167,7 +167,7 @@ describe('@style-sheet: service checks', () => { prop4: 42, }; - const value = EvaStyleSheet.createThemedStyle(mapping as ViewStyle, theme); + const value = StyleService.createThemedEntry(mapping as ViewStyle, theme); expect(value).toMatchSnapshot(); }); diff --git a/src/components/theme/style/styleConsumer.service.ts b/src/components/theme/style/styleConsumer.service.ts index f7f20bb52..1f3a10565 100644 --- a/src/components/theme/style/styleConsumer.service.ts +++ b/src/components/theme/style/styleConsumer.service.ts @@ -11,11 +11,11 @@ import { } from '@eva-design/dss'; import { StyledComponentProps } from './styled'; import { - EvaStyleSheet, + Interaction, + StyleService, StyleType, - ThemeType, -} from './evaStyleSheet.service'; -import { Interaction } from './type'; +} from './style.service'; +import { ThemeType } from '../theme/theme.service'; const SEPARATOR_MAPPING_ENTRY: string = '.'; @@ -84,7 +84,7 @@ export class StyleConsumerService { } const mapping: StyleType = this.withValidParameters(generatedMapping); - const themedStyle: StyleType = EvaStyleSheet.createThemedStyle(mapping, theme); + const themedStyle: StyleType = StyleService.createThemedEntry(mapping, theme); return { ...source, theme, themedStyle }; } @@ -125,7 +125,8 @@ export class StyleConsumerService { return mapping; } - private getStyleInfo

(props: P, interaction: Interaction[]): StyleInfo { + private getStyleInfo

(props: P, + interaction: Interaction[]): StyleInfo { const variantProps: Partial

= this.getDerivedVariants(this.meta, props); const stateProps: Partial

= this.getDerivedStates(this.meta, props); diff --git a/src/components/theme/style/styleSheet.service.tsx b/src/components/theme/style/styleSheet.service.tsx deleted file mode 100644 index b6e78a1e0..000000000 --- a/src/components/theme/style/styleSheet.service.tsx +++ /dev/null @@ -1,29 +0,0 @@ -/** - * @license - * Copyright Akveo. All Rights Reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - */ - -import { EvaStyleSheet, Styles, ThemeType } from './evaStyleSheet.service'; -import { useTheme } from '../theme/useTheme'; - -/** - * Service for creating styles that fit current theme. - * Unlike StyleSheet class exported from React Native package, it allows using Eva theme variables. - * - * @overview-example UseStyleSheetSimpleUsage - */ -export class StyleSheet { - - /** - * @returns a hook function that returns style mapped to current theme from the given object. - */ - static create = >(styles: Styles): () => T => { - return (): T => { - // eslint-disable-next-line react-hooks/rules-of-hooks - const theme: ThemeType = useTheme(); - return EvaStyleSheet.createThemedStyles(styles, theme); - }; - }; -} - diff --git a/src/components/theme/style/styled.tsx b/src/components/theme/style/styled.tsx index cfb3ddcf2..a51fc4b99 100644 --- a/src/components/theme/style/styled.tsx +++ b/src/components/theme/style/styled.tsx @@ -9,12 +9,12 @@ import hoistNonReactStatics from 'hoist-non-react-statics'; import { ThemeStyleType } from '@eva-design/dss'; import { StyleConsumerService } from './styleConsumer.service'; import { + Interaction, StyleType, - ThemeType, -} from './evaStyleSheet.service'; -import { Interaction } from './type'; +} from './style.service'; import { MappingContext } from '../mapping/mappingContext'; import { ThemeContext } from '../theme/themeContext'; +import { ThemeType } from '../theme/theme.service'; interface PrivateProps { forwardedRef?: React.Ref; @@ -96,7 +96,9 @@ export const styled =

(Component: React.ComponentType

): Sty this.setState({ interaction }); }; - private withStyledProps = (source: P, style: ThemeStyleType, theme: ThemeType): WrappedProps => { + private withStyledProps = (source: P, + style: ThemeStyleType, + theme: ThemeType): WrappedProps => { const { interaction } = this.state; const props: WrappingProps = { ...this.defaultProps, ...source }; @@ -125,14 +127,15 @@ export const styled =

(Component: React.ComponentType

): Sty return ( {(style: ThemeStyleType): WrappedElement => ( {(theme: ThemeType): WrappedElement => { - return this.renderWrappedElement(style, theme); + return this.renderWrappedElement(style, theme); }} )} ); } } - const WrappingElement = (props: WrappingProps, ref: React.Ref): WrappingElement => { + const WrappingElement = (props: WrappingProps, + ref: React.Ref): WrappingElement => { return ( // @ts-ignore diff --git a/src/components/theme/style/type.ts b/src/components/theme/style/type.ts deleted file mode 100644 index 37c07c87b..000000000 --- a/src/components/theme/style/type.ts +++ /dev/null @@ -1,13 +0,0 @@ -export enum Interaction { - HOVER = 'hover', - ACTIVE = 'active', - FOCUSED = 'focused', - INDETERMINATE = 'indeterminate', - VISIBLE = 'visible', -} - -export enum State { - CHECKED = 'checked', - SELECTED = 'selected', - DISABLED = 'disabled', -} diff --git a/src/components/theme/style/useStyleSheet.tsx b/src/components/theme/style/useStyleSheet.tsx deleted file mode 100644 index 876d20fcb..000000000 --- a/src/components/theme/style/useStyleSheet.tsx +++ /dev/null @@ -1,36 +0,0 @@ -/** - * @license - * Copyright Akveo. All Rights Reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - */ - -import { - Styles, - EvaStyleSheet, - ThemeType, -} from './evaStyleSheet.service'; -import { useTheme } from '../theme/useTheme'; - -/** - * @deprecated since version 4.4.0-beta.3. Use `StyleSheet.create` from this package instead. - * - * Takes a theme provided by ApplicationProvider or ThemeProvider and applies it to style. - */ -export const useStyleSheet = >(styles: Styles) => { - const docRoot: string = 'https://akveo.github.io/react-native-ui-kitten/docs'; - - const message: string = [ - 'useStyleSheet is deprecated and will be removed in a stable version', - 'Consider migrating to a new syntax.', - `📖 Documentation: ${docRoot}/components/themed-component/overview#stylesheet`, - ].join('\n'); - - console.warn(message); - - return { - create: (): T => { - const theme: ThemeType = useTheme(); - return EvaStyleSheet.createThemedStyles(styles, theme); - }, - }; -}; diff --git a/src/components/theme/theme/theme.service.tsx b/src/components/theme/theme/theme.service.tsx new file mode 100644 index 000000000..8018a59b8 --- /dev/null +++ b/src/components/theme/theme/theme.service.tsx @@ -0,0 +1,83 @@ +/** + * @license + * Copyright Akveo. All Rights Reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + */ + +import React from 'react'; +import { ThemeContext } from './themeContext'; + +export type ThemeValue = string; +export type ThemeType = Record; + +/** + * Takes an actual theme provided by ApplicationProvider or ThemeProvider and + * @returns it to a functional component. + * + * @overview-example UseThemeSimpleUsage + */ +export const useTheme = (): ThemeType => { + return React.useContext(ThemeContext); +}; + +/** + * Service for working with Eva themes + */ +export class ThemeService { + + /** + * @returns compiled theme since Eva theme may contain variables referencing each other. + */ + static create = (theme: ThemeType): ThemeType => { + return Object.keys(theme).reduce((acc: ThemeType, key: string): ThemeType => { + return { ...acc, [key]: ThemeService.getValue(key, theme, key) }; + }, {}); + }; + + /** + * Finds theme value recursively since eva theme variables can reference each other. + * + * @returns ThemeValue if found, fallback param otherwise. + */ + static getValue = (name: string, + theme: ThemeType, + fallback?: ThemeValue): ThemeValue | undefined => { + + if (ThemeService.isReference(name)) { + const themeKey: string = ThemeService.createKeyFromReference(name); + return ThemeService.findValue(themeKey, theme) || fallback; + } + + return ThemeService.findValue(name, theme) || fallback; + }; + + /** + * Finds theme value recursively since eva theme variables can reference each other. + * + * @returns ThemeValue if found. + */ + private static findValue = (name: string, theme: ThemeType): ThemeValue | undefined => { + const value: ThemeValue = theme[name]; + + if (ThemeService.isReference(value)) { + const themeKey: string = ThemeService.createKeyFromReference(value); + return ThemeService.findValue(themeKey, theme); + } + + return value; + }; + + /** + * @returns true if theme value references to another + */ + private static isReference = (value: ThemeValue): boolean => { + return `${value}`.startsWith('$'); + }; + + /** + * Transforms reference key to theme key + */ + private static createKeyFromReference = (value: ThemeValue): string => { + return `${value}`.substring(1); + }; +} diff --git a/src/components/theme/theme/theme.spec.tsx b/src/components/theme/theme/theme.spec.tsx index 0840bc12d..20475afd2 100644 --- a/src/components/theme/theme/theme.spec.tsx +++ b/src/components/theme/theme/theme.spec.tsx @@ -20,9 +20,9 @@ import { withStyles, } from './withStyles'; import { - EvaStyleSheet, + ThemeService, ThemeType, -} from '../style/evaStyleSheet.service'; +} from './theme.service'; import { theme, themeInverse, @@ -169,8 +169,8 @@ describe('@theme: ui component checks', () => { const { theme: theme1 } = themedComponents[0].props; const { theme: theme2 } = themedComponents[1].props; - expect(theme1).toEqual(EvaStyleSheet.createCompiledTheme(theme)); - expect(theme2).toEqual(EvaStyleSheet.createCompiledTheme(themeInverse)); + expect(theme1).toEqual(ThemeService.create(theme)); + expect(theme2).toEqual(ThemeService.create(themeInverse)); }); }); diff --git a/src/components/theme/theme/themeContext.ts b/src/components/theme/theme/themeContext.ts index 3937d4568..60e63bc73 100644 --- a/src/components/theme/theme/themeContext.ts +++ b/src/components/theme/theme/themeContext.ts @@ -5,7 +5,7 @@ */ import React from 'react'; -import { ThemeType } from '../style/evaStyleSheet.service'; +import { ThemeType } from './theme.service'; const defaultTheme: ThemeType = {}; diff --git a/src/components/theme/theme/themeProvider.component.tsx b/src/components/theme/theme/themeProvider.component.tsx index 839cd07a9..ebcf073f6 100644 --- a/src/components/theme/theme/themeProvider.component.tsx +++ b/src/components/theme/theme/themeProvider.component.tsx @@ -7,9 +7,9 @@ import React from 'react'; import { ThemeContext } from './themeContext'; import { - EvaStyleSheet, + ThemeService, ThemeType, -} from '../style/evaStyleSheet.service'; +} from './theme.service'; export interface ThemeProviderProps { theme: ThemeType; @@ -32,7 +32,7 @@ export class ThemeProvider extends React.PureComponent { return ( + value={ThemeService.create(theme)}> {children} ); diff --git a/src/components/theme/theme/useTheme.tsx b/src/components/theme/theme/useTheme.tsx deleted file mode 100644 index e6c0a014e..000000000 --- a/src/components/theme/theme/useTheme.tsx +++ /dev/null @@ -1,18 +0,0 @@ -/** - * @license - * Copyright Akveo. All Rights Reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - */ - -import React from 'react'; -import { ThemeContext } from './themeContext'; -import { ThemeType } from '../style/evaStyleSheet.service'; - -/** - * Takes an actual theme provided by ApplicationProvider or ThemeProvider and returns it to a functional component. - * - * @overview-example UseThemeSimpleUsage - */ -export const useTheme = (): ThemeType => { - return React.useContext(ThemeContext); -}; diff --git a/src/components/theme/theme/withStyles.tsx b/src/components/theme/theme/withStyles.tsx index 9bd0e4eda..250020d1f 100644 --- a/src/components/theme/theme/withStyles.tsx +++ b/src/components/theme/theme/withStyles.tsx @@ -7,10 +7,8 @@ import React from 'react'; import hoistNonReactStatics from 'hoist-non-react-statics'; import { ThemeContext } from './themeContext'; -import { - Styles, - ThemeType, -} from '../style/evaStyleSheet.service'; +import { ThemeType } from './theme.service'; +import { Styles } from '../style/style.service'; interface PrivateProps { forwardedRef?: React.RefObject; diff --git a/src/showcases/components/useStyleSheet/useStyleSheetSimpleUsage.component.tsx b/src/showcases/components/useStyleSheet/useStyleSheetSimpleUsage.component.tsx index 217995e15..b09b970f4 100644 --- a/src/showcases/components/useStyleSheet/useStyleSheetSimpleUsage.component.tsx +++ b/src/showcases/components/useStyleSheet/useStyleSheetSimpleUsage.component.tsx @@ -2,11 +2,12 @@ import React from 'react'; import { Layout, Text, - StyleSheet, + StyleService, + useStyleSheet, } from '@ui-kitten/components'; export const UseStyleSheetSimpleUsageShowcase = () => { - const styles = useStyleSheet(); + const styles = useStyleSheet(themedStyles); return ( @@ -17,7 +18,7 @@ export const UseStyleSheetSimpleUsageShowcase = () => { ); }; -const useStyleSheet = StyleSheet.create({ +const themedStyles = StyleService.create({ container: { flex: 1, justifyContent: 'center', From ed2e020d01742035bcbc19da5ab648bda2d86396 Mon Sep 17 00:00:00 2001 From: Artur Yorsh Date: Fri, 17 Jan 2020 17:00:12 +0300 Subject: [PATCH 3/3] test(components): fix style spec --- src/components/theme/style/style.spec.tsx | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/components/theme/style/style.spec.tsx b/src/components/theme/style/style.spec.tsx index c2bce744c..d8eebbd0c 100644 --- a/src/components/theme/style/style.spec.tsx +++ b/src/components/theme/style/style.spec.tsx @@ -25,7 +25,10 @@ import { StyleService, Interaction, } from './style.service'; -import { ThemeType } from '../theme/theme.service'; +import { + ThemeService, + ThemeType, +} from '../theme/theme.service'; import { styles, theme, @@ -134,27 +137,27 @@ describe('@style: consumer service methods check', () => { describe('@style-sheet: service checks', () => { it('finds theme value properly', async () => { - const themeValue = StyleService.getThemeValue('gray-100', theme); - const undefinedValue = StyleService.getThemeValue('undefined', theme); + const themeValue = ThemeService.getValue('gray-100', theme); + const undefinedValue = ThemeService.getValue('undefined', theme); expect(themeValue).toEqual(theme['gray-100']); expect(undefinedValue).toBeUndefined(); }); it('finds referencing theme value properly', async () => { - const themeValue = StyleService.getThemeValue('referencing', theme); + const themeValue = ThemeService.getValue('referencing', theme); expect(themeValue).toEqual(theme['gray-100']); }); it('finds multiple referencing theme value properly', async () => { - const themeValue = StyleService.getThemeValue('double-referencing', theme); + const themeValue = ThemeService.getValue('double-referencing', theme); expect(themeValue).toEqual(theme['gray-100']); }); it('finds referencing theme value properly (initial reference)', async () => { - const themeValue = StyleService.getThemeValue('referencing', theme); + const themeValue = ThemeService.getValue('referencing', theme); expect(themeValue).toEqual(theme['gray-100']); });