Merge branch 'contentIntegration' into react

This commit is contained in:
Cássio Zen
2016-08-01 16:09:46 -03:00
44 changed files with 734 additions and 200 deletions

11
src/components/Cards.js Normal file
View File

@ -0,0 +1,11 @@
import UnknownCard from './Cards/UnknownCard';
import ImageCard from './Cards/ImageCard';
import AlltypeCard from './Cards/AlltypeCard';
const Cards = {
_unknown: UnknownCard,
image: ImageCard,
alltype: AlltypeCard
};
export default Cards;

View File

@ -0,0 +1,5 @@
.cardContent {
white-space: nowrap;
text-align: center;
font-weight: 500;
}

View File

@ -0,0 +1,81 @@
import React, { PropTypes } from 'react';
import { Card } from '../UI';
import ScaledLine from './ScaledLine';
import styles from './AlltypeCard.css';
export default class AlltypeCard extends React.Component {
// Based on the Slabtype Algorithm by Erik Loyer
// http://erikloyer.com/index.php/blog/the_slabtype_algorithm_part_1_background/
renderInscription(inscription) {
const idealCharPerLine = 22;
// segment the text into lines
const words = inscription.split(' ');
let preText, postText, finalText;
let preDiff, postDiff;
let wordIndex = 0;
const lineText = [];
// while we still have words left, build the next line
while (wordIndex < words.length) {
postText = '';
// build two strings (preText and postText) word by word, with one
// string always one word behind the other, until
// the length of one string is less than the ideal number of characters
// per line, while the length of the other is greater than that ideal
while (postText.length < idealCharPerLine) {
preText = postText;
postText += words[wordIndex] + ' ';
wordIndex++;
if (wordIndex >= words.length) {
break;
}
}
// calculate the character difference between the two strings and the
// ideal number of characters per line
preDiff = idealCharPerLine - preText.length;
postDiff = postText.length - idealCharPerLine;
// if the smaller string is closer to the length of the ideal than
// the longer string, and doesnt contain just a single space, then
// use that one for the line
if ((preDiff < postDiff) && (preText.length > 2)) {
finalText = preText;
wordIndex--;
// otherwise, use the longer string for the line
} else {
finalText = postText;
}
lineText.push(finalText.substr(0, finalText.length - 1));
}
return lineText.map(text => (
<ScaledLine key={text.trim().replace(/[^a-z0-9]+/gi, '-')} toWidth={216}>
{text}
</ScaledLine>
));
}
render() {
const { onClick, text } = this.props;
return (
<Card onClick={onClick}>
<div className={styles.cardContent}>{this.renderInscription(text)}</div>
</Card>
);
}
}
AlltypeCard.propTypes = {
onClick: PropTypes.func,
text: PropTypes.string.isRequired
};
AlltypeCard.defaultProps = {
onClick: function() {},
};

View File

@ -0,0 +1,18 @@
.root {
cursor: pointer;
}
.root h1 {
font-size: 17px;
}
.root h2 {
font-weight: 400;
font-size: 15px;
}
.root p {
color: #555;
font-size: 14px;
margin-top: 5px;
}

View File

@ -0,0 +1,31 @@
import React, { PropTypes } from 'react';
import { Card } from '../UI';
import styles from './ImageCard.css';
export default class ImageCard extends React.Component {
render() {
const { onClick, onImageLoaded, image, text, description } = this.props;
return (
<Card onClick={onClick} className={styles.root}>
<img src={image} onLoad={onImageLoaded} />
<h1>{text}</h1>
{description ? <p>{description}</p> : null}
</Card>
);
}
}
ImageCard.propTypes = {
image: PropTypes.string,
onClick: PropTypes.func,
onImageLoaded: PropTypes.func,
text: PropTypes.string.isRequired,
description: PropTypes.string
};
ImageCard.defaultProps = {
onClick: function() {},
onImageLoaded: function() {}
};

View File

@ -0,0 +1,39 @@
import React, { PropTypes } from 'react';
export default class ScaledLine extends React.Component {
constructor(props) {
super(props);
this._content = null;
this.state = {
ratio: 1,
};
}
componentDidMount() {
const actualContent = this._content.children[0];
this.setState({
ratio: this.props.toWidth / actualContent.offsetWidth,
});
}
render() {
const { ratio } = this.state;
const { children } = this.props;
const styles = {
fontSize: ratio.toFixed(3) + 'em'
};
return (
<div ref={(c) => this._content = c} style={styles}>
<span>{children}</span>
</div>
);
}
}
ScaledLine.propTypes = {
children: PropTypes.node.isRequired,
toWidth: PropTypes.number.isRequired
};

View File

