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 )
@@ -116,13 +133,52 @@ export default (componentName, localTheme, options = {}) => (ThemedComponent) =>
116
133
return Themed
117
134
}
118
135
119
- export function themeable ( style = { } , theme ) {
120
- if ( ! theme ) return style
121
- return Object . keys ( theme ) . reduce ( ( result , key ) => ( {
122
- ...result , [ key ] : style [ key ] ? `${ style [ key ] } ${ theme [ key ] } ` : theme [ key ]
123
- } ) , style )
136
+ /**
137
+ * Merges two themes by concatenating values with the same keys
138
+ * @param {TReactCSSThemrTheme } original - Original theme object
139
+ * @param {TReactCSSThemrTheme } mixin - Mixing theme object
140
+ * @returns {TReactCSSThemrTheme } - Merged resulting theme
141
+ */
142
+ export function themeable ( original = { } , mixin ) {
143
+ //don't merge if no mixin is passed
144
+ if ( ! mixin ) return original
145
+
146
+ //merge themes by concatenating values with the same keys
147
+ return Object . keys ( mixin ) . reduce (
148
+
149
+ //merging reducer
150
+ ( result , key ) => {
151
+ const originalValue = original [ key ]
152
+ const mixinValue = mixin [ key ]
153
+
154
+ let newValue
155
+
156
+ //check if values are nested objects
157
+ if ( typeof originalValue === 'object' && typeof mixinValue === 'object' ) {
158
+ //go recursive
159
+ newValue = themeable ( originalValue , mixinValue )
160
+ } else {
161
+ //either concat or take mixin value
162
+ newValue = originalValue ? `${ originalValue } ${ mixinValue } ` : mixinValue
163
+ }
164
+
165
+ return {
166
+ ...result ,
167
+ [ key ] : newValue
168
+ }
169
+ } ,
170
+
171
+ //use original theme as an acc
172
+ original
173
+ )
124
174
}
125
175
176
+ /**
177
+ * Validates compose option
178
+ * @param {String|Boolean } composeTheme - Compose them option
179
+ * @throws
180
+ * @returns {undefined }
181
+ */
126
182
function validateComposeOption ( composeTheme ) {
127
183
if ( [ COMPOSE_DEEPLY , COMPOSE_SOFTLY , DONT_COMPOSE ] . indexOf ( composeTheme ) === - 1 ) {
128
184
throw new Error (
@@ -133,6 +189,12 @@ function validateComposeOption(composeTheme) {
133
189
}
134
190
}
135
191
192
+ /**
193
+ * Removes namespace from key
194
+ * @param {String } key - Key
195
+ * @param {String } themeNamespace - Theme namespace
196
+ * @returns {String } - Key
197
+ */
136
198
function removeNamespace ( key , themeNamespace ) {
137
199
const capitalized = key . substr ( themeNamespace . length )
138
200
return capitalized . slice ( 0 , 1 ) . toLowerCase ( ) + capitalized . slice ( 1 )
0 commit comments