Merge branch 'contentIntegration' into react
This commit is contained in:
11
src/components/Cards.js
Normal file
11
src/components/Cards.js
Normal 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;
|
5
src/components/Cards/AlltypeCard.css
Normal file
5
src/components/Cards/AlltypeCard.css
Normal file
@ -0,0 +1,5 @@
|
||||
.cardContent {
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
font-weight: 500;
|
||||
}
|
81
src/components/Cards/AlltypeCard.js
Normal file
81
src/components/Cards/AlltypeCard.js
Normal 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 doesn’t 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() {},
|
||||
};
|
18
src/components/Cards/ImageCard.css
Normal file
18
src/components/Cards/ImageCard.css
Normal 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;
|
||||
}
|
31
src/components/Cards/ImageCard.js
Normal file
31
src/components/Cards/ImageCard.js
Normal 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() {}
|
||||
};
|
39
src/components/Cards/ScaledLine.js
Normal file
39
src/components/Cards/ScaledLine.js
Normal 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
|
||||
};
|
15
src/components/Cards/UnknownCard.js
Normal file
15
src/components/Cards/UnknownCard.js
Normal 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,
|
||||
};
|
@ -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 = {
|
||||
|
@ -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 {
|
||||
|
@ -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>;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 = {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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}>© 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}>© Thousand Cats Corp</footer>
|
||||
</Card>
|
||||
))
|
||||
|
Reference in New Issue
Block a user