UNPKG

4.71 kBJavaScriptView Raw
1/* @flow */
2
3import {Component, PropTypes} from 'react';
4
5import StyleKeeper from './style-keeper.js';
6import resolveStyles from './resolve-styles.js';
7
8const KEYS_TO_IGNORE_WHEN_COPYING_PROPERTIES = [
9 'arguments',
10 'callee',
11 'caller',
12 'length',
13 'name',
14 'prototype',
15 'type',
16];
17
18function copyProperties(source, target) {
19 Object.getOwnPropertyNames(source).forEach(key => {
20 if (
21 KEYS_TO_IGNORE_WHEN_COPYING_PROPERTIES.indexOf(key) < 0 &&
22 !target.hasOwnProperty(key)
23 ) {
24 const descriptor = Object.getOwnPropertyDescriptor(source, key);
25 Object.defineProperty(target, key, descriptor);
26 }
27 });
28}
29
30function isStateless(component: Function): boolean {
31 return !component.render &&
32 !(component.prototype && component.prototype.render);
33}
34
35export default function enhanceWithRadium(
36 configOrComposedComponent: Class<any> | constructor | Function | Object,
37 config?: Object = {},
38): constructor {
39 if (typeof configOrComposedComponent !== 'function') {
40 const newConfig = {...config, ...configOrComposedComponent};
41 return function(configOrComponent) {
42 return enhanceWithRadium(configOrComponent, newConfig);
43 };
44 }
45
46 const component: Function = configOrComposedComponent;
47 let ComposedComponent: constructor = component;
48
49 // Handle stateless components
50 if (isStateless(ComposedComponent)) {
51 ComposedComponent = class extends Component {
52 state: Object;
53
54 render() {
55 return component(this.props, this.context);
56 }
57 };
58 ComposedComponent.displayName = component.displayName || component.name;
59 }
60
61 class RadiumEnhancer extends ComposedComponent {
62 static _isRadiumEnhanced = true;
63
64 state: Object;
65
66 _radiumMediaQueryListenersByQuery: {
67 [query: string]: {remove: () => void},
68 };
69 _radiumMouseUpListener: {remove: () => void};
70 _radiumIsMounted: boolean;
71
72 constructor() {
73 super(...arguments);
74
75 this.state = this.state || {};
76 this.state._radiumStyleState = {};
77 this._radiumIsMounted = true;
78 }
79
80 componentWillUnmount() {
81 if (super.componentWillUnmount) {
82 super.componentWillUnmount();
83 }
84
85 this._radiumIsMounted = false;
86
87 if (this._radiumMouseUpListener) {
88 this._radiumMouseUpListener.remove();
89 }
90
91 if (this._radiumMediaQueryListenersByQuery) {
92 Object.keys(this._radiumMediaQueryListenersByQuery).forEach(
93 function(query) {
94 this._radiumMediaQueryListenersByQuery[query].remove();
95 },
96 this,
97 );
98 }
99 }
100
101 getChildContext() {
102 const superChildContext = super.getChildContext
103 ? super.getChildContext()
104 : {};
105
106 if (!this.props.radiumConfig) {
107 return superChildContext;
108 }
109
110 const newContext = {...superChildContext};
111
112 if (this.props.radiumConfig) {
113 newContext._radiumConfig = this.props.radiumConfig;
114 }
115
116 return newContext;
117 }
118
119 render() {
120 const renderedElement = super.render();
121 let currentConfig = this.props.radiumConfig ||
122 this.context._radiumConfig ||
123 config;
124
125 if (config && currentConfig !== config) {
126 currentConfig = {
127 ...config,
128 ...currentConfig,
129 };
130 }
131
132 return resolveStyles(this, renderedElement, currentConfig);
133 }
134 }
135
136 // Class inheritance uses Object.create and because of __proto__ issues
137 // with IE <10 any static properties of the superclass aren't inherited and
138 // so need to be manually populated.
139 // See http://babeljs.io/docs/advanced/caveats/#classes-10-and-below-
140 copyProperties(component, RadiumEnhancer);
141
142 if (process.env.NODE_ENV !== 'production') {
143 // This also fixes React Hot Loader by exposing the original components top
144 // level prototype methods on the Radium enhanced prototype as discussed in
145 // https://github.com/FormidableLabs/radium/issues/219.
146 copyProperties(ComposedComponent.prototype, RadiumEnhancer.prototype);
147 }
148
149 if (RadiumEnhancer.propTypes && RadiumEnhancer.propTypes.style) {
150 RadiumEnhancer.propTypes = {
151 ...RadiumEnhancer.propTypes,
152 style: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
153 };
154 }
155
156 RadiumEnhancer.displayName = component.displayName ||
157 component.name ||
158 'Component';
159
160 RadiumEnhancer.contextTypes = {
161 ...RadiumEnhancer.contextTypes,
162 _radiumConfig: PropTypes.object,
163 _radiumStyleKeeper: PropTypes.instanceOf(StyleKeeper),
164 };
165
166 RadiumEnhancer.childContextTypes = {
167 ...RadiumEnhancer.childContextTypes,
168 _radiumConfig: PropTypes.object,
169 _radiumStyleKeeper: PropTypes.instanceOf(StyleKeeper),
170 };
171
172 return RadiumEnhancer;
173}