Skip to content

Instantly share code, notes, and snippets.

@rohozhnikoff
Last active August 12, 2016 17:10
Show Gist options
  • Save rohozhnikoff/df2e26dbabe2cd67d54030f0b5549734 to your computer and use it in GitHub Desktop.
Save rohozhnikoff/df2e26dbabe2cd67d54030f0b5549734 to your computer and use it in GitHub Desktop.
[react-native] style creator
import { Platform, StyleSheet } from 'react-native';
function prepareStyles(stylesheet) {
return StyleSheet.create(
mapValues(
stylesheet,
function wrapPlatformSpecificRules(rules) {
rules['ios'] && rules['ios'] = Platform.select({'ios': rules['ios']});
rules['android'] && rules['android'] = Platform.select({'android': rules['android']})
return rules;
}
)
)
}
function filterTrueSelectors(hash) {
return reduce(hash, (list, v, k) => {
v && list.concat([k]);
return list
}, []);
}
function makeKeyFromArguments(args) {
return map(args, function(v, k){
ifObject(v)
? map(v, (v, k) => k + ':' + Boolean(v)).join(',')
: v;
}).join(';')
}
const Styler = function(){
return Styler.create.apply(Styler, arguments);
}
Styler.utils = {}
Styler.theme = {}
const STORE = []
Styler.create = function(rules){
const id = STORE.length; // next available id
function getter(){
if(arguments.length === 1 && isString(arguments[0])) {
return this[arguments[0]]
}
const key = makeKeyFromArguments(arguments);
return this[key] || this[key] = toArray(arguments).map((selector) => {
return isObject(selector)
? StyleSheet.flatten(filterTrueSelectors(selector).map((v, _selector) => this[s]))
: this[selector]
})
}
const preparedRules = isFunction(rules)
? rules(this.utils, this.theme)
: rules
assign(getter, preparedRules);
if (isFunction(rules)) {
STORE[id] = {
getter,
rulesMethod: rules,
rulesKeys: keys(preparedRules)
};
}
return getter;
}
Styler.extendTheme = function(theme){
this.theme = isFunction(theme) ? theme(this.utils, this.theme) : theme;
Styler.recalc()
return this;
}
Styler.extendUtils = function(utils){
this.utils = isFunction(utils) ? utils(this.utils, this.theme) : utils;
Styler.recalc(); // does it really need? maybe we separate dimensions/os to Params
return this;
}
Styler.recalc = function(){
if(STORE.length === 0) {
return
}
STORE.forEach((handle) => {
const {getter, rulesMethod, rulesKeys} = handle;
const newRules = rulesMethod(this.utils, this.theme);
const nulledRules = rulesKeys.filter((key) => !newRules[key])
.reduce((nulled, key) => {
nulled[key] = null
return nulled
}, {});
handle.rulesKeys = keys(newRules)
assign(getter, nulledRules, newRules)
});
Styler.bindedComponents.forEach((component) => {
component.forceUpdate()
})
}
Styler.bindedComponents = [];
Styler.updateOnRecalc = function(RootComponent) {
Styler.bindedComponents.push(RootComponent)
return RootComponent
}
const STYLES = Styler.create(({rgba, size, orientation}, {basicFont}) => {
'wrapper': {
fontSize: basicFont + 1,
android: {
color: 'green'
},
ios: {
color: 'blue'
}
},
'wrapper-loading': {
background: rgba('#ff00ff', .5)
},
'image': {
}
})
class Component extends React.Component {
@Styler.updateOnRecalc // recalc all method-based styles, then force update
render() {
return <View style={[this.props.style, STYLES('wrapper', {
'wrapper-loading': this.props.isLoading
})]}>
<Image source={this.props.source} style={STYLES.image}/>
</View>
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment