Last active
September 6, 2018 19:37
-
-
Save subtleGradient/a47830160afb246980b1a16e4f7458ad to your computer and use it in GitHub Desktop.
Sheet Mixin Demo. Shows how to use spreadable mixins to do some wacky behavior sheets stuff with React
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<meta charset=utf-8> | |
<title>HasSheetsMixin</title> | |
<script src="http://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.11.0/es6-shim.min.js"></script> | |
<script src="http://fb.me/react-with-addons-0.11.1.js"></script> | |
<script src="http://fb.me/JSXTransformer-0.11.1.js"></script> | |
<div id="SheetsDemoRoot"></div> | |
<script type="text/jsx;harmony=true" src="SheetMixinDemo.jsx"></script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** @jsx React.DOM */ | |
function createMergingSelector(getSheets){ | |
return function mergeStyles(...names){ | |
return mergeMatchingItemsFromSheets(getSheets(this, names), this, names); | |
}; | |
} | |
function createCollectionSelector(getSheets){ | |
return function collectStyles(...names){ | |
return collectMatchingItemsFromSheets(getSheets(this, names), this, names); | |
}; | |
} | |
function assignOntoThis(object){ | |
Object.assign(this, object); | |
} | |
function mergeMatchingItemsFromSheets(sheets, component, names){ | |
return collectMatchingItemsFromSheets(sheets, component, names, assignOntoThis, {}); | |
} | |
function collectMatchingItemsFromSheets(sheets, component, names, callback, callbackThis){ | |
if (!Array.isArray(sheets)) { | |
return; | |
} | |
if (callback === undefined) { | |
callbackThis = []; | |
callback = callbackThis.push; | |
} | |
for (var sheetIndex=0, sheet; sheet = sheets[sheetIndex]; sheetIndex++){ | |
for (var nameIndex=0, name; name = names[nameIndex]; nameIndex++){ | |
var style = typeof name == 'string' ? sheet[name] : name; | |
callbackThis = forEachMatchingItem(style, component, name, callback, callbackThis); | |
} | |
} | |
return callbackThis; | |
} | |
function forEachMatchingItem(item, component, refName, callback, callbackThis){ | |
if (item == null) { | |
return callbackThis; | |
} | |
if (typeof item == 'object' && Array.isArray(item)) { | |
for (var itemPartsIndex=0, itemPart; itemPart = item[itemPartsIndex]; itemPartsIndex++){ | |
callbackThis = forEachMatchingItem(itemPart, component, refName, callback, callbackThis); | |
} | |
return callbackThis; | |
} else if (typeof item == 'function') { | |
item = item(component, refName); | |
} | |
if (item == null) { | |
return callbackThis; | |
} | |
if (callback === undefined) { | |
callbackThis = []; | |
callback = callbackThis.push; | |
} | |
callback.call(callbackThis, item); | |
return callbackThis; | |
} | |
// if (__DEV__) | |
{ | |
;(function(){ | |
var global = this; | |
var theThis = {theThis:true}; var theComponent = {theComponent:true}; var theRefName = {theRefName:true}; | |
var theStyle = {color:'red'}; | |
var callbackCount = 0; | |
forEachMatchingItem(theStyle, theComponent, theRefName, function(style) { | |
callbackCount++; | |
console.assert(style === theStyle); | |
console.assert(arguments.length === 1); | |
console.assert(this === theThis); | |
}, theThis); | |
console.assert(callbackCount === 1); | |
forEachMatchingItem(theStyle, theComponent, theRefName, function(style) { | |
callbackCount++; | |
console.assert(style === theStyle); | |
console.assert(arguments.length === 1); | |
console.assert(this === global); | |
}); | |
console.assert(callbackCount === 2); | |
}()); | |
;(function(){ | |
var theThis = {}; var theComponent = {}; var theRefName = {}; | |
var theStyle = {color:'red'}; | |
var callbackCount = 0; | |
var matchingStyles = forEachMatchingItem(theStyle, theComponent, theRefName); | |
console.assert(matchingStyles.length === 1); | |
forEachMatchingItem(theStyle, theComponent, theRefName, matchingStyles.push, matchingStyles); | |
console.assert(matchingStyles.length === 2); | |
forEachMatchingItem(theStyle, theComponent, theRefName, matchingStyles.push, matchingStyles); | |
console.assert(matchingStyles.length === 3); | |
}()); | |
;(function(){ | |
var theThis = {}; var theComponent = {}; var theRefName = {}; | |
var theStyle = 123; | |
var callbackCount = 0; | |
var matchingStyles = forEachMatchingItem(theStyle, theComponent, theRefName); | |
console.assert(matchingStyles.length === 1); | |
matchingStyles[0] === theStyle; | |
}()); | |
;(function(){ | |
var theStyle = {theStyle:true}; | |
var callbackCount = 0; | |
var matchingStyles = forEachMatchingItem(function(){callbackCount++;return theStyle}); | |
console.assert(callbackCount === 1); | |
console.assert(matchingStyles.length === 1); | |
matchingStyles[0] === theStyle; | |
}()); | |
}; | |
//////////////////////////////////////////////////////////////////////////////// | |
var OtherStyles = { | |
awesomeable: component => component.props.isAwesome ? OtherStyles.awesome : OtherStyles.notAwesome, | |
awesome: { | |
backgroundColor: 'yellow', | |
}, | |
notAwesome: { | |
backgroundColor: 'gray', | |
}, | |
hoverable: (component, ref) => component.state && component.state[ref + 'IsHovering'] && OtherStyles.awesome || undefined, | |
}; | |
var DemoSheet = { | |
body: [ | |
OtherStyles.awesomeable, | |
OtherStyles.hoverable, | |
], | |
}; | |
var BasicBehaviors = { | |
// hover: { | |
// onMouseEnter(event){console.log(event)}, | |
// onMouseLeave(event){console.log(event)}, | |
// }, | |
hover: (component, ref) => ( | |
component['hover ' + ref] || ( | |
component['hover ' + ref] = { | |
onMouseEnter(event){ | |
var changeSet = {}; | |
changeSet[ref + 'IsHovering'] = true; | |
component.setState(changeSet); | |
}, | |
onMouseLeave(event){ | |
var changeSet = {}; | |
changeSet[ref + 'IsHovering'] = false; | |
component.setState(changeSet); | |
}, | |
} | |
) | |
), | |
}; | |
var SheetsDemoBehaviors = { | |
body: [ | |
BasicBehaviors.hover, | |
], | |
}; | |
var SheetsDemoStyles = { | |
body: [ | |
OtherStyles.awesomeable, | |
OtherStyles.hoverable, | |
component => component.state && component.state.bodyIsHovering ? SheetsDemoStyles.bodyHover : SheetsDemoStyles.bodyNotHover, | |
{ | |
fontSize: 28, | |
borderSize: 4, | |
borderStyle: 'solid', | |
}, | |
], | |
bodyHover: { | |
borderColor: '#00FF00', | |
}, | |
bodyNotHover: { | |
borderColor: '#0000FF', | |
}, | |
}; | |
var SheetsDemo = React.createClass({ | |
statics: { | |
behaviors: [ | |
BasicBehaviors, | |
SheetsDemoBehaviors, | |
], | |
styles: [ | |
DemoSheet, | |
SheetsDemoStyles, | |
], | |
}, | |
behaviors: createMergingSelector(component => component.constructor.behaviors), | |
mergedStylesForElement: createMergingSelector(component => component.constructor.styles), | |
propsForRef(ref){ | |
var style = this.mergedStylesForElement.apply(this, arguments); | |
var behaviors = this.behaviors(ref); | |
return Object.assign({ref, style}, behaviors); | |
}, | |
render(){ | |
var ref = this.propsForRef; | |
return ( | |
<div {...ref('body')}> | |
<b {...BasicBehaviors.hover(this, 'Hello')}> | |
Hello | |
{this.state && this.state.HelloIsHovering && 'LULZ'} | |
</b> | |
World {this.state && this.state.bodyIsHovering && 'LULZ'} | |
</div> | |
); | |
}, | |
}); | |
React.renderComponent( | |
<div> | |
<SheetsDemo /> | |
<SheetsDemo isAwesome /> | |
</div>, | |
document.getElementById('SheetsDemoRoot') | |
); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hello from the future. This is REALLY hard to understand. But it's super brilliant! I love it!