1
1
import React , { Component , PropTypes } from 'react'
2
2
import invariant from 'invariant'
3
3
4
+ /**
5
+ * @typedef {Object.<string, TReactCSSThemrTheme> } TReactCSSThemrTheme
6
+ */
7
+
8
+ /**
9
+ * @typedef {{} } TReactCSSThemrOptions
10
+ * @property {String|Boolean } [composeTheme=COMPOSE_DEEPLY]
11
+ * @property {Boolean } [withRef=false]
12
+ */
13
+
4
14
const COMPOSE_DEEPLY = 'deeply'
5
15
const COMPOSE_SOFTLY = 'softly'
6
16
const DONT_COMPOSE = false
@@ -14,6 +24,13 @@ const THEMR_CONFIG = typeof Symbol !== 'undefined' ?
14
24
Symbol ( 'THEMR_CONFIG' ) :
15
25
'__REACT_CSS_THEMR_CONFIG__'
16
26
27
+ /**
28
+ * Themr decorator
29
+ * @param {String|Number|Symbol } componentName - Component name
30
+ * @param {TReactCSSThemrTheme } localTheme - Base theme
31
+ * @param {{} } options - Themr options
32
+ * @returns {function(ThemedComponent:Function):Function } - ThemedComponent
33
+ */
17
34
export default ( componentName , localTheme , options = { } ) => ( ThemedComponent ) => {
18
35
const { composeTheme : optionComposeTheme , withRef : optionWithRef } = { ...DEFAULT_OPTIONS , ...options }
19
36
validateComposeOption ( optionComposeTheme )
@@ -140,13 +157,52 @@ export default (componentName, localTheme, options = {}) => (ThemedComponent) =>
140
157
return Themed
141
158
}
142
159
143
- export function themeable ( style = { } , theme ) {
144
- if ( ! theme ) return style
145
- return Object . keys ( theme ) . reduce ( ( result , key ) => ( {
146
- ...result , [ key ] : style [ key ] ? `${ style [ key ] } ${ theme [ key ] } ` : theme [ key ]
147
- } ) , style )
160
+ /**
161
+ * Merges two themes by concatenating values with the same keys
162
+ * @param {TReactCSSThemrTheme } original - Original theme object
163
+ * @param {TReactCSSThemrTheme } mixin - Mixing theme object
164
+ * @returns {TReactCSSThemrTheme } - Merged resulting theme
165
+ */
166
+ export function themeable ( original = { } , mixin ) {
167
+ //don't merge if no mixin is passed
168
+ if ( ! mixin ) return original
169
+
170
+ //merge themes by concatenating values with the same keys
171
+ return Object . keys ( mixin ) . reduce (
172
+
173
+ //merging reducer
174
+ ( result , key ) => {
175
+ const originalValue = original [ key ]
176
+ const mixinValue = mixin [ key ]
177
+
178
+ let newValue
179
+
180
+ //check if values are nested objects
181
+ if ( typeof originalValue === 'object' && typeof mixinValue === 'object' ) {
182
+ //go recursive
183
+ newValue = themeable ( originalValue , mixinValue )
184
+ } else {
185
+ //either concat or take mixin value
186
+ newValue = originalValue ? `${ originalValue } ${ mixinValue } ` : mixinValue
187
+ }
188
+
189
+ return {
190
+ ...result ,
191
+ [ key ] : newValue
192
+ }
193
+ } ,
194
+
195
+ //use original theme as an acc
196
+ original
197
+ )
148
198
}
149
199
200
+ /**
201
+ * Validates compose option
202
+ * @param {String|Boolean } composeTheme - Compose them option
203
+ * @throws
204
+ * @returns {undefined }
205
+ */
150
206
function validateComposeOption ( composeTheme ) {
151
207
if ( [ COMPOSE_DEEPLY , COMPOSE_SOFTLY , DONT_COMPOSE ] . indexOf ( composeTheme ) === - 1 ) {
152
208
throw new Error (
@@ -157,6 +213,12 @@ function validateComposeOption(composeTheme) {
157
213
}
158
214
}
159
215
216
+ /**
217
+ * Removes namespace from key
218
+ * @param {String } key - Key
219
+ * @param {String } themeNamespace - Theme namespace
220
+ * @returns {String } - Key
221
+ */
160
222
function removeNamespace ( key , themeNamespace ) {
161
223
const capitalized = key . substr ( themeNamespace . length )
162
224
return capitalized . slice ( 0 , 1 ) . toLowerCase ( ) + capitalized . slice ( 1 )
0 commit comments