@ -0,0 +1,15 @@
import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { Card } from '../UI';
export default function UnknownCard({ collection }) {
return (
<Card>
<p>No card of type {collection.getIn(['card', 'type'])}.</p>
</Card>
);
}
UnknownCard.propTypes = {
collection: ImmutablePropTypes.map,
};

View File

@ -1,18 +1,91 @@
import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { Link } from 'react-router';
import Bricks from 'bricks.js';
import history from '../routing/history';
import Cards from './Cards';
import _ from 'lodash';
export default function EntryListing({ collection, entries }) {
const name = collection.get('name');
return <div>
<h2>Listing entries!</h2>
{entries.map((entry) => {
const path = `/collections/${name}/entries/${entry.get('slug')}`;
return <Link key={entry.get('slug')} to={path}>
<h3>{entry.getIn(['data', 'title'])}</h3>
</Link>;
})}
</div>;
export default class EntryListing extends React.Component {
constructor(props) {
super(props);
this.bricksInstance = null;
this.bricksConfig = {
packed: 'data-packed',
sizes: [
{ columns: 1, gutter: 15 },
{ mq: '495px', columns: 2, gutter: 15 },
{ mq: '750px', columns: 3, gutter: 15 },
{ mq: '1005px', columns: 4, gutter: 15 },
{ mq: '1260px', columns: 5, gutter: 15 },
{ mq: '1515px', columns: 6, gutter: 15 },
{ mq: '1770px', columns: 7, gutter: 15 },
]
};
this.updateBricks = _.throttle(this.updateBricks.bind(this), 30);
}
componentDidMount() {
this.bricksInstance = Bricks({
container: this._entries,
packed: this.bricksConfig.packed,
sizes: this.bricksConfig.sizes
});
this.bricksInstance.resize(true);
if (this.props.entries && this.props.entries.size > 0) {
setTimeout(() => {
this.bricksInstance.pack();
}, 0);
}
}
componentDidUpdate(prevProps) {
if ((prevProps.entries === undefined || prevProps.entries.size === 0) && this.props.entries.size === 0) {
return;
}
this.bricksInstance.pack();
}
componengWillUnmount() {
this.bricksInstance.resize(false);
}
updateBricks() {
this.bricksInstance.pack();
}
cardFor(collection, entry, link) {
//const { entry, getMedia, onChange, onAddMedia, onRemoveMedia } = this.props;
const card = Cards[collection.getIn(['card', 'type'])] || Cards._unknown;
return React.createElement(card, {
key: entry.get('slug'),
collection: collection,
onClick: history.push.bind(this, link),
onImageLoaded: this.updateBricks,
text: entry.getIn(['data', collection.getIn(['card', 'text'])]),
description: entry.getIn(['data', collection.getIn(['card', 'description'])]),
image: entry.getIn(['data', collection.getIn(['card', 'image'])]),
});
}
render() {
const { collection, entries } = this.props;
const name = collection.get('name');
return <div>
<h1>Listing {name}</h1>
<div ref={(c) => this._entries = c}>
{entries.map((entry) => {
const path = `/collections/${name}/entries/${entry.get('slug')}`;
return this.cardFor(collection, entry, path);
})}
</div>
</div>;
}
}
EntryListing.propTypes = {

View File

@ -4,10 +4,12 @@
composes: rounded from "../theme.css";
composes: depth from "../theme.css";
overflow: hidden;
width: 240px;
}
.card > *:not(iframe, video, img, header, footer) {
margin: 0 10px;
margin-left: 10px;
margin-right: 10px;
}
.card > *:not(iframe, video, img, header, footer):first-child {

View File

@ -1,6 +1,6 @@
import React from 'react';
import styles from './Card.css';
export default function Card({ style, className = '', children }) {
return <div className={`${styles.card} ${className}`} style={style}>{children}</div>;
export default function Card({ style, className = '', onClick, children }) {
return <div className={`${styles.card} ${className}`} style={style} onClick={onClick}>{children}</div>;
}

View File

@ -1,7 +1,7 @@
:root {
--defaultColor: #333;
--backgroundColor: #fff;
--shadowColor: rgba(0, 0, 0, 0.25);
--shadowColor: rgba(0, 0, 0, 0.117647);
--successColor: #1c7;
--warningColor: #fa0;
--errorColor: #f52;
@ -12,15 +12,14 @@
}
.container {
margin: 10px;
color: var(--defaultColor);
background-color: var(--backgroundColor);
}
.rounded {
border-radius: 6px;
border-radius: 2px;
}
.depth {
box-shadow: 0px 1px 2px 0px var(--shadowColor);
box-shadow: var(--shadowColor) 0px 1px 6px, var(--shadowColor) 0px 1px 4px;
}

View File

@ -1,11 +1,11 @@
import UnknownControl from './widgets/UnknownControl';
import UnknownPreview from './widgets/UnknownPreview';
import StringControl from './widgets/StringControl';
import StringPreview from './widgets/StringPreview';
import MarkdownControl from './widgets/MarkdownControl';
import MarkdownPreview from './widgets/MarkdownPreview';
import ImageControl from './widgets/ImageControl';
import ImagePreview from './widgets/ImagePreview';
import UnknownControl from './Widgets/UnknownControl';
import UnknownPreview from './Widgets/UnknownPreview';
import StringControl from './Widgets/StringControl';
import StringPreview from './Widgets/StringPreview';
import MarkdownControl from './Widgets/MarkdownControl';
import MarkdownPreview from './Widgets/MarkdownPreview';
import ImageControl from './Widgets/ImageControl';
import ImagePreview from './Widgets/ImagePreview';
const Widgets = {

View File

@ -53,7 +53,7 @@ export default class ImageControl extends React.Component {
if (file) {
const mediaProxy = new MediaProxy(file.name, file);
this.props.onAddMedia(mediaProxy);
this.props.onChange(mediaProxy.uri);
this.props.onChange(mediaProxy.path);
} else {
this.props.onChange(null);
}
@ -63,7 +63,7 @@ export default class ImageControl extends React.Component {
renderImageName() {
if (!this.props.value) return null;
if (this.value instanceof MediaProxy) {
return truncateMiddle(this.props.value.uri, MAX_DISPLAY_LENGTH);
return truncateMiddle(this.props.value.path, MAX_DISPLAY_LENGTH);
} else {
return truncateMiddle(this.props.value, MAX_DISPLAY_LENGTH);
}

View File

@ -3,7 +3,6 @@ import { Card } from '../UI';
import { storiesOf } from '@kadira/storybook';
const styles = {
fixedWidth: { width: 280 },
footer: {
color: '#aaa',
backgroundColor: '#555',
@ -11,40 +10,33 @@ const styles = {
marginTop: 5,
padding: 10
}
}
};
storiesOf('Card', module)
.add('Default View', () => (
<div style={styles.fixedWidth}>
<Card>
<h1>A Card</h1>
<h2>Subtitle</h2>
<p>
Margins are applied to all elements inside a card. <br/>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. lobortis vel. Nulla porttitor enim at tellus eget
malesuada eleifend. Nunc tellus turpis, tincidunt sed felis facilisis, lacinia condimentum quam. Cras quis
tortor fermentum, aliquam tortor eu, consequat ligula. Nulla eget nulla act odio varius ullamcorper turpis.
In consequat egestas nulla condimentum faucibus. Donec scelerisque convallis est nec fringila. Suspendisse
non lorem non erat congue consequat.
</p>
</Card>
</div>
<Card>
<h1>A Card</h1>
<h2>Subtitle</h2>
<p>
Margins are applied to all elements inside a card. <br/>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. lobortis vel. Nulla porttitor enim at tellus eget
malesuada eleifend. Nunc tellus turpis, tincidunt sed felis facilisis, lacinia condimentum quam. Cras quis
tortor fermentum, aliquam tortor eu, consequat ligula. Nulla eget nulla act odio varius ullamcorper turpis.
In consequat egestas nulla condimentum faucibus. Donec scelerisque convallis est nec fringila. Suspendisse
non lorem non erat congue consequat.
</p>
</Card>
)).add('Full width content', () => (
<div style={styles.fixedWidth}>
<Card>
<img src="https://i.ytimg.com/vi/tntOCGkgt98/maxresdefault.jpg" />
<h1>Card & cat</h1>
<p>Media Elements such as video, img (and iFrame for embeds) don't have margin</p>
</Card>
</div>
<Card>
<img src="https://i.ytimg.com/vi/tntOCGkgt98/maxresdefault.jpg" />
<h1>Card & cat</h1>
<p>Media Elements such as video, img (and iFrame for embeds) don't have margin</p>
</Card>
)).add('Footer', () => (
<div style={styles.fixedWidth}>
<Card>
<img src="http://www.top13.net/wp-content/uploads/2015/10/perfectly-timed-funny-cat-pictures-5.jpg" />
<h1>Now with footer.</h1>
<p>header and footer elements are also not subject to margin</p>
<footer style={styles.footer}>&copy; Thousand Cats Corp</footer>
</Card>
</div>
<Card>
<img src="http://www.top13.net/wp-content/uploads/2015/10/perfectly-timed-funny-cat-pictures-5.jpg" />
<h1>Now with footer.</h1>
<p>header and footer elements are also not subject to margin</p>
<footer style={styles.footer}>&copy; Thousand Cats Corp</footer>
</Card>
))