UNPKG

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