From 67a8f8f0c5e2c930020562248b50854348481d49 Mon Sep 17 00:00:00 2001 From: Artur Yorsh <10753921+artyorsh@users.noreply.github.com> Date: Fri, 20 Dec 2019 15:28:32 +0300 Subject: [PATCH] feat(components): add theming hooks --- docs/src/structure.ts | 4 +- .../theme/application/application.spec.tsx | 2 +- .../applicationProvider.component.tsx | 3 +- src/components/theme/index.ts | 39 +-- src/components/theme/style/style.service.ts | 18 -- src/components/theme/style/style.spec.tsx | 64 +++-- .../theme/style/style.spec.tsx.snap | 18 +- .../theme/style/styleConsumer.service.ts | 35 ++- .../theme/style/styleSheet.service.ts | 63 +++++ ...styleConsumer.component.tsx => styled.tsx} | 101 ++------ src/components/theme/style/type.ts | 4 - src/components/theme/style/useStyleSheet.tsx | 20 ++ src/components/theme/theme/theme.service.ts | 52 ---- src/components/theme/theme/theme.spec.tsx | 48 +--- .../theme/theme/themeConsumer.component.tsx | 158 ------------ src/components/theme/theme/themeContext.ts | 6 +- .../theme/theme/themeProvider.component.tsx | 16 +- src/components/theme/theme/type.ts | 5 - src/components/theme/theme/useTheme.tsx | 12 + src/components/theme/theme/withStyles.tsx | 101 ++++++++ src/components/ui/radio/radio.spec.tsx.snap | 226 +++++++++--------- src/playground/env/index.js | 4 +- src/playground/src/app/app.component.tsx | 6 +- src/playground/src/app/mapping.json | 71 ++++++ .../src/components/safeAreaLayout.tsx | 17 +- .../src/components/showcase.component.tsx | 51 ++-- .../showcaseContainer.component.tsx | 2 +- .../src/components/showcaseItem.component.tsx | 23 +- .../components/showcaseSection.component.tsx | 22 +- .../components/showcaseSettings.component.tsx | 22 +- .../styledComponentSimpleUsage.component.tsx | 73 ++++++ .../styledComponentStates.component.tsx | 101 ++++++++ .../styledComponentVariants.component.tsx | 126 ++++++++++ .../themeProviderSimpleUsage.component.tsx | 27 +++ .../useStyleSheetSimpleUsage.component.tsx | 27 +++ .../useThemeSimpleUsage.component.tsx | 27 +++ .../withStylesSimpleUsage.component.tsx | 26 ++ src/playground/src/icons/index.tsx | 15 +- src/playground/src/icons/materialIconPack.tsx | 3 +- .../src/navigation/home.navigator.tsx | 2 + .../src/navigation/services.navigator.tsx | 18 ++ .../components/components.component.tsx | 3 +- .../src/scenes/home/homeTabBar.component.tsx | 7 +- .../src/scenes/samples/samples.component.tsx | 3 +- .../scenes/services/services.component.tsx | 77 ++++++ .../src/scenes/styled/styled.component.tsx | 24 ++ .../styledComponentShowcase.component.tsx | 52 ++++ src/playground/src/scenes/styled/type.ts | 50 ++++ .../themeProvider/themeProvider.component.tsx | 71 ++++++ .../useStyleSheet/useStyleSheet.component.tsx | 25 ++ .../scenes/useTheme/useTheme.component.tsx | 31 +++ .../withStyles/withStyles.component.tsx | 54 +++++ 52 files changed, 1402 insertions(+), 653 deletions(-) delete mode 100644 src/components/theme/style/style.service.ts create mode 100644 src/components/theme/style/styleSheet.service.ts rename src/components/theme/style/{styleConsumer.component.tsx => styled.tsx} (63%) create mode 100644 src/components/theme/style/useStyleSheet.tsx delete mode 100644 src/components/theme/theme/theme.service.ts delete mode 100644 src/components/theme/theme/themeConsumer.component.tsx delete mode 100644 src/components/theme/theme/type.ts create mode 100644 src/components/theme/theme/useTheme.tsx create mode 100644 src/components/theme/theme/withStyles.tsx create mode 100644 src/playground/src/app/mapping.json create mode 100644 src/playground/src/components/showcases/styled/styledComponentSimpleUsage.component.tsx create mode 100644 src/playground/src/components/showcases/styled/styledComponentStates.component.tsx create mode 100644 src/playground/src/components/showcases/styled/styledComponentVariants.component.tsx create mode 100644 src/playground/src/components/showcases/themeProvider/themeProviderSimpleUsage.component.tsx create mode 100644 src/playground/src/components/showcases/useStyleSheet/useStyleSheetSimpleUsage.component.tsx create mode 100644 src/playground/src/components/showcases/useTheme/useThemeSimpleUsage.component.tsx create mode 100644 src/playground/src/components/showcases/withStyles/withStylesSimpleUsage.component.tsx create mode 100644 src/playground/src/navigation/services.navigator.tsx create mode 100644 src/playground/src/scenes/services/services.component.tsx create mode 100644 src/playground/src/scenes/styled/styled.component.tsx create mode 100644 src/playground/src/scenes/styled/styledComponentShowcase.component.tsx create mode 100644 src/playground/src/scenes/styled/type.ts create mode 100644 src/playground/src/scenes/themeProvider/themeProvider.component.tsx create mode 100644 src/playground/src/scenes/useStyleSheet/useStyleSheet.component.tsx create mode 100644 src/playground/src/scenes/useTheme/useTheme.component.tsx create mode 100644 src/playground/src/scenes/withStyles/withStyles.component.tsx diff --git a/docs/src/structure.ts b/docs/src/structure.ts index df883c3a6..c803577a4 100644 --- a/docs/src/structure.ts +++ b/docs/src/structure.ts @@ -232,8 +232,10 @@ export const structure = [ { type: 'tabs', name: 'Themed Component', - icon: 'with-styles.svg', + icon: 'withStyles.svg', source: [ + 'useTheme', + 'useStyleSheet', 'withStyles', ], }, diff --git a/src/components/theme/application/application.spec.tsx b/src/components/theme/application/application.spec.tsx index 4c486e7f0..7f0a0d9ab 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 '@kitten/theme'; +import { ThemeType } from '../style/styleSheet.service'; describe('@app: application wrapper check', () => { diff --git a/src/components/theme/application/applicationProvider.component.tsx b/src/components/theme/application/applicationProvider.component.tsx index db4c83b51..3f64c8f52 100644 --- a/src/components/theme/application/applicationProvider.component.tsx +++ b/src/components/theme/application/applicationProvider.component.tsx @@ -15,7 +15,6 @@ import { import { StyleProvider } from '../style/styleProvider.component'; import { ThemeProviderProps } from '../theme/themeProvider.component'; import { ModalPanel } from '../modal/modalPanel.component'; -import { ThemeType } from '../theme/type'; interface ComponentProps { mapping: SchemaType; @@ -78,7 +77,7 @@ export class ApplicationProvider extends React.Component { - const mappingValue: any = mapping[current]; - - return { ...acc, [current]: getThemeValue(mappingValue, theme, mappingValue) }; - }, {}); -} diff --git a/src/components/theme/style/style.spec.tsx b/src/components/theme/style/style.spec.tsx index 7723a8a98..ee675abcd 100644 --- a/src/components/theme/style/style.spec.tsx +++ b/src/components/theme/style/style.spec.tsx @@ -3,6 +3,7 @@ import { TouchableOpacity, View, ViewProps, + ViewStyle, } from 'react-native'; import { fireEvent, @@ -18,18 +19,13 @@ import { import { styled, StyledComponentProps, - ContextProps, -} from './styleConsumer.component'; +} from './styled'; import { StyleConsumerService } from './styleConsumer.service'; -import { createThemedStyle } from './style.service'; +import { Interaction } from './type'; import { - Interaction, - StyleType, -} from './type'; -import { - ThemedStyleType, + StyleSheet, ThemeType, -} from '../theme/type'; +} from './styleSheet.service'; import { styles, theme, @@ -98,12 +94,8 @@ const json = (value: any): string => JSON.stringify(value); describe('@style: consumer service methods check', () => { - const context: ContextProps = { - style: styles, - theme: theme, - }; - - const service: StyleConsumerService = new StyleConsumerService('Radio', context); + // @ts-ignore + const service: StyleConsumerService = new StyleConsumerService('Radio', styles); describe('* style mapping', () => { @@ -129,7 +121,8 @@ describe('@style: consumer service methods check', () => { ...derivedProps, }; - const value: StyledComponentProps = service.withStyledProps(props, context, [Interaction.ACTIVE]); + // @ts-ignore + const value: StyledComponentProps = service.withStyledProps(props, styles, theme, [Interaction.ACTIVE]); expect(value.themedStyle).toMatchSnapshot(); }); @@ -138,23 +131,44 @@ describe('@style: consumer service methods check', () => { }); -describe('@style: service methods checks', () => { +describe('@style-sheet: service checks', () => { + + it('finds theme value properly', async () => { + const themeValue = StyleSheet.getThemeValue('gray-100', theme); + const undefinedValue = StyleSheet.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); + + expect(themeValue).toEqual(theme['gray-100']); + }); - describe('* styling', () => { + it('finds multiple referencing theme value properly', async () => { + const themeValue = StyleSheet.getThemeValue('double-referencing', theme); - const mapping: ThemedStyleType = { + expect(themeValue).toEqual(theme['gray-100']); + }); + + it('finds referencing theme value properly (initial reference)', async () => { + const themeValue = StyleSheet.getThemeValue('referencing', theme); + + expect(themeValue).toEqual(theme['gray-100']); + }); + + it('* creates themedStyle property for mapping properly', () => { + const mapping = { prop1: 'blue-primary', prop2: 'blue-dark', prop3: 'gray-primary', prop4: 42, }; - it('* default theme', () => { - const value: StyleType = createThemedStyle(mapping, theme); - - expect(value).toMatchSnapshot(); - }); - + const value = StyleSheet.createThemedStyle(mapping as ViewStyle, theme); + expect(value).toMatchSnapshot(); }); }); diff --git a/src/components/theme/style/style.spec.tsx.snap b/src/components/theme/style/style.spec.tsx.snap index f981549e9..e4737b3e2 100644 --- a/src/components/theme/style/style.spec.tsx.snap +++ b/src/components/theme/style/style.spec.tsx.snap @@ -22,15 +22,6 @@ Object { } `; -exports[`@style: service methods checks * styling * default theme 1`] = ` -Object { - "prop1": "#3366FF", - "prop2": "#2541CC", - "prop3": "#A6AEBD", - "prop4": 42, -} -`; - exports[`@style: ui component checks * default appearance styled properly 1`] = ` Object { "borderColor": "#A6AEBD", @@ -140,3 +131,12 @@ Object { "width": 36, } `; + +exports[`@style-sheet: service checks * creates themedStyle property for mapping properly 1`] = ` +Object { + "prop1": "#3366FF", + "prop2": "#2541CC", + "prop3": "#A6AEBD", + "prop4": 42, +} +`; diff --git a/src/components/theme/style/styleConsumer.service.ts b/src/components/theme/style/styleConsumer.service.ts index 4307308c0..bdc0eb1bb 100644 --- a/src/components/theme/style/styleConsumer.service.ts +++ b/src/components/theme/style/styleConsumer.service.ts @@ -8,13 +8,13 @@ import { ControlMetaType, ControlThemedStyleType, ThemedStyleType, - ThemeStyleType, } from '@eva-design/dss'; +import { StyledComponentProps } from './styled'; import { - ContextProps, - StyledComponentProps, -} from './styleConsumer.component'; -import { createThemedStyle } from './style.service'; + StyleSheet, + StyleType, + ThemeType, +} from './styleSheet.service'; import { Interaction } from './type'; const SEPARATOR_MAPPING_ENTRY: string = '.'; @@ -30,10 +30,10 @@ export class StyleConsumerService { private readonly name: string; private readonly meta: ControlMetaType; - constructor(name: string, context: ContextProps) { + constructor(name: string, style: StyleType) { this.name = name; - this.meta = this.safe(context.style[name], (generatedConfig): ControlMetaType => { + this.meta = this.safe(style[name], (generatedConfig): ControlMetaType => { return generatedConfig.meta; }); @@ -61,12 +61,13 @@ export class StyleConsumerService { } public withStyledProps

(source: P, - context: ContextProps, + style: StyleType, + theme: ThemeType, interaction: Interaction[]): P & StyledComponentProps { const styleInfo: StyleInfo = this.getStyleInfo(source, interaction); - const generatedMapping: ThemedStyleType = this.getGeneratedStyleMapping(context.style, styleInfo); + const generatedMapping: StyleType = this.getGeneratedStyleMapping(style, styleInfo); if (!generatedMapping) { const docRoot: string = 'https://akveo.github.io/react-native-ui-kitten/docs'; @@ -79,20 +80,16 @@ export class StyleConsumerService { console.warn(message); - return this.withStyledProps({ ...source, ...this.createDefaultProps() }, context, interaction); + return this.withStyledProps({ ...source, ...this.createDefaultProps() }, style, theme, interaction); } - const mapping = this.withValidParameters(generatedMapping); + const mapping: StyleType = this.withValidParameters(generatedMapping); + const themedStyle: StyleType = StyleSheet.createThemedStyle(mapping, theme); - return { - ...source, - theme: context.theme, - themedStyle: createThemedStyle(mapping, context.theme), - }; + return { ...source, theme, themedStyle }; } - private getGeneratedStyleMapping

(style: ThemeStyleType, - info: StyleInfo): ThemedStyleType { + private getGeneratedStyleMapping

(style: StyleType, info: StyleInfo): StyleType { return this.safe(style[this.name], (componentStyles: ControlThemedStyleType): ThemedStyleType => { const styleKeys: string[] = Object.keys(componentStyles.styles); @@ -102,7 +99,7 @@ export class StyleConsumerService { }); } - private withValidParameters(mapping: ThemedStyleType): ThemedStyleType { + private withValidParameters(mapping: StyleType): StyleType { const invalidParameters: string[] = []; Object.keys(mapping).forEach((key: string) => { diff --git a/src/components/theme/style/styleSheet.service.ts b/src/components/theme/style/styleSheet.service.ts new file mode 100644 index 000000000..6e4be5e1e --- /dev/null +++ b/src/components/theme/style/styleSheet.service.ts @@ -0,0 +1,63 @@ +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 StyleSheet { + + 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) }; + }, {} 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) }; + }, {}); + }; + + static createCompiledTheme = (theme: ThemeType): ThemeType => { + return Object.keys(theme).reduce((acc: ThemeType, key: string): ThemeType => { + return { ...acc, [key]: StyleSheet.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; + } + + return StyleSheet.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); + } + + 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/styleConsumer.component.tsx b/src/components/theme/style/styled.tsx similarity index 63% rename from src/components/theme/style/styleConsumer.component.tsx rename to src/components/theme/style/styled.tsx index 498e4885a..34a78cd6b 100644 --- a/src/components/theme/style/styleConsumer.component.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, -} from './type'; + ThemeType, +} from './styleSheet.service'; +import { Interaction } from './type'; import { MappingContext } from '../mapping/mappingContext'; import { ThemeContext } from '../theme/themeContext'; -import { ThemeType } from '../theme/type'; interface PrivateProps { forwardedRef?: React.Ref; @@ -31,11 +31,6 @@ interface State { interaction: Interaction[]; } -export interface ContextProps { - style: ThemeStyleType; - theme: ThemeType; -} - export type StyledComponentClass

= React.ComponentClass; /** @@ -57,68 +52,11 @@ export type StyledComponentClass

= React.ComponentClass { - * // Request styles for `active` state and re-render - * - * this.props.dispatch([Interaction.ACTIVE]); - * - * if(this.props.onPressIn) { - * this.props.onPressIn(e); - * } - * }; - * - * onPressOut = (e) => { - * // Request styles for default state and re-render + * @overview-example StyledComponentSimpleUsage * - * this.props.dispatch([]); + * @overview-example StyledComponentStates * - * if(this.props.onPressOut) { - * this.props.onPressOut(e); - * } - * }; - * - * render() { - * // Retrieve styles for current state from props (provided with themedStyle prop) - * // And apply it with saving priority of `style` prop - * - * const { style, themedStyle, ...restProps } = this.props; - * - * return ( - * - * ); - * } - * } - * - * export const StyledButton = styled(Button); - * ``` - * - * @overview-example Styled Component Usage - * - * ``` - * import React from 'react'; - * import { StyledButton } from './path-to/styledButton.component'; - * - * export const StyledButtonShowcase = (props) => ( - * - * ); - * ``` + * @overview-example StyledComponentVariants */ export const styled =

(Component: React.ComponentType

): StyledComponentClass

=> { @@ -146,34 +84,33 @@ export const styled =

(Component: React.ComponentType

): Sty private defaultProps: StyledComponentProps; private service: StyleConsumerService; - private onInit = (context: ContextProps) => { - + private onInit = (style: ThemeStyleType, theme: ThemeType): void => { // @ts-ignore - this.service = new StyleConsumerService(Component.styledComponentName, context); + this.service = new StyleConsumerService(Component.styledComponentName, style, theme); this.defaultProps = this.service.createDefaultProps(); this.init = true; }; - private onDispatch = (interaction: Interaction[]) => { + private onDispatch = (interaction: Interaction[]): void => { this.setState({ interaction }); }; - private withStyledProps = (source: P, context: ContextProps): WrappedProps => { + private withStyledProps = (source: P, style: ThemeStyleType, theme: ThemeType): WrappedProps => { const { interaction } = this.state; const props: WrappingProps = { ...this.defaultProps, ...source }; - return this.service.withStyledProps(props, context, interaction); + return this.service.withStyledProps(props, style, theme, interaction); }; - private renderWrappedElement = (context: ContextProps): WrappedElement => { + private renderWrappedElement = (style: ThemeStyleType, theme: ThemeType): WrappedElement => { if (!this.init) { - this.onInit(context); + this.onInit(style, theme); } const { forwardedRef, ...restProps } = this.props; - const props: P & StyledComponentProps = this.withStyledProps(restProps as P, context); + const props: P & StyledComponentProps = this.withStyledProps(restProps as P, style, theme); return ( (Component: React.ComponentType

): Sty }; public render(): React.ReactNode { - const StyledElement = this.renderWrappedElement; - return ( - {(styles: ThemeStyleType): WrappedElement => ( - {(theme: ThemeType): WrappedElement => ( - - )} + {(style: ThemeStyleType): WrappedElement => ( + {(theme: ThemeType): WrappedElement => { + return this.renderWrappedElement(style, theme); + }} )} ); } diff --git a/src/components/theme/style/type.ts b/src/components/theme/style/type.ts index b79ff3ab3..37c07c87b 100644 --- a/src/components/theme/style/type.ts +++ b/src/components/theme/style/type.ts @@ -1,7 +1,3 @@ -export interface StyleType { - [key: string]: any; -} - export enum Interaction { HOVER = 'hover', ACTIVE = 'active', diff --git a/src/components/theme/style/useStyleSheet.tsx b/src/components/theme/style/useStyleSheet.tsx new file mode 100644 index 000000000..96a534b26 --- /dev/null +++ b/src/components/theme/style/useStyleSheet.tsx @@ -0,0 +1,20 @@ +import { + Styles, + StyleSheet, + ThemeType, +} from '../style/styleSheet.service'; +import { useTheme } from '../theme/useTheme'; + +/** + * Takes a theme provided by ApplicationProvider or ThemeProvider and applies it to style. + * + * @overview-example UseStyleSheetSimpleUsage + */ +export const useStyleSheet = >(styles: Styles) => { + return { + create: (): T => { + const theme: ThemeType = useTheme(); + return StyleSheet.createThemedStyles(styles, theme); + }, + }; +}; diff --git a/src/components/theme/theme/theme.service.ts b/src/components/theme/theme/theme.service.ts deleted file mode 100644 index 80dfdb910..000000000 --- a/src/components/theme/theme/theme.service.ts +++ /dev/null @@ -1,52 +0,0 @@ -/** - * @license - * Copyright Akveo. All Rights Reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - */ - -import { ThemeType } from './type'; - -const SYMBOL_REFERENCE: string = '$'; - -/** - * @param name: string - theme property name, like `backgroundColor` - * @param theme: ThemeType - theme - * @param fallback: any - fallback value - * - * @return any. Theme property value if it presents in theme, fallback otherwise - */ -export function getThemeValue(name: string, theme: ThemeType, fallback?: any): any | undefined { - if (isReferenceKey(name)) { - const themeKey: string = toThemeKey(name); - - return findThemeValue(themeKey, theme) || fallback; - } - - return findThemeValue(name, theme) || fallback; -} - -function findThemeValue(name: string, theme: ThemeType): any | undefined { - const value: any = theme[name]; - - if (isReferenceKey(value)) { - const themeKey: string = toThemeKey(value); - - return findThemeValue(themeKey, theme); - } - - return value; -} - -/** - * @returns true if theme value references to another - */ -function isReferenceKey(value: any): boolean { - return `${value}`.startsWith(SYMBOL_REFERENCE); -} - -/** - * Transforms reference key to theme key - */ -function toThemeKey(value: any): string { - return `${value}`.substring(1); -} diff --git a/src/components/theme/theme/theme.spec.tsx b/src/components/theme/theme/theme.spec.tsx index ad07260b5..828151bf3 100644 --- a/src/components/theme/theme/theme.spec.tsx +++ b/src/components/theme/theme/theme.spec.tsx @@ -15,9 +15,14 @@ import { ThemeProvider, ThemeProviderProps, } from './themeProvider.component'; -import { withStyles } from './themeConsumer.component'; -import { getThemeValue } from './theme.service'; -import { ThemeType } from './type'; +import { + ThemedComponentProps, + withStyles, +} from './withStyles'; +import { + StyleSheet, + ThemeType, +} from '../style/styleSheet.service'; import { theme, themeInverse, @@ -28,7 +33,7 @@ const themeChangeTouchableTestId: string = '@theme/btnChangeTheme'; const json = (object: any): string => JSON.stringify(object); -class Mock extends React.Component { +class Mock extends React.Component { public render(): React.ReactElement { return ( @@ -96,37 +101,6 @@ class OverrideMock extends React.Component { } } -describe('@theme: service method checks', () => { - - it('finds theme value properly', async () => { - const themeValue = getThemeValue('gray-100', theme); - const undefinedValue = getThemeValue('undefined', theme); - - expect(themeValue).toEqual(theme['gray-100']); - expect(undefinedValue).toBeUndefined(); - }); - - it('finds referencing theme value properly', async () => { - const themeValue = getThemeValue('referencing', theme); - - expect(themeValue).toEqual(theme['gray-100']); - }); - - it('finds multiple referencing theme value properly', async () => { - const themeValue = getThemeValue('double-referencing', theme); - - expect(themeValue).toEqual(theme['gray-100']); - }); - - it('finds referencing theme value properly (initial reference)', async () => { - const themeValue = getThemeValue('referencing', theme); - - expect(themeValue).toEqual(theme['gray-100']); - }); - - -}); - describe('@theme: ui component checks', () => { it('* static methods are copied over', () => { @@ -195,8 +169,8 @@ describe('@theme: ui component checks', () => { const { theme: theme1 } = themedComponents[0].props; const { theme: theme2 } = themedComponents[1].props; - expect(json(theme1)).toEqual(json(theme)); - expect(json(theme2)).toEqual(json(themeInverse)); + expect(theme1).toEqual(StyleSheet.createCompiledTheme(theme)); + expect(theme2).toEqual(StyleSheet.createCompiledTheme(themeInverse)); }); }); diff --git a/src/components/theme/theme/themeConsumer.component.tsx b/src/components/theme/theme/themeConsumer.component.tsx deleted file mode 100644 index f440d6021..000000000 --- a/src/components/theme/theme/themeConsumer.component.tsx +++ /dev/null @@ -1,158 +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 hoistNonReactStatics from 'hoist-non-react-statics'; -import { ThemeContext } from './themeContext'; -import { - StyleSheetType, - ThemedStyleType, - ThemeType, -} from './type'; -import { createThemedStyle } from '../style/style.service'; -import { StyleType } from '@kitten/theme'; - -interface PrivateProps { - forwardedRef?: React.RefObject; -} - -export interface ThemedComponentProps { - theme?: ThemeType; - themedStyle?: ThemedStyleType | undefined; -} - -export type CreateStylesFunction = (theme: ThemeType) => StyleSheetType; - -export interface Context { - theme: ThemeType; -} - -export type ThemedComponentClass

= React.ComponentClass; - -/** - * `withStyles` is a High Order Function which is used to create themed style for non-styled component. - * Basically used when need to use theme variable somewhere. - * Returns component class which can be used as themed component. - * - * @property {ThemeType} theme - Determines theme used to style component. - * - * @property {StyleType} themedStyle - Determines component style for it's current state. - * - * @param Component - Type: {ComponentType}. Determines class of component to be themed. - * - * @param createStyles - Type: {(theme: ThemeType) => any}. Determines arrow function used to create styles. - * - * @overview-example Declaring Themed Component - * - * ``` - * import React from 'react'; - * import { TouchableOpacity } from 'react-native'; - * import { withStyles } from '@ui-kitten/components'; - * - * class Button extends React.Component { - * - * render() { - * // Retrieve styles from props (provided with themedStyle prop) - * // And apply it with saving priority of `style` prop - * - * const { style, themedStyle, ...restProps } = this.props; - * - * return ( - * - * ); - * } - * } - * - * export const ThemedButton = withStyles(Button, (theme) => ({ - * backgroundColor: theme['color-primary-default'], - * })); - * ``` - * - * @overview-example Themed Component Usage - * - * ``` - * import React from 'react'; - * import { ThemedButton } from './path-to/themedButton.component'; - * - * export const ThemedButtonShowcase = (props) => ( - * - * ); - * ``` - */ -export const withStyles =

(Component: React.ComponentType

, - createStyles?: CreateStylesFunction): ThemedComponentClass

=> { - - type WrappingProps = PrivateProps & WrappedProps; - type WrappedProps = ThemedComponentProps & P; - type WrappingElement = React.ReactElement; - type WrappedElement = React.ReactElement; - type WrappedElementInstance = React.ReactInstance; - - class Wrapper extends React.Component { - - private createThemedStyles = (style: ThemedStyleType, theme: ThemeType): ThemedStyleType => { - return Object.keys(style).reduce((acc: StyleType, current: string): ThemedStyleType => { - return { ...acc, [current]: createThemedStyle(style[current], theme) }; - }, {}); - }; - - private withThemedProps = (source: P, context: Context): WrappedProps => { - const style: ThemedStyleType = createStyles ? createStyles(context.theme) : {}; - - return { - ...source, - theme: context.theme, - themedStyle: this.createThemedStyles(style, context.theme), - }; - }; - - private renderWrappedElement = (context: Context): WrappedElement => { - const { forwardedRef, ...restProps } = this.props; - const props: WrappedProps = this.withThemedProps(restProps as P, context); - - return ( - - ); - }; - - public render(): React.ReactNode { - const ThemedElement = this.renderWrappedElement; - - return ( - {(theme: ThemeType): React.ReactElement

=> ( - - )} - ); - } - } - - const WrappingElement = (props: WrappingProps, ref: React.Ref): WrappingElement => { - return ( - // @ts-ignore - - - ); - }; - - const ThemedComponent = React.forwardRef(WrappingElement); - - ThemedComponent.displayName = Component.displayName || Component.name; - - // @ts-ignore - hoistNonReactStatics(ThemedComponent, Component); - - // @ts-ignore - return ThemedComponent; -}; diff --git a/src/components/theme/theme/themeContext.ts b/src/components/theme/theme/themeContext.ts index add92d2d0..29aa894eb 100644 --- a/src/components/theme/theme/themeContext.ts +++ b/src/components/theme/theme/themeContext.ts @@ -5,8 +5,8 @@ */ import React from 'react'; -import { ThemeType } from './type'; +import { ThemeType } from '../style/styleSheet.service'; -const defaultValue: ThemeType = {}; +const defaultTheme: ThemeType = {}; -export const ThemeContext: React.Context = React.createContext(defaultValue); +export const ThemeContext: React.Context = React.createContext(defaultTheme); diff --git a/src/components/theme/theme/themeProvider.component.tsx b/src/components/theme/theme/themeProvider.component.tsx index 948ab786d..0e5ac9cc2 100644 --- a/src/components/theme/theme/themeProvider.component.tsx +++ b/src/components/theme/theme/themeProvider.component.tsx @@ -6,13 +6,25 @@ import React from 'react'; import { ThemeContext } from './themeContext'; -import { ThemeType } from './type'; +import { + StyleSheet, + ThemeType, +} from '../style/styleSheet.service'; export interface ThemeProviderProps { theme: ThemeType; children?: React.ReactNode; } +/** + * Since ApplicationProvider is the root component of the application, + * it provides same theme for all underlying components. + * + * ThemeProvider allows modifying this theme so that each component that is the child + * of ThemeProvider will use modified theme. + * + * @overview-example ThemeProviderSimpleUsage + */ export class ThemeProvider extends React.PureComponent { public render(): React.ReactNode { @@ -20,7 +32,7 @@ export class ThemeProvider extends React.PureComponent { return ( + value={StyleSheet.createCompiledTheme(theme)}> {children} ); diff --git a/src/components/theme/theme/type.ts b/src/components/theme/theme/type.ts deleted file mode 100644 index 08de73dc2..000000000 --- a/src/components/theme/theme/type.ts +++ /dev/null @@ -1,5 +0,0 @@ -export type ThemeType = any; - -export type ThemedStyleType = any; - -export type StyleSheetType = any; diff --git a/src/components/theme/theme/useTheme.tsx b/src/components/theme/theme/useTheme.tsx new file mode 100644 index 000000000..8854f5ab4 --- /dev/null +++ b/src/components/theme/theme/useTheme.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +import { ThemeContext } from './themeContext'; +import { ThemeType } from '../style/styleSheet.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 new file mode 100644 index 000000000..3e3a53171 --- /dev/null +++ b/src/components/theme/theme/withStyles.tsx @@ -0,0 +1,101 @@ +/** + * @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 hoistNonReactStatics from 'hoist-non-react-statics'; +import { ThemeContext } from './themeContext'; +import { + Styles, + ThemeType, +} from '../style/styleSheet.service'; + +interface PrivateProps { + forwardedRef?: React.RefObject; +} + +export interface ThemedComponentProps = any> { + theme?: ThemeType; + themedStyle?: T | undefined; +} + +export type ThemedComponentClass> = React.ComponentClass & P>; + +interface PrivateProps { + forwardedRef?: React.RefObject; +} + +type CreateStylesFunction> = (theme: ThemeType) => T; + +/** + * `withStyles` is a High Order Function which is used to create themed style for non-styled component. + * Basically used when need to use theme variable somewhere. + * Returns component class which can be used as themed component. + * + * @property {ThemeType} theme - Determines theme used to style component. + * + * @property {Styles} themedStyle - Determines component style for it's current state. + * + * @param Component - Type: {ComponentType}. Determines class of component to be themed. + * + * @param createStyles - Type: {(theme: ThemeType) => Styles}. Determines arrow function used to create styles. + * + * @overview-example WithStylesSimpleUsage + */ +export const withStyles =

(Component: React.ComponentType

, + createStyles?: CreateStylesFunction): ThemedComponentClass => { + + type WrappingProps = PrivateProps & WrappedProps; + type WrappedProps = ThemedComponentProps & P; + type WrappingElement = React.ReactElement; + type WrappedElementInstance = React.ReactInstance; + + class Wrapper extends React.Component { + + private withThemedProps = (props: P, theme: ThemeType): WrappedProps => { + const themedStyle = createStyles && createStyles(theme); + return { ...props, theme, themedStyle }; + }; + + private renderWrappedElement = (theme: ThemeType): React.ReactElement> => { + const { forwardedRef, ...restProps } = this.props; + const props: WrappedProps = this.withThemedProps(restProps as P, theme); + + return ( + + ); + }; + + public render(): React.ReactElement { + return ( + + {this.renderWrappedElement} + + ); + } + } + + const WrappingElement = (props: WrappingProps, ref: React.Ref): WrappingElement => { + return ( + // @ts-ignore + + ); + }; + + const ThemedComponent = React.forwardRef(WrappingElement); + + ThemedComponent.displayName = Component.displayName || Component.name; + + hoistNonReactStatics(ThemedComponent, Component); + + // @ts-ignore + return ThemedComponent; +}; diff --git a/src/components/ui/radio/radio.spec.tsx.snap b/src/components/ui/radio/radio.spec.tsx.snap index a63ab59b1..1e6ecf7f0 100644 --- a/src/components/ui/radio/radio.spec.tsx.snap +++ b/src/components/ui/radio/radio.spec.tsx.snap @@ -75,53 +75,53 @@ exports[`@radio: matches snapshot default 1`] = ` } theme={ Object { - "background-alternative-color-1": "$color-basic-800", - "background-alternative-color-2": "$color-basic-900", - "background-alternative-color-3": "$color-basic-1000", - "background-alternative-color-4": "$color-basic-1100", - "background-basic-color-1": "$color-basic-100", - "background-basic-color-2": "$color-basic-200", - "background-basic-color-3": "$color-basic-300", - "background-basic-color-4": "$color-basic-400", - "background-primary-color-1": "$color-primary-500", - "background-primary-color-2": "$color-primary-600", - "background-primary-color-3": "$color-primary-700", - "background-primary-color-4": "$color-primary-800", - "border-alternative-color-1": "$color-basic-800", - "border-alternative-color-2": "$color-basic-900", - "border-alternative-color-3": "$color-basic-1000", - "border-alternative-color-4": "$color-basic-1100", - "border-alternative-color-5": "$color-basic-1100", - "border-basic-color-1": "$color-basic-100", - "border-basic-color-2": "$color-basic-200", - "border-basic-color-3": "$color-basic-300", - "border-basic-color-4": "$color-basic-400", - "border-basic-color-5": "$color-basic-500", - "border-danger-color-1": "$color-danger-500", - "border-danger-color-2": "$color-danger-600", - "border-danger-color-3": "$color-danger-700", - "border-danger-color-4": "$color-danger-800", - "border-danger-color-5": "$color-danger-900", - "border-info-color-1": "$color-info-500", - "border-info-color-2": "$color-info-600", - "border-info-color-3": "$color-info-700", - "border-info-color-4": "$color-info-800", - "border-info-color-5": "$color-info-900", - "border-primary-color-1": "$color-primary-500", - "border-primary-color-2": "$color-primary-600", - "border-primary-color-3": "$color-primary-700", - "border-primary-color-4": "$color-primary-800", - "border-primary-color-5": "$color-primary-900", - "border-success-color-1": "$color-success-500", - "border-success-color-2": "$color-success-600", - "border-success-color-3": "$color-success-700", - "border-success-color-4": "$color-success-800", - "border-success-color-5": "$color-success-900", - "border-warning-color-1": "$color-warning-500", - "border-warning-color-2": "$color-warning-600", - "border-warning-color-3": "$color-warning-700", - "border-warning-color-4": "$color-warning-800", - "border-warning-color-5": "$color-warning-900", + "background-alternative-color-1": "#222B45", + "background-alternative-color-2": "#1A2138", + "background-alternative-color-3": "#151A30", + "background-alternative-color-4": "#101426", + "background-basic-color-1": "#FFFFFF", + "background-basic-color-2": "#F7F9FC", + "background-basic-color-3": "#EDF1F7", + "background-basic-color-4": "#E4E9F2", + "background-primary-color-1": "#3366FF", + "background-primary-color-2": "#274BDB", + "background-primary-color-3": "#1A34B8", + "background-primary-color-4": "#102694", + "border-alternative-color-1": "#222B45", + "border-alternative-color-2": "#1A2138", + "border-alternative-color-3": "#151A30", + "border-alternative-color-4": "#101426", + "border-alternative-color-5": "#101426", + "border-basic-color-1": "#FFFFFF", + "border-basic-color-2": "#F7F9FC", + "border-basic-color-3": "#EDF1F7", + "border-basic-color-4": "#E4E9F2", + "border-basic-color-5": "#C5CEE0", + "border-danger-color-1": "#FF3D71", + "border-danger-color-2": "#DB2C66", + "border-danger-color-3": "#B81D5B", + "border-danger-color-4": "#94124E", + "border-danger-color-5": "#700940", + "border-info-color-1": "#0095FF", + "border-info-color-2": "#006FD6", + "border-info-color-3": "#0057C2", + "border-info-color-4": "#0041A8", + "border-info-color-5": "#002885", + "border-primary-color-1": "#3366FF", + "border-primary-color-2": "#274BDB", + "border-primary-color-3": "#1A34B8", + "border-primary-color-4": "#102694", + "border-primary-color-5": "#091C7A", + "border-success-color-1": "#00E096", + "border-success-color-2": "#00B383", + "border-success-color-3": "#008F72", + "border-success-color-4": "#007566", + "border-success-color-5": "#00524C", + "border-warning-color-1": "#FFAA00", + "border-warning-color-2": "#DB8B00", + "border-warning-color-3": "#B86E00", + "border-warning-color-4": "#945400", + "border-warning-color-5": "#703C00", "color-basic-100": "#FFFFFF", "color-basic-1000": "#151A30", "color-basic-1100": "#101426", @@ -133,17 +133,17 @@ exports[`@radio: matches snapshot default 1`] = ` "color-basic-700": "#2E3A59", "color-basic-800": "#222B45", "color-basic-900": "#1A2138", - "color-basic-active": "$color-basic-400", + "color-basic-active": "#E4E9F2", "color-basic-control-transparent-100": "rgba(255, 255, 255, 0.08)", "color-basic-control-transparent-200": "rgba(255, 255, 255, 0.16)", "color-basic-control-transparent-300": "rgba(255, 255, 255, 0.24)", "color-basic-control-transparent-400": "rgba(255, 255, 255, 0.32)", "color-basic-control-transparent-500": "rgba(255, 255, 255, 0.40)", "color-basic-control-transparent-600": "rgba(255, 255, 255, 0.48)", - "color-basic-default": "$color-basic-200", - "color-basic-disabled": "$color-basic-300", - "color-basic-focus": "$color-basic-500", - "color-basic-hover": "$color-basic-200", + "color-basic-default": "#F7F9FC", + "color-basic-disabled": "#EDF1F7", + "color-basic-focus": "#C5CEE0", + "color-basic-hover": "#F7F9FC", "color-basic-transparent-100": "rgba(143, 155, 179, 0.08)", "color-basic-transparent-200": "rgba(143, 155, 179, 0.16)", "color-basic-transparent-300": "rgba(143, 155, 179, 0.24)", @@ -159,11 +159,11 @@ exports[`@radio: matches snapshot default 1`] = ` "color-danger-700": "#B81D5B", "color-danger-800": "#94124E", "color-danger-900": "#700940", - "color-danger-active": "$color-danger-600", - "color-danger-default": "$color-danger-500", - "color-danger-disabled": "$color-danger-300", - "color-danger-focus": "$color-danger-700", - "color-danger-hover": "$color-danger-400", + "color-danger-active": "#DB2C66", + "color-danger-default": "#FF3D71", + "color-danger-disabled": "#FFA8B4", + "color-danger-focus": "#B81D5B", + "color-danger-hover": "#FF708D", "color-danger-transparent-100": "rgba(255, 61, 113, 0.08)", "color-danger-transparent-200": "rgba(255, 61, 113, 0.16)", "color-danger-transparent-300": "rgba(255, 61, 113, 0.24)", @@ -179,11 +179,11 @@ exports[`@radio: matches snapshot default 1`] = ` "color-info-700": "#0057C2", "color-info-800": "#0041A8", "color-info-900": "#002885", - "color-info-active": "$color-info-600", - "color-info-default": "$color-info-500", - "color-info-disabled": "$color-info-300", - "color-info-focus": "$color-info-700", - "color-info-hover": "$color-info-400", + "color-info-active": "#006FD6", + "color-info-default": "#0095FF", + "color-info-disabled": "#94CBFF", + "color-info-focus": "#0057C2", + "color-info-hover": "#42AAFF", "color-info-transparent-100": "rgba(0, 149, 255, 0.08)", "color-info-transparent-200": "rgba(0, 149, 255, 0.16)", "color-info-transparent-300": "rgba(0, 149, 255, 0.24)", @@ -199,11 +199,11 @@ exports[`@radio: matches snapshot default 1`] = ` "color-primary-700": "#1A34B8", "color-primary-800": "#102694", "color-primary-900": "#091C7A", - "color-primary-active": "$color-primary-600", - "color-primary-default": "$color-primary-500", - "color-primary-disabled": "$color-primary-200", - "color-primary-focus": "$color-primary-700", - "color-primary-hover": "$color-primary-400", + "color-primary-active": "#274BDB", + "color-primary-default": "#3366FF", + "color-primary-disabled": "#D9E4FF", + "color-primary-focus": "#1A34B8", + "color-primary-hover": "#598BFF", "color-primary-transparent-100": "rgba(51, 102, 255, 0.08)", "color-primary-transparent-200": "rgba(51, 102, 255, 0.16)", "color-primary-transparent-300": "rgba(51, 102, 255, 0.24)", @@ -228,11 +228,11 @@ exports[`@radio: matches snapshot default 1`] = ` "color-success-700": "#008F72", "color-success-800": "#007566", "color-success-900": "#00524C", - "color-success-active": "$color-success-600", - "color-success-default": "$color-success-500", - "color-success-disabled": "$color-success-200", - "color-success-focus": "$color-success-700", - "color-success-hover": "$color-success-400", + "color-success-active": "#00B383", + "color-success-default": "#00E096", + "color-success-disabled": "#B3FFD6", + "color-success-focus": "#008F72", + "color-success-hover": "#51F0B0", "color-success-transparent-100": "rgba(0, 224, 150, 0.08)", "color-success-transparent-200": "rgba(0, 224, 150, 0.16)", "color-success-transparent-300": "rgba(0, 224, 150, 0.24)", @@ -257,53 +257,53 @@ exports[`@radio: matches snapshot default 1`] = ` "color-warning-700": "#B86E00", "color-warning-800": "#945400", "color-warning-900": "#703C00", - "color-warning-active": "$color-warning-600", - "color-warning-default": "$color-warning-500", - "color-warning-disabled": "$color-warning-300", - "color-warning-focus": "$color-warning-700", - "color-warning-hover": "$color-warning-400", + "color-warning-active": "#DB8B00", + "color-warning-default": "#FFAA00", + "color-warning-disabled": "#FFE59E", + "color-warning-focus": "#B86E00", + "color-warning-hover": "#FFC94D", "color-warning-transparent-100": "rgba(255, 170, 0, 0.08)", "color-warning-transparent-200": "rgba(255, 170, 0, 0.16)", "color-warning-transparent-300": "rgba(255, 170, 0, 0.24)", "color-warning-transparent-400": "rgba(255, 170, 0, 0.32)", "color-warning-transparent-500": "rgba(255, 170, 0, 0.40)", "color-warning-transparent-600": "rgba(255, 170, 0, 0.48)", - "icon-active-color": "$color-primary-500", - "icon-basic-color": "$color-basic-700", - "icon-control-color": "$color-basic-100", - "icon-disabled-color": "$color-basic-400", - "icon-hint-color": "$color-basic-500", - "outline-color": "$color-basic-400", - "text-alternate-color": "$color-basic-100", - "text-basic-color": "$color-basic-900", - "text-control-color": "$color-basic-100", - "text-danger-active-color": "$color-danger-active", - "text-danger-color": "$color-danger-default", - "text-danger-disabled-color": "$color-danger-disabled", - "text-danger-focus-color": "$color-danger-focus", - "text-danger-hover-color": "$color-danger-hover", - "text-disabled-color": "$color-basic-500", - "text-hint-color": "$color-basic-600", - "text-info-active-color": "$color-info-active", - "text-info-color": "$color-info-default", - "text-info-disabled-color": "$color-info-disabled", - "text-info-focus-color": "$color-info-focus", - "text-info-hover-color": "$color-info-hover", - "text-primary-active-color": "$color-primary-active", - "text-primary-color": "$color-primary-default", - "text-primary-disabled-color": "$color-primary-disabled", - "text-primary-focus-color": "$color-primary-focus", - "text-primary-hover-color": "$color-primary-hover", - "text-success-active-color": "$color-success-active", - "text-success-color": "$color-success-default", - "text-success-disabled-color": "$color-success-disabled", - "text-success-focus-color": "$color-success-focus", - "text-success-hover-color": "$color-success-hover", - "text-warning-active-color": "$color-warning-active", - "text-warning-color": "$color-warning-default", - "text-warning-disabled-color": "$color-warning-disabled", - "text-warning-focus-color": "$color-warning-focus", - "text-warning-hover-color": "$color-warning-hover", + "icon-active-color": "#3366FF", + "icon-basic-color": "#2E3A59", + "icon-control-color": "#FFFFFF", + "icon-disabled-color": "#E4E9F2", + "icon-hint-color": "#C5CEE0", + "outline-color": "#E4E9F2", + "text-alternate-color": "#FFFFFF", + "text-basic-color": "#1A2138", + "text-control-color": "#FFFFFF", + "text-danger-active-color": "#DB2C66", + "text-danger-color": "#FF3D71", + "text-danger-disabled-color": "#FFA8B4", + "text-danger-focus-color": "#B81D5B", + "text-danger-hover-color": "#FF708D", + "text-disabled-color": "#C5CEE0", + "text-hint-color": "#8F9BB3", + "text-info-active-color": "#006FD6", + "text-info-color": "#0095FF", + "text-info-disabled-color": "#94CBFF", + "text-info-focus-color": "#0057C2", + "text-info-hover-color": "#42AAFF", + "text-primary-active-color": "#274BDB", + "text-primary-color": "#3366FF", + "text-primary-disabled-color": "#D9E4FF", + "text-primary-focus-color": "#1A34B8", + "text-primary-hover-color": "#598BFF", + "text-success-active-color": "#00B383", + "text-success-color": "#00E096", + "text-success-disabled-color": "#B3FFD6", + "text-success-focus-color": "#008F72", + "text-success-hover-color": "#51F0B0", + "text-warning-active-color": "#DB8B00", + "text-warning-color": "#FFAA00", + "text-warning-disabled-color": "#FFE59E", + "text-warning-focus-color": "#B86E00", + "text-warning-hover-color": "#FFC94D", } } > diff --git a/src/playground/env/index.js b/src/playground/env/index.js index da3cd834d..eb45b992c 100644 --- a/src/playground/env/index.js +++ b/src/playground/env/index.js @@ -1,6 +1,6 @@ const path = require('path'); module.exports = { - ENV: 'prod', - EVA_PATH: path.resolve(__dirname, '../../../node_modules/@eva-design'), + ENV: 'dev', + EVA_PATH: path.resolve(__dirname, '../../../../eva/packages'), }; diff --git a/src/playground/src/app/app.component.tsx b/src/playground/src/app/app.component.tsx index 9ef522c09..fa13e5360 100644 --- a/src/playground/src/app/app.component.tsx +++ b/src/playground/src/app/app.component.tsx @@ -15,6 +15,7 @@ import { ThemeContext, ThemeContextType, } from '@pg/themes/themeContext'; +import { default as customMapping } from './mapping.json'; import { ApplicationLoader } from './applicationLoader.component'; const themes = { @@ -22,10 +23,6 @@ const themes = { Dark: dark, }; -const customMapping = { - strict: { 'text-font-family': 'System' }, -}; - const fonts = { 'opensans-regular': require('../assets/fonts/opensans-regular.ttf'), 'roboto-regular': require('../assets/fonts/roboto-regular.ttf'), @@ -47,6 +44,7 @@ export default () => { const applicationProviderConfig: ApplicationProviderProps = { mapping: mapping, theme: themes[theme], + // @ts-ignore customMapping: customMapping, }; diff --git a/src/playground/src/app/mapping.json b/src/playground/src/app/mapping.json new file mode 100644 index 000000000..acb3abc14 --- /dev/null +++ b/src/playground/src/app/mapping.json @@ -0,0 +1,71 @@ +{ + "strict": { + "text-font-family": "System" + }, + "components": { + "StyledComponent": { + "meta": { + "parameters": { + "width": { + "type": "number" + }, + "height": { + "type": "number" + }, + "backgroundColor": { + "type": "string" + } + }, + "appearances": { + "default": { + "default": true + } + }, + "states": { + "active": { + "default": false, + "priority": 0 + } + }, + "variantGroups": { + "status": { + "primary": { + "default": true + }, + "danger": { + "default": false + } + } + } + }, + "appearances": { + "default": { + "mapping": { + "width": 40, + "height": 40 + }, + "variantGroups": { + "status": { + "primary": { + "backgroundColor": "color-primary-default", + "state": { + "active": { + "backgroundColor": "color-primary-active" + } + } + }, + "danger": { + "backgroundColor": "color-danger-default", + "state": { + "active": { + "backgroundColor": "color-danger-active" + } + } + } + } + } + } + } + } + } +} diff --git a/src/playground/src/components/safeAreaLayout.tsx b/src/playground/src/components/safeAreaLayout.tsx index bcb9efe2e..5b92ad71b 100644 --- a/src/playground/src/components/safeAreaLayout.tsx +++ b/src/playground/src/components/safeAreaLayout.tsx @@ -1,6 +1,5 @@ import React from 'react'; import { - StatusBarProps as StatusBarReactNativeProps, StyleProp, ViewStyle, } from 'react-native'; @@ -13,7 +12,7 @@ import { LayoutElement, LayoutProps, ThemeType, - withStyles, + useTheme, } from '@ui-kitten/components'; export enum SaveAreaInset { @@ -29,10 +28,12 @@ export interface SafeAreaLayoutProps extends LayoutProps { export type SafeAreaLayoutElement = React.ReactElement; -const SafeAreaLayoutComponent = (props: SafeAreaLayoutProps): LayoutElement => { +export const SafeAreaLayout = (props: SafeAreaLayoutProps): LayoutElement => { + + const theme: ThemeType = useTheme(); const safeAreaInsets: EdgeInsets = useSafeArea(); - const { insets, style, themedStyle, ...layoutProps } = props; + const { insets, style, ...layoutProps } = props; const toStyleProp = (inset: SaveAreaInset): ViewStyle => { @@ -41,7 +42,7 @@ const SafeAreaLayoutComponent = (props: SafeAreaLayoutProps): LayoutElement => { return { paddingBottom: safeAreaInsets.bottom }; case SaveAreaInset.TOP: return { - ...themedStyle.container, + backgroundColor: theme['background-basic-color-3'], paddingTop: safeAreaInsets.top, }; } @@ -59,9 +60,3 @@ const SafeAreaLayoutComponent = (props: SafeAreaLayoutProps): LayoutElement => { /> ); }; - -export const SafeAreaLayout = withStyles(SafeAreaLayoutComponent, (theme: ThemeType) => ({ - container: { - backgroundColor: theme['background-basic-color-3'], - }, -})); diff --git a/src/playground/src/components/showcase.component.tsx b/src/playground/src/components/showcase.component.tsx index 412da906a..8917a59bb 100644 --- a/src/playground/src/components/showcase.component.tsx +++ b/src/playground/src/components/showcase.component.tsx @@ -3,11 +3,7 @@ import { ScrollView, ViewStyle, } from 'react-native'; -import { - ThemedComponentProps, - ThemeType, - withStyles, -} from '@ui-kitten/components'; +import { useStyleSheet } from '@ui-kitten/components'; import { ComponentShowcase, ComponentShowcaseSection, @@ -17,19 +13,18 @@ import { ShowcaseSectionProps, } from './showcaseSection.component'; -interface ComponentProps { +export interface ShowcaseProps { showcase: ComponentShowcase; settings?: { [prop: string]: any }; - renderItem: (props: any) => React.ReactElement; + renderItem: (props: any) => React.ReactElement; } -export type ShowcaseProps = ThemedComponentProps & ComponentProps; - type ListItemElement = React.ReactElement; -const ShowcaseComponent = (props: ShowcaseProps): React.ReactElement => { +export const Showcase = (props: ShowcaseProps): React.ReactElement => { - const { themedStyle, showcase } = props; + const styles = StyleSheet.create(); + const { showcase } = props; const renderShowcaseElement = (showcaseProps: any): React.ReactElement => { return props.renderItem({ @@ -49,37 +44,35 @@ const ShowcaseComponent = (props: ShowcaseProps): React.ReactElement => { const listItemElement: ListItemElement = renderSectionElement(item); - const borderStyle: ViewStyle | null = index === showcase.sections.length - 1 ? null : themedStyle.itemBorder; + const borderStyle: ViewStyle | null = index === showcase.sections.length - 1 ? null : styles.itemBorder; return React.cloneElement(listItemElement, { key: index, - style: [themedStyle.item, borderStyle, listItemElement.props.style], + style: [styles.item, borderStyle, listItemElement.props.style], }); }; return ( {showcase.sections.map(renderSectionItem)} ); }; -export const Showcase = withStyles(ShowcaseComponent, (theme: ThemeType) => { - return { - container: { - flex: 1, - backgroundColor: theme['background-basic-color-1'], - }, - item: { - paddingHorizontal: 24, - paddingVertical: 16, - }, - itemBorder: { - borderBottomWidth: 1, - borderBottomColor: theme['border-basic-color-3'], - }, - }; +const StyleSheet = useStyleSheet({ + container: { + flex: 1, + backgroundColor: 'background-basic-color-1', + }, + item: { + paddingHorizontal: 24, + paddingVertical: 16, + }, + itemBorder: { + borderBottomWidth: 1, + borderBottomColor: 'border-basic-color-3', + }, }); diff --git a/src/playground/src/components/showcaseContainer.component.tsx b/src/playground/src/components/showcaseContainer.component.tsx index 7380b3434..3e7654c3c 100644 --- a/src/playground/src/components/showcaseContainer.component.tsx +++ b/src/playground/src/components/showcaseContainer.component.tsx @@ -21,7 +21,7 @@ import { ShowcaseSettings } from './showcaseSettings.component'; interface ShowcaseContainerProps extends SafeAreaLayoutProps { showcase: ComponentShowcase; settings?: ComponentShowcaseSetting[]; - renderItem: (props: any) => React.ReactElement; + renderItem: (props: any) => React.ReactElement; onBackPress?: () => void; } diff --git a/src/playground/src/components/showcaseItem.component.tsx b/src/playground/src/components/showcaseItem.component.tsx index 1f85f6670..68d1b1038 100644 --- a/src/playground/src/components/showcaseItem.component.tsx +++ b/src/playground/src/components/showcaseItem.component.tsx @@ -2,39 +2,35 @@ import React from 'react'; import { View, ViewProps, + StyleSheet, } from 'react-native'; import { Text, TextElement, - ThemedComponentProps, - ThemeType, - withStyles, } from '@ui-kitten/components'; import { ComponentShowcaseItem } from '@pg/model/componentShowcase.model'; -interface ComponentProps { +export interface ShowcaseItemProps extends ViewProps { item: ComponentShowcaseItem; renderItem: (props: any) => React.ReactElement; } -export type ShowcaseItemProps = ThemedComponentProps & ViewProps & ComponentProps; +export const ShowcaseItem = (props: ShowcaseItemProps): React.ReactElement => { -const ShowcaseItemComponent = (props: ShowcaseItemProps): React.ReactElement => { - - const { style, themedStyle, item, renderItem } = props; + const { style, item, renderItem } = props; const renderElement = (): React.ReactElement => { const element: React.ReactElement = renderItem(item.props); return React.cloneElement(element, { - style: [themedStyle.element, element.props.style], + style: [element.props.style], }); }; const renderTitleElement = (): TextElement => ( + style={styles.titleLabel}> {item.title} ); @@ -44,14 +40,14 @@ const ShowcaseItemComponent = (props: ShowcaseItemProps): React.ReactElement => const showcaseElement: React.ReactElement = renderElement(); return ( - + {titleElement} {showcaseElement} ); }; -export const ShowcaseItem = withStyles(ShowcaseItemComponent, (theme: ThemeType) => ({ +const styles = StyleSheet.create({ container: { flexDirection: 'row', alignItems: 'center', @@ -62,5 +58,4 @@ export const ShowcaseItem = withStyles(ShowcaseItemComponent, (theme: ThemeType) fontSize: 13, textAlign: 'left', }, - element: {}, -})); +}); diff --git a/src/playground/src/components/showcaseSection.component.tsx b/src/playground/src/components/showcaseSection.component.tsx index ef7c85015..2588ad5ba 100644 --- a/src/playground/src/components/showcaseSection.component.tsx +++ b/src/playground/src/components/showcaseSection.component.tsx @@ -1,5 +1,6 @@ import React from 'react'; import { + StyleSheet, View, ViewProps, } from 'react-native'; @@ -7,9 +8,6 @@ import { Text, TextElement, TextProps, - ThemedComponentProps, - ThemeType, - withStyles, } from '@ui-kitten/components'; import { ComponentShowcaseItem, @@ -20,28 +18,26 @@ import { ShowcaseItemProps, } from './showcaseItem.component'; -interface ComponentProps { +export interface ShowcaseSectionProps extends ViewProps { section: ComponentShowcaseSection; renderItem: (props: any) => React.ReactElement; } -export type ShowcaseSectionProps = ThemedComponentProps & ViewProps & ComponentProps; +export const ShowcaseSection = (props: ShowcaseSectionProps): React.ReactElement => { -const ShowcaseSectionComponent = (props: ShowcaseSectionProps): React.ReactElement => { - - const { style, themedStyle, section, renderItem } = props; + const { style, section, renderItem } = props; const renderShowcaseItem = (item: ComponentShowcaseItem, index: number): React.ReactElement => ( ); const renderTitleElement = (): TextElement => ( - + {section.title} ); @@ -49,14 +45,14 @@ const ShowcaseSectionComponent = (props: ShowcaseSectionProps): React.ReactEleme const titleElement: React.ReactElement = section.title && renderTitleElement(); return ( - + {titleElement} {section.items.map(renderShowcaseItem)} ); }; -export const ShowcaseSection = withStyles(ShowcaseSectionComponent, (theme: ThemeType) => ({ +const styles = StyleSheet.create({ container: {}, titleLabel: { marginVertical: 8, @@ -67,4 +63,4 @@ export const ShowcaseSection = withStyles(ShowcaseSectionComponent, (theme: Them item: { marginVertical: 8, }, -})); +}); diff --git a/src/playground/src/components/showcaseSettings.component.tsx b/src/playground/src/components/showcaseSettings.component.tsx index a305fa86f..879f0439f 100644 --- a/src/playground/src/components/showcaseSettings.component.tsx +++ b/src/playground/src/components/showcaseSettings.component.tsx @@ -11,9 +11,7 @@ import { CheckBox, OverflowMenu, OverflowMenuItemType, - ThemedComponentProps, - ThemeType, - withStyles, + useStyleSheet, } from '@ui-kitten/components'; import { ComponentShowcaseSetting } from '@pg/model/componentShowcase.model'; import { AppTheme } from '@pg/themes/themeContext'; @@ -23,7 +21,7 @@ import { TrashIcon, } from '@pg/icons'; -interface ComponentProps { +export interface ShowcaseSettingsProps extends ViewProps { themes?: AppTheme[]; settings?: ComponentShowcaseSetting[]; onThemeSelect?: (theme: AppTheme) => void; @@ -31,13 +29,13 @@ interface ComponentProps { onReset: () => void; } -export type ShowcaseSettingsProps = ThemedComponentProps & ViewProps & ComponentProps; - -const ShowcaseSettingsComponent = (props: ShowcaseSettingsProps): React.ReactElement => { +export const ShowcaseSettings = (props: ShowcaseSettingsProps): React.ReactElement => { const [themesMenuVisible, setThemesMenuVisible] = React.useState(false); const [settingsMenuVisible, setSettingsMenuVisible] = React.useState(false); + const styles = StyleSheet.create(); + const createSettingMenuItem = (setting: ComponentShowcaseSetting): OverflowMenuItemType => { return { title: setting.description || `${setting.propertyName}: ${setting.value}`, @@ -90,10 +88,8 @@ const ShowcaseSettingsComponent = (props: ShowcaseSettingsProps): React.ReactEle Platform.OS !== 'web' && Updates.reload(); }; - const { style, themedStyle } = props; - return ( - + ({ +const StyleSheet = useStyleSheet({ container: { flexDirection: 'row', justifyContent: 'space-evenly', paddingVertical: 16, - backgroundColor: theme['background-basic-color-2'], + backgroundColor: 'background-basic-color-2', }, -})); +}); diff --git a/src/playground/src/components/showcases/styled/styledComponentSimpleUsage.component.tsx b/src/playground/src/components/showcases/styled/styledComponentSimpleUsage.component.tsx new file mode 100644 index 000000000..e2c1e8072 --- /dev/null +++ b/src/playground/src/components/showcases/styled/styledComponentSimpleUsage.component.tsx @@ -0,0 +1,73 @@ +import React from 'react'; +import { TouchableOpacity } from 'react-native'; +import { + ApplicationProvider, + styled, +} from '@ui-kitten/components'; +import { + light, + mapping, +} from '@eva-design/eva'; + +/** + * All UI Kitten components are based on `styled` component. + * + * An example below demonstrates the how parameters can be transformed to component styles. + * With next examples we'll see more complex examples. + */ + +export const StyledComponentSimpleUsageShowcase = () => ( + + + + + +); + +class StyledComponent extends React.Component { + + static styledComponentName = 'StyledComponent'; + + render() { + return ( + + ); + } +} + +const StyledComponentShowcase = styled(StyledComponent); + +const styledComponentMapping = { + meta: { + parameters: { + width: { + type: 'number', + }, + height: { + type: 'number', + }, + backgroundColor: { + type: 'string', + }, + }, + appearances: { + default: { + default: true, + }, + }, + variantGroups: {}, + states: {}, + }, + appearances: { + default: { + mapping: { + width: 32, + height: 32, + backgroundColor: 'color-primary-default', + }, + }, + }, +}; diff --git a/src/playground/src/components/showcases/styled/styledComponentStates.component.tsx b/src/playground/src/components/showcases/styled/styledComponentStates.component.tsx new file mode 100644 index 000000000..18142c0d1 --- /dev/null +++ b/src/playground/src/components/showcases/styled/styledComponentStates.component.tsx @@ -0,0 +1,101 @@ +import React from 'react'; +import { TouchableOpacity } from 'react-native'; +import { + ApplicationProvider, + Interaction, + styled, +} from '@ui-kitten/components'; +import { + light, + mapping, +} from '@eva-design/eva'; + +/** + * This example shows how styled component can control states. + * + * Let's say we don't like the standard behavior of TouchableOpacity when it's pressed and + * we want the component to change it's color rather being highlighted. + * + * We define an active state in `meta` key and in mapping, so that component will change `backgroundColor`, + * when `active` is requested. To do this, we call `dispatch` function when Touchable is pressed. + * Then, when touch is released, we request nothing, which stands for `default`. + */ + +export const StyledComponentStatesShowcase = () => ( + + + + + +); + +class StyledComponent extends React.Component { + + static styledComponentName = 'StyledComponent'; + + onPressIn = () => { + this.props.dispatch([Interaction.ACTIVE]); + }; + + onPressOut = () => { + this.props.dispatch([]); + }; + + render() { + return ( + + ); + } +} + +const StyledComponentShowcase = styled(StyledComponent); + +const styledComponentMapping = { + meta: { + parameters: { + width: { + type: 'number', + }, + height: { + type: 'number', + }, + backgroundColor: { + type: 'string', + }, + }, + appearances: { + default: { + default: true, + }, + }, + variantGroups: {}, + states: { + active: { + default: false, + priority: 0, + }, + }, + }, + appearances: { + default: { + mapping: { + width: 32, + height: 32, + backgroundColor: 'color-primary-default', + state: { + active: { + backgroundColor: 'color-primary-active', + }, + }, + }, + }, + }, +}; diff --git a/src/playground/src/components/showcases/styled/styledComponentVariants.component.tsx b/src/playground/src/components/showcases/styled/styledComponentVariants.component.tsx new file mode 100644 index 000000000..81052de43 --- /dev/null +++ b/src/playground/src/components/showcases/styled/styledComponentVariants.component.tsx @@ -0,0 +1,126 @@ +import React from 'react'; +import { TouchableOpacity } from 'react-native'; +import { + ApplicationProvider, + Interaction, + styled, +} from '@ui-kitten/components'; +import { + light, + mapping, +} from '@eva-design/eva'; + +/** + * This example shows how styled component can apply custom properties like status or size. + * Note that it is controlled by passing them outside, via props, but it is defined in mappings. + * + * Let's say we want to have a `status` property that can be `primary` (which is default) and `danger`. + * Furthermore, we still want to control backgroundColor when it is pressed. + * + * The `variantGroups` key in `meta` defines all custom properties that can be applied by component. + * Each key in variant group is a string value that can be passed to this prop. + * + * Note that we can move `backgroundColor` property from `mapping` to each status. + */ + +export const StyledComponentVariantsShowcase = () => ( + + + + + +); + +class StyledComponent extends React.Component { + + static styledComponentName = 'StyledComponent'; + + onPressIn = () => { + this.props.dispatch([Interaction.ACTIVE]); + }; + + onPressOut = () => { + this.props.dispatch([]); + }; + + render() { + return ( + + ); + } +} + +const StyledComponentShowcase = styled(StyledComponent); + +const styledComponentMapping = { + meta: { + parameters: { + width: { + type: 'number', + }, + height: { + type: 'number', + }, + backgroundColor: { + type: 'string', + }, + }, + appearances: { + default: { + default: true, + }, + }, + variantGroups: { + status: { + primary: { + default: true, + }, + danger: { + default: false, + }, + }, + }, + states: { + active: { + default: false, + priority: 0, + }, + }, + }, + appearances: { + default: { + mapping: { + width: 40, + height: 40, + }, + variantGroups: { + status: { + primary: { + backgroundColor: 'color-primary-default', + state: { + active: { + backgroundColor: 'color-primary-active', + }, + }, + }, + danger: { + backgroundColor: 'color-danger-default', + state: { + active: { + backgroundColor: 'color-danger-active', + }, + }, + }, + }, + }, + }, + }, +}; diff --git a/src/playground/src/components/showcases/themeProvider/themeProviderSimpleUsage.component.tsx b/src/playground/src/components/showcases/themeProvider/themeProviderSimpleUsage.component.tsx new file mode 100644 index 000000000..37e5d4fed --- /dev/null +++ b/src/playground/src/components/showcases/themeProvider/themeProviderSimpleUsage.component.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import { light, dark } from '@eva-design/eva'; +import { ThemeProvider, Card, Layout, Text } from '@ui-kitten/components'; + +export const ThemeProviderSimpleUsageShowcase = () => ( + + + + + I use light theme + + + + + + I use dark theme + + + + + + I use custom light theme + + + + +); diff --git a/src/playground/src/components/showcases/useStyleSheet/useStyleSheetSimpleUsage.component.tsx b/src/playground/src/components/showcases/useStyleSheet/useStyleSheetSimpleUsage.component.tsx new file mode 100644 index 000000000..0a7babe6a --- /dev/null +++ b/src/playground/src/components/showcases/useStyleSheet/useStyleSheetSimpleUsage.component.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import { + Text, + Layout, + useStyleSheet, +} from '@ui-kitten/components'; + +export const UseStyleSheetSimpleUsageShowcase = () => { + const styles = StyleSheet.create(); + + return ( + + + I use success color as background! + + + ); +}; + +const StyleSheet = useStyleSheet({ + container: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + backgroundColor: 'color-success-default', + }, +}); diff --git a/src/playground/src/components/showcases/useTheme/useThemeSimpleUsage.component.tsx b/src/playground/src/components/showcases/useTheme/useThemeSimpleUsage.component.tsx new file mode 100644 index 000000000..c7985c67a --- /dev/null +++ b/src/playground/src/components/showcases/useTheme/useThemeSimpleUsage.component.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import { StyleSheet } from 'react-native'; +import { + Layout, + Text, + useTheme, +} from '@ui-kitten/components'; + +export const UseThemeSimpleUsageShowcase = () => { + const theme = useTheme(); + + return ( + + + I use primary color as background! + + + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + }, +}); diff --git a/src/playground/src/components/showcases/withStyles/withStylesSimpleUsage.component.tsx b/src/playground/src/components/showcases/withStyles/withStylesSimpleUsage.component.tsx new file mode 100644 index 000000000..7ca077e0d --- /dev/null +++ b/src/playground/src/components/showcases/withStyles/withStylesSimpleUsage.component.tsx @@ -0,0 +1,26 @@ +import React from 'react'; +import { + Layout, + Text, + withStyles, +} from '@ui-kitten/components'; + +const ThemedComponent = ({ themedStyle, theme }) => ( + + + I use info as background color and success as text color! + + +); + +export const WithStylesSimpleUsageShowcase = withStyles(ThemedComponent, theme => ({ + container: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + backgroundColor: theme['color-info-default'], + }, +})); + diff --git a/src/playground/src/icons/index.tsx b/src/playground/src/icons/index.tsx index 646edf164..14ee7bbc4 100644 --- a/src/playground/src/icons/index.tsx +++ b/src/playground/src/icons/index.tsx @@ -10,9 +10,8 @@ import { * Needed to support compatible icon names between different icon packs. */ export interface AppIconRegistry { - ['arrow-ios-downward']: string; - ['arrow-ios-upward']: string; ['arrow-back']: string; + ['brush']: string; ['color-palette']: string; ['grid']: string; ['list']: string; @@ -24,18 +23,14 @@ export interface AppIconRegistry { ['trash']: string; } -export const ArrowDownwardIcon = (style: StyleType): IconElement => ( - -); - -export const ArrowUpwardIcon = (style: StyleType): IconElement => ( - -); - export const BackIcon = (style): IconElement => ( ); +export const BrushIcon = (style): IconElement => ( + +); + export const ColorPaletteIcon = (style: StyleType): IconElement => ( ); diff --git a/src/playground/src/icons/materialIconPack.tsx b/src/playground/src/icons/materialIconPack.tsx index fc14dfac5..af1efdcfd 100644 --- a/src/playground/src/icons/materialIconPack.tsx +++ b/src/playground/src/icons/materialIconPack.tsx @@ -21,9 +21,8 @@ const IconProvider = (name) => ({ }); const MaterialIconRegistry: AppIconRegistry = { - ['arrow-ios-downward']: 'keyboard-arrow-down', - ['arrow-ios-upward']: 'keyboard-arrow-up', ['arrow-back']: 'arrow-back', + ['brush']: 'brush', ['color-palette']: 'palette', ['grid']: 'dashboard', ['list']: 'list', diff --git a/src/playground/src/navigation/home.navigator.tsx b/src/playground/src/navigation/home.navigator.tsx index 5c00a97bd..9741feac3 100644 --- a/src/playground/src/navigation/home.navigator.tsx +++ b/src/playground/src/navigation/home.navigator.tsx @@ -4,10 +4,12 @@ import { createDrawerNavigator } from 'react-navigation-drawer'; import { HomeDrawer } from '@pg/scenes/home/homeDrawer.component'; import { HomeTabBar } from '@pg/scenes/home/homeTabBar.component'; import { ComponentsNavigator } from './components.navigator'; +import { ServicesNavigator } from './services.navigator'; import { SamplesNavigator } from './samples.navigator'; const HomeBottomNavigator = createBottomTabNavigator({ ['Components']: ComponentsNavigator, + ['Services']: ServicesNavigator, ['Samples']: SamplesNavigator, }, { tabBarComponent: HomeTabBar, diff --git a/src/playground/src/navigation/services.navigator.tsx b/src/playground/src/navigation/services.navigator.tsx new file mode 100644 index 000000000..1c6f86eda --- /dev/null +++ b/src/playground/src/navigation/services.navigator.tsx @@ -0,0 +1,18 @@ +import { createStackNavigator } from 'react-navigation-stack'; +import { ServicesScreen } from '@pg/scenes/services/services.component'; +import { UseStyleSheetScreen } from '@pg/scenes/useStyleSheet/useStyleSheet.component'; +import { WithStylesScreen } from '@pg/scenes/withStyles/withStyles.component'; +import { ThemeProviderScreen } from '@pg/scenes/themeProvider/themeProvider.component'; +import { UseThemeScreen } from '@pg/scenes/useTheme/useTheme.component'; +import { StyledComponentScreen } from '@pg/scenes/styled/styled.component'; + +export const ServicesNavigator = createStackNavigator({ + ['Services']: ServicesScreen, + ['Use Theme']: UseThemeScreen, + ['Use StyleSheet']: UseStyleSheetScreen, + ['With Styles']: WithStylesScreen, + ['Theme Provider']: ThemeProviderScreen, + ['Styled']: StyledComponentScreen, +}, { + headerMode: 'none', +}); diff --git a/src/playground/src/scenes/components/components.component.tsx b/src/playground/src/scenes/components/components.component.tsx index 60f0abcaf..369dd99c6 100644 --- a/src/playground/src/scenes/components/components.component.tsx +++ b/src/playground/src/scenes/components/components.component.tsx @@ -13,6 +13,7 @@ import { } from '@ui-kitten/components'; import { SafeAreaLayout, + SafeAreaLayoutElement, SaveAreaInset, } from '@pg/components/safeAreaLayout'; import { Toolbar } from '@pg/components/toolbar.component'; @@ -54,7 +55,7 @@ export const routes: string[] = [ 'TopNavigation', ]; -export const ComponentsScreen = ({ navigation }): React.ReactElement => { +export const ComponentsScreen = ({ navigation }): SafeAreaLayoutElement => { const [displayComponents, setDisplayComponents] = React.useState(routes); diff --git a/src/playground/src/scenes/home/homeTabBar.component.tsx b/src/playground/src/scenes/home/homeTabBar.component.tsx index da50fb308..ab5cf3b1e 100644 --- a/src/playground/src/scenes/home/homeTabBar.component.tsx +++ b/src/playground/src/scenes/home/homeTabBar.component.tsx @@ -10,6 +10,7 @@ import { SaveAreaInset, } from '@pg/components/safeAreaLayout'; import { + BrushIcon, GridIcon, ListIcon, } from '@pg/icons'; @@ -17,7 +18,7 @@ import { export const HomeTabBar = ({ navigation }): SafeAreaLayoutElement => { const onSelect = (index: number): void => { - const selectedTabRoute: string = navigation.state.routes[index]; + const selectedTabRoute = navigation.state.routes[index]; navigation.navigate(selectedTabRoute.routeName); }; @@ -32,6 +33,10 @@ export const HomeTabBar = ({ navigation }): SafeAreaLayoutElement => { title='Components' icon={ListIcon} /> + { +export const SamplesScreen = ({ navigation }): SafeAreaLayoutElement => { const onItemPress = (index: number): void => { navigation.navigate(routes[index]); diff --git a/src/playground/src/scenes/services/services.component.tsx b/src/playground/src/scenes/services/services.component.tsx new file mode 100644 index 000000000..aacb8477c --- /dev/null +++ b/src/playground/src/scenes/services/services.component.tsx @@ -0,0 +1,77 @@ +import React from 'react'; +import { + ListRenderItemInfo, + StyleSheet, +} from 'react-native'; +import { + Divider, + List, + ListItem, + ListItemElement, +} from '@ui-kitten/components'; +import { + SafeAreaLayout, + SaveAreaInset, +} from '@pg/components/safeAreaLayout'; +import { Toolbar } from '@pg/components/toolbar.component'; +import { + ColorPaletteIcon, + MenuIcon, +} from '@pg/icons'; + +export const routes: string[] = [ + 'Use Theme', + 'Use StyleSheet', + 'With Styles', + 'Theme Provider', + 'Styled', +]; + +export const ServicesScreen = ({ navigation }): React.ReactElement => { + + const onItemPress = (index: number): void => { + navigation.navigate(routes[index]); + }; + + const renderItem = (info: ListRenderItemInfo): ListItemElement => ( + + onItemPress(info.index)} + /> + + + ); + + return ( + + + + + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, + searchInput: { + marginVertical: 4, + marginHorizontal: 8, + }, + item: { + paddingHorizontal: 16, + paddingVertical: 12, + }, +}); diff --git a/src/playground/src/scenes/styled/styled.component.tsx b/src/playground/src/scenes/styled/styled.component.tsx new file mode 100644 index 000000000..baea5ac12 --- /dev/null +++ b/src/playground/src/scenes/styled/styled.component.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import { SafeAreaLayoutElement } from '@pg/components/safeAreaLayout'; +import { + StyledComponentElement, + StyledComponentProps, + StyledComponentShowcase, +} from './styledComponentShowcase.component'; +import { ShowcaseContainer } from '../../components/showcaseContainer.component'; +import { styledComponentShowcase } from './type'; + +export const StyledComponentScreen = ({ navigation }): SafeAreaLayoutElement => { + + const renderItem = (props: StyledComponentProps): StyledComponentElement => ( + + ); + + return ( + navigation.goBack() } + /> + ); +}; diff --git a/src/playground/src/scenes/styled/styledComponentShowcase.component.tsx b/src/playground/src/scenes/styled/styledComponentShowcase.component.tsx new file mode 100644 index 000000000..3d61d101f --- /dev/null +++ b/src/playground/src/scenes/styled/styledComponentShowcase.component.tsx @@ -0,0 +1,52 @@ +import React from 'react'; +import { + GestureResponderEvent, + TouchableOpacity, + TouchableOpacityProps, +} from 'react-native'; +import { + Interaction, + styled, + StyledComponentProps as UIKittenStyledComponentProps, +} from '@ui-kitten/components'; + +export interface StyledComponentProps extends TouchableOpacityProps, UIKittenStyledComponentProps { + status?: string; +} + +export type StyledComponentElement = React.ReactElement; + +class StyledComponent extends React.Component { + + static styledComponentName: string = 'StyledComponent'; + + private onPressIn = (e: GestureResponderEvent): void => { + this.props.dispatch([Interaction.ACTIVE]); + if (this.props.onPressIn) { + this.props.onPressIn(e); + } + }; + + private onPressOut = (e: GestureResponderEvent): void => { + this.props.dispatch([]); + if (this.props.onPressOut) { + this.props.onPressOut(e); + } + }; + + public render(): React.ReactElement { + const { themedStyle, style, ...props } = this.props; + + return ( + + ); + } +} + +export const StyledComponentShowcase = styled(StyledComponent); diff --git a/src/playground/src/scenes/styled/type.ts b/src/playground/src/scenes/styled/type.ts new file mode 100644 index 000000000..2337c0323 --- /dev/null +++ b/src/playground/src/scenes/styled/type.ts @@ -0,0 +1,50 @@ +import { + ComponentShowcase, + ComponentShowcaseItem, + ComponentShowcaseSection, +} from '@pg/model/componentShowcase.model'; + +const defaultStyledComponent: ComponentShowcaseItem = { + title: 'Default', + props: {}, +}; + +const primaryStyledComponent: ComponentShowcaseItem = { + title: 'Primary', + props: { + ...defaultStyledComponent.props, + status: 'primary', + }, +}; + +const dangerStyledComponent: ComponentShowcaseItem = { + title: 'Danger', + props: { + ...defaultStyledComponent.props, + status: 'danger', + }, +}; + +const defaultSection: ComponentShowcaseSection = { + title: 'Default', + items: [ + defaultStyledComponent, + ], +}; + +const statusSection: ComponentShowcaseSection = { + title: 'Status', + items: [ + primaryStyledComponent, + dangerStyledComponent, + ], +}; + +export const styledComponentShowcase: ComponentShowcase = { + title: 'Styled Component', + sections: [ + defaultSection, + statusSection, + ], +}; + diff --git a/src/playground/src/scenes/themeProvider/themeProvider.component.tsx b/src/playground/src/scenes/themeProvider/themeProvider.component.tsx new file mode 100644 index 000000000..92f5646e6 --- /dev/null +++ b/src/playground/src/scenes/themeProvider/themeProvider.component.tsx @@ -0,0 +1,71 @@ +import React from 'react'; +import { + View, + ViewProps, +} from 'react-native'; +import { + Text, + ThemedComponentProps, + ThemeProvider, + ThemeType, + useStyleSheet, + useTheme, + withStyles, +} from '@ui-kitten/components'; + +const WithStylesShowcaseComponent = (props: ThemedComponentProps): React.ReactElement => ( + + I'm withStyles + +); + +const WithStylesShowcase = withStyles(WithStylesShowcaseComponent, (theme: ThemeType) => ({ + container: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + backgroundColor: theme['color-primary-default'], + }, +})); + +const UseStylesSheetShowcase = (): React.ReactElement => { + const styles = StyleSheet.create(); + + return ( + + I'm useStyleSheet + + ); +}; + +const StyleSheet = useStyleSheet({ + container: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + backgroundColor: 'color-primary-default', + }, +}); + +export const ThemeProviderScreen = (): React.ReactElement => { + const currentTheme: ThemeType = useTheme(); + + return ( + + + + + I use default primary color as background. + But the guys below will not! + + + + + + + + + + ); +}; + diff --git a/src/playground/src/scenes/useStyleSheet/useStyleSheet.component.tsx b/src/playground/src/scenes/useStyleSheet/useStyleSheet.component.tsx new file mode 100644 index 000000000..88f8fc5a7 --- /dev/null +++ b/src/playground/src/scenes/useStyleSheet/useStyleSheet.component.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import { View } from 'react-native'; +import { Text, useStyleSheet } from '@ui-kitten/components'; + +export const UseStyleSheetScreen = () => { + + const styles = StyleSheet.create(); + + return ( + + + I useStyleSheet to set background color. + + + ); +}; + +const StyleSheet = useStyleSheet({ + container: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + backgroundColor: 'color-primary-default', + }, +}); diff --git a/src/playground/src/scenes/useTheme/useTheme.component.tsx b/src/playground/src/scenes/useTheme/useTheme.component.tsx new file mode 100644 index 000000000..73d033ca4 --- /dev/null +++ b/src/playground/src/scenes/useTheme/useTheme.component.tsx @@ -0,0 +1,31 @@ +import React from 'react'; +import { + StyleSheet, + View, + ViewProps, +} from 'react-native'; +import { + Text, + ThemeType, + useTheme, +} from '@ui-kitten/components'; + +export const UseThemeScreen = (): React.ReactElement => { + const theme: ThemeType = useTheme(); + + return ( + + + I useTheme to set background color. + + + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + }, +}); diff --git a/src/playground/src/scenes/withStyles/withStyles.component.tsx b/src/playground/src/scenes/withStyles/withStyles.component.tsx new file mode 100644 index 000000000..42b32ceb9 --- /dev/null +++ b/src/playground/src/scenes/withStyles/withStyles.component.tsx @@ -0,0 +1,54 @@ +import React from 'react'; +import { + View, + ViewProps, +} from 'react-native'; +import { + Text, + ThemedComponentProps, + ThemeType, + withStyles, +} from '@ui-kitten/components'; + +class WithStylesClassComponent extends React.Component { + + public render(): React.ReactElement { + return ( + + Class Component + + ); + } +} + +const WithStylesFunctionComponent = (props: ViewProps & ThemedComponentProps): React.ReactElement => ( + + Functional Component + +); + +const WithStylesClassShowcase = withStyles(WithStylesClassComponent, (theme: ThemeType) => ({ + container: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + backgroundColor: theme['color-primary-default'], + }, +})); + +const WithStylesFunctionShowcase = withStyles(WithStylesFunctionComponent, (theme: ThemeType) => ({ + container: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + backgroundColor: theme['color-success-default'], + }, +})); + +export const WithStylesScreen = (): React.ReactElement => ( + + + + +); +