Skip to content

Commit c2ec3c6

Browse files
committed
flexbox status: successy!
1 parent 010465a commit c2ec3c6

File tree

4 files changed

+120
-100
lines changed

4 files changed

+120
-100
lines changed

index.html

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,17 @@
22
<head>
33
<title>Carousel Demo for Patreon</title>
44
<style>
5+
html, body {
6+
height: 100%;
7+
}
58
body {
6-
background: linear-gradient(160deg, #009ee3 0%,#5e4e9c 100%);
79
margin: 0;
10+
background: linear-gradient(160deg, #009ee3 0%,#5e4e9c 100%);
811
}
912
#carousel {
10-
height: 100%;
13+
height: 70%;
14+
width: 80%;
15+
margin: auto;
1116
}
1217
</style>
1318
</head>

scripts/components/Carousel.jsx

Lines changed: 109 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export default React.createClass({
1818
function rangeCheck(props, propName, componentName) {
1919
if (props[propName] > props.numItems) {
2020
return new Error('Carousel must be initialized with ' +
21-
'enough items to fill every slot.')
21+
'enough items to fill every slot.');
2222
}
2323
}
2424

@@ -31,16 +31,8 @@ export default React.createClass({
3131
return error;
3232

3333
},
34-
slideDuration: React.PropTypes.number,
35-
fullscreen: React.PropTypes.bool
36-
},
37-
38-
validateSlots(props, propName, componentName) {
39-
React.PropTypes.number(props, propName, componentName);
40-
if (props[propName] > props.numItems) {
41-
return new Error('Carousel may not be initialized with more slots' +
42-
'than items')
43-
}
34+
respawnThreshold: React.PropTypes.number,
35+
slideDuration: React.PropTypes.number
4436
},
4537

4638
getDefaultProps() {
@@ -124,20 +116,22 @@ export default React.createClass({
124116
},
125117

126118
handleGenericInteraction() {
127-
this.setState({genericInteractions: this.state.genericInteractions + 1})
128-
// don't respawn if the user's first interaction is with the
129-
// 'clear' button!
130-
if ((this.state.genericInteractions + 1) % this.props.respawnThreshold === 0) {
119+
this.setState({genericInteractions: this.state.genericInteractions + 1});
120+
if (this.state.genericInteractions % this.props.respawnThreshold === 0) {
131121
CarouselActions.respawn();
132122
}
123+
event.stopPropagation();
133124
},
134125

135126
handleReset() {
136127
CarouselActions.reset(this.props.numItems);
137128
},
138129

139-
handleClear() {
130+
handleClear(event) {
140131
CarouselActions.clear();
132+
// don't let clicks on the 'clear' button bubble up to the generic
133+
// interaction handler.
134+
event.stopPropagation();
141135
},
142136

143137
slideBackward() {
@@ -184,36 +178,44 @@ export default React.createClass({
184178
// https://vimeo.com/channels/684289/116209150
185179
staticStyles: {
186180
container: {
187-
display: 'table-row' // flexbox is the modern way to build
188-
}, // this type of layout, but display: table
189-
verticalAligner: { // and friends work adequately for this
190-
display: 'table-cell', // case and enjoy ubiquitous support.
191-
verticalAlign: 'middle'
192-
},
193-
overflowConcealer: {
194-
display: 'table-cell', // 100% of container width after
195-
width: '100%', // accounting for the navigational buttons
196-
overflowX: 'hidden',
197-
overflowY: 'hidden'
181+
position: 'relative', // we'll position the reset and clear
182+
display: 'flex', // buttons relative to the outer div
183+
alignItems: 'center',
184+
height: '100%', // occupy the full height of our parent
185+
overflow: 'hidden' // rather than the natural height of the
186+
}, // tallest carousel item
187+
endCap: {
188+
flexShrink: 0,
189+
zIndex: 100
198190
},
191+
stock: {
192+
flexGrow: 1,
193+
display: 'flex',
194+
alignItems: 'center',
195+
position: 'relative' // the stock is the reference for
196+
}, // the slider and the 'game over' message
199197
slider: {
200-
position: 'relative', // later, we will calculate how much to
201-
whiteSpace: 'nowrap' // shift the slider relative to its parent
198+
flexGrow: 1,
199+
position: 'relative', // we will calculate how much to offset
200+
whiteSpace: 'nowrap' // the slider in render()
202201
},
203-
item: {
204-
display: 'inline-block'
202+
messageContainer: {
203+
position: 'absolute',
204+
top: 0,
205+
right: 0,
206+
bottom: 0,
207+
left: 0,
208+
display: 'flex',
209+
justifyContent: 'center',
210+
alignItems: 'center'
205211
},
206-
gameOverVerticalAligner: { // flexbox would have obviated the
207-
position: 'absolute', // 1 need for this non-semantic wrapper
208-
top: '50%', // 2 div and these five numbered style
209-
width: '100%' // 3 properties
210-
},
211-
gameOverText: {
212-
position: 'absolute', // 4
213-
top: '-0.5em', // 5
214-
width: '100%',
212+
message: {
215213
textAlign: 'center',
216-
fontSize: 50
214+
fontSize: 50,
215+
fontWeight: 100
216+
},
217+
item: {
218+
display: 'inline-block'
217219
},
218220
leftArrow: {
219221
width: 0,
@@ -236,8 +238,11 @@ export default React.createClass({
236238
borderLeft: '15px solid black'
237239
},
238240
buttonGroup: {
239-
textAlign: 'right'
240-
},
241+
position: 'absolute',
242+
bottom: 4,
243+
right: 4,
244+
zIndex: 100 // setting negative zIndex on the carousel items
245+
}, // breaks their onClick handlers.
241246
button: { // fake Twitter Bootstrap button
242247
display: 'inlineBlock',
243248
webkitAppearance: 'button',
@@ -253,26 +258,33 @@ export default React.createClass({
253258
},
254259

255260
render() {
256-
// suppress render until the backing store is initialized
257-
if (this.state.items === undefined) return <div />
258-
259261
const dynamicStyles = {};
260-
const itemWidth = 100 / this.props.numSlots;
261-
dynamicStyles.slider = Object.assign({},
262-
this.staticStyles.slider
263-
// {height: 0,
264-
// paddingBottom: `${itemWidth}%`}
265-
)
262+
// allow our caller to set styles on us, provided they don't
263+
// conflict with the ones we neeed.
264+
dynamicStyles.container = Object.assign({},
265+
this.props.style,
266+
this.staticStyles.container)
266267

267-
// } else if (this.state.gameOver) {
268-
// items = <div style={this.staticStyles.gameOverVerticalAligner}>
269-
// <div style={this.staticStyles.gameOverText}>
270-
// Game Over
271-
// </div>
272-
// </div>
268+
// suppress render until the backing store is initialized
269+
if (this.state.items === undefined) {
270+
return <div style={dynamicStyles.container}/>;
271+
}
272+
273+
let messageStyle;
274+
if (this.state.gameOver) {
275+
messageStyle = {transform: 'scale(1,1)',
276+
transition: 'transform 450ms cubic-bezier(.4,1.4,.4,1)'};
277+
} else {
278+
messageStyle = {transform: 'scale(0,0)',
279+
transition: 'none'};
280+
}
281+
dynamicStyles.message = Object.assign({},
282+
this.staticStyles.message,
283+
messageStyle);
273284

274285
// calculate the slider's left offset and supply an appropriate
275286
// transition: ease while sliding, snap before re-render.
287+
const itemWidth = 100 / this.props.numSlots;
276288
let slidingStyle;
277289
switch(this.state.sliding) {
278290
case this.enums.sliding.FORWARD:
@@ -284,12 +296,10 @@ export default React.createClass({
284296
transition: `left ${this.props.slideDuration}ms ease`};
285297
break;
286298
default:
287-
slidingStyle = {left: `-${itemWidth}%`,
288-
transition: 'none'};
299+
slidingStyle = {left: `-${itemWidth}%`, transition: 'none'};
289300
}
290-
Object.assign(dynamicStyles.slider, slidingStyle);
291-
292-
// get the items we need and render them.
301+
dynamicStyles.slider = Object.assign({}, this.staticStyles.slider, slidingStyle);
302+
// get the items we need
293303
dynamicStyles.item = Object.assign({},
294304
this.staticStyles.item,
295305
{width: `${itemWidth}%`});
@@ -299,10 +309,11 @@ export default React.createClass({
299309
const circularized = Immutable.Repeat(withIndices).flatten(1);
300310
const slice = circularized.slice(this.state.offsetIndex,
301311
this.state.offsetIndex + this.props.numSlots + 2);
312+
// render them
302313
let items = slice.map( ([shape, storeIndex], sliceIndex) =>
303314
<CarouselItem key={storeIndex + // a unique and
304-
Math.floor(sliceIndex / // stable key saves
305-
this.state.items.size)} // DOM operations
315+
Math.floor(sliceIndex / // stable key
316+
this.state.items.size)} // avoids redraws
306317
index={storeIndex}
307318
style={dynamicStyles.item}
308319
hp={shape.hp}
@@ -311,41 +322,45 @@ export default React.createClass({
311322
// iterables in JSX, but for now we must
312323
// convert to the built-in Array type.
313324

314-
return <div style={{position: 'relative'}}
325+
return <div style={this.staticStyles.container}
315326
onClick={this.handleGenericInteraction}>
316-
<div style={this.staticStyles.container}>
317-
<div style={this.staticStyles.verticalAligner}>
318-
<input type="button"
319-
style={this.staticStyles.leftArrow}
320-
disabled={this.state.sliding !==
321-
this.enums.sliding.STOPPED}
322-
onClick={this.slideBackward} />
327+
<div style={this.staticStyles.endCap}>
328+
<input type="button"
329+
style={this.staticStyles.leftArrow}
330+
disabled={this.state.sliding !==
331+
this.enums.sliding.STOPPED}
332+
onClick={this.slideBackward} />
333+
</div>
334+
<div style={this.staticStyles.stock}>
335+
<div style={dynamicStyles.slider}>
336+
{items}
323337
</div>
324-
<div style={this.staticStyles.overflowConcealer}>
325-
<div style={dynamicStyles.slider}>
326-
{items}
327-
</div>
328-
Game Over
329-
</div>
330-
<div style={this.staticStyles.verticalAligner}>
331-
<input type="button"
332-
style={this.staticStyles.rightArrow}
333-
disabled={this.state.sliding !==
334-
this.enums.sliding.STOPPED}
335-
onClick={this.slideForward} />
338+
<div style={this.staticStyles.messageContainer}>
339+
<span style={dynamicStyles.message}>
340+
{'Thanks for Playing!'}
341+
<br />
342+
{'April Arcus <3 Patreon'}
343+
</span>
336344
</div>
337345
</div>
338-
<div style={this.staticStyles.buttonGroup}>
346+
<div style={this.staticStyles.endCap}>
339347
<input type="button"
340-
style={this.staticStyles.button}
341-
value="Reset"
342-
onClick={this.handleReset} />
343-
<input type="button"
344-
style={this.staticStyles.button}
345-
value="Clear"
346-
disabled={this.state.gameOver}
347-
onClick={this.handleClear} />
348+
style={this.staticStyles.rightArrow}
349+
disabled={this.state.sliding !==
350+
this.enums.sliding.STOPPED}
351+
onClick={this.slideForward} />
348352
</div>
353+
<div style={this.staticStyles.buttonGroup}>
354+
<input type="button"
355+
style={this.staticStyles.button}
356+
value="Reset"
357+
onClick={this.handleReset} />
358+
<input type="button"
359+
style={this.staticStyles.button}
360+
value="Clear"
361+
disabled={this.state.gameOver}
362+
onClick={this.handleClear} />
363+
</div>
349364
</div>;
350365
}
351366
});

scripts/components/CarouselItem.jsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ export default React.createClass({
101101
generateRandomPolygonFromSeed(seed) {
102102
const rng = seedrandom(seed);
103103
const hue = Math.floor(rng() * 360);
104-
const zIndex = Math.floor(rng() * 20);
104+
const zIndex = Math.floor(rng() * 100);
105105
const irregularity = rng() * 0.8;
106106
const spikeyness = rng() * 0.6;
107107
const numVerts = Math.floor(3 + rng() * 5);
@@ -121,7 +121,8 @@ export default React.createClass({
121121
const outerStyle = Object.assign({},
122122
this.props.style,
123123
{position: 'relative',
124-
zIndex: this.state.zIndex})
124+
zIndex: this.state.zIndex
125+
})
125126

126127
let shrink;
127128
if (this.props.hp > 0) {

scripts/index.jsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,4 @@ const React = require('react'),
66
React.render(<Carousel numSlots={6}
77
numItems={8}
88
respawnThreshold={12}
9-
slideDuration={150}
10-
fullscreen={true} />, document.getElementById('carousel'));
9+
slideDuration={150} />, document.getElementById('carousel'));

0 commit comments

Comments
 (0)