forked from akveo/react-native-ui-kitten
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathstyled.tsx
158 lines (130 loc) · 5 KB
/
styled.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/**
* @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 { ThemeStyleType } from '@eva-design/dss';
import { StyleConsumerService } from './styleConsumer.service';
import {
Interaction,
StyleType,
} from './style.service';
import { MappingContext } from '../mapping/mappingContext';
import { ThemeContext } from '../theme/themeContext';
import { ThemeType } from '../theme/theme.service';
interface PrivateProps<T> {
forwardedRef?: React.Ref<T>;
}
export interface StyledComponentProps {
appearance?: string;
theme?: ThemeType;
themedStyle?: StyleType;
dispatch?: (interaction: Interaction[]) => void;
}
interface State {
interaction: Interaction[];
}
export type StyledComponentClass<P> = React.ComponentClass<StyledComponentProps & P>;
/**
* `styled` is a High Order Function which is used to apply style mapping on component.
*
* Requires component to have `styledComponentName` string property which defines
* corresponding component name in mapping. (e.g 'Button' for Button component).
* Returns component class which can be used as styled component.
*
* @property {string} appearance - Determines style appearance of component. Default is provided by mapping.
*
* @property {ThemeType} theme - Determines theme used to style component.
*
* @property {StyleType} themedStyle - Determines component style for it's current state.
*
* @property {(interaction: Interaction[]) => void} dispatch - Determines function
* for dispatching current state of component. This is designed to be used as style request function.
* Calls component re-render if style for requested state differ from current.
*
* @param Component - Type: {ComponentType}. Determines class or functional component to be styled.
*
* @overview-example StyledComponentSimpleUsage
*
* @overview-example StyledComponentStates
*
* @overview-example StyledComponentVariants
*/
export const styled = <P extends object>(Component: React.ComponentType<P>): StyledComponentClass<P> => {
// @ts-ignore
if (!Component.styledComponentName) {
console.warn('Styled components should specify corresponding style name.');
return null;
}
type WrappingProps = PrivateProps<WrappedElementInstance> & WrappedProps;
type WrappedProps = StyledComponentProps & P;
type WrappingElement = React.ReactElement<WrappingProps>;
type WrappedElement = React.ReactElement<WrappedProps>;
type WrappedElementInstance = React.ReactInstance;
class Wrapper extends React.Component<WrappingProps, State> {
public state: State = {
interaction: [],
};
private init: boolean = false;
// Yes. This is not static because it is calculated once we got some meta from style context.
private defaultProps: StyledComponentProps;
private service: StyleConsumerService;
private onInit = (style: ThemeStyleType, theme: ThemeType): void => {
// @ts-ignore
this.service = new StyleConsumerService(Component.styledComponentName, style, theme);
this.defaultProps = this.service.createDefaultProps();
this.init = true;
};
private onDispatch = (interaction: Interaction[]): void => {
this.setState({ interaction });
};
private withStyledProps = (source: P,
style: ThemeStyleType,
theme: ThemeType): WrappedProps => {
const { interaction } = this.state;
const props: WrappingProps = { ...this.defaultProps, ...source };
return this.service.withStyledProps(props, style, theme, interaction);
};
private renderWrappedElement = (style: ThemeStyleType, theme: ThemeType): WrappedElement => {
if (!this.init) {
this.onInit(style, theme);
}
const { forwardedRef, ...restProps } = this.props;
const props: P & StyledComponentProps = this.withStyledProps(restProps as P, style, theme);
return (
<Component
{...props}
ref={forwardedRef}
dispatch={this.onDispatch}
/>
);
};
public render(): React.ReactNode {
return (
<MappingContext.Consumer>{(style: ThemeStyleType): WrappedElement => (
<ThemeContext.Consumer>{(theme: ThemeType): WrappedElement => {
return this.renderWrappedElement(style, theme);
}}</ThemeContext.Consumer>
)}</MappingContext.Consumer>
);
}
}
const WrappingElement = (props: WrappingProps,
ref: React.Ref<WrappedElementInstance>): WrappingElement => {
return (
// @ts-ignore
<Wrapper
{...props}
forwardedRef={ref}
/>
);
};
const ResultComponent = React.forwardRef<WrappedElementInstance, WrappingProps>(WrappingElement);
ResultComponent.displayName = Component.displayName || Component.name;
// @ts-ignore
hoistNonReactStatics(ResultComponent, Component);
// @ts-ignore
return ResultComponent;
};