Skip to content

Instantly share code, notes, and snippets.

@keul
Created October 22, 2017 22:03
Show Gist options
  • Save keul/4e1632d056c2d4b1a056f6d15735db55 to your computer and use it in GitHub Desktop.
Save keul/4e1632d056c2d4b1a056f6d15735db55 to your computer and use it in GitHub Desktop.
RLdXEE
<div class="main dark-bk">
<div class="grid-container full-size light-bk fontf">
<div class="grid-x margin-1 show-for-small-only">
<div class="cell">
<h1 class="main-title upper">
Yoox<br>
Net-A-Porter<br>
Group
</h1>
</div>
</div>
<div class="grid-x">
<div class="cell">
<picture>
<source media="(min-width: 1024px)" srcset="https://media.yoox.biz/ytos/resources/FEDTEST/images/man-yellow-jacket/man-yellow-jacket-2048w.jpg">
<source media="(min-width: 640px)" srcset="https://media.yoox.biz/ytos/resources/FEDTEST/images/man-yellow-jacket/man-yellow-jacket-1024w.jpg">
<source media="(min-width: 320px)" srcset="https://media.yoox.biz/ytos/resources/FEDTEST/images/man-yellow-jacket/man-yellow-jacket-640w.jpg">
<img src="https://media.yoox.biz/ytos/resources/FEDTEST/images/man-yellow-jacket/man-yellow-jacket-2048w.jpg" alt="">
</picture>
</div>
</div>
<div class="grid-x margin-1">
<div class="cell hide-for-small-only">
<h1 class="main-title upper">
Yoox<br>
Net-A-Porter<br>
Group
</h1>
</div>
<div class="cell intro">
<p class="lead">
YOOX NET-A-PORTER GROUP is the <strong>world’s leading online luxury fashion retailer</strong>. The Group is a Global company with Anglo-Italian roots, the result of a game-changing merger, which in October 2015 brought together YOOX GROUP and
THE NET-A-PORTER GROUP, two companies that <strong>have revolutionized the luxury fashion industry</strong> since their birth in 2000.
</p>
</div>
</div>
<div id="news" class="grid-x callout margin-1 plain-bk">
<div class="cell text-center">
<i class="fa fa-circle-o-notch fa-spin fa-4x fa-fw" aria-hidden="true"></i><br>
Loading news&hellip;
</div>
</div>
<div class="grid-x margin-1 align-middle">
<div class="large-4 cell">
<label class="nl-intro upper" for="newsletter">Newsletter</label>
</div>
<div class="large-8 cell" id="newsletter-section">
<form class="newsletter" action="">
<input type="email" id="newsletter" name="newsletter" placeholder="work@ynap.com" />
<button class="button selectable-input">Subscribe</button>
</form>
</div>
</div>
<footer class="footer grid-x margin-0 align-middle">
<div class="cell">
<p class="copyright text-center upper">
Copyright &copy; 2000-2017 Yoox Net-A-Porter Group
</p>
</div>
</footer>
</div>
</div>
/**
* News App
*/
const preparePages = (current, total) => {
const pages = [];
for (let i = 0; i < total; i++) {
pages.push(i + 1);
}
let start = 0;
if (current >= 3) {
start = current + 2 >= total ? total - 5 : current - 3;
}
return pages.slice(start, start + 5);
};
const NewsNavigation = props => {
const { current, total, loadNews } = props;
const pages = preparePages(current, total);
return (
<ul className="pagination" role="navigation" aria-label="Pagination">
{pages.map(item => (
<li key={item} className={current === item ? "current" : ""}>
{current == item ? (
item
) : (
<a
onClick={ev => {
ev.preventDefault();
loadNews(item);
}}
aria-label={item}
>
{item}
</a>
)}
</li>
))}
</ul>
);
};
class NewsApp extends React.Component {
constructor(props) {
super(props);
this.state = {
newsItem: null,
currentIndex: 1,
total: 10, // BBB: API does not return number of items, let say we know we have only 10 news
loading: true
};
this.loadNews = this.loadNews.bind(this);
this.fetchNews = this.fetchNews.bind(this);
}
componentDidMount() {
this.fetchNews(this.state.currentIndex);
}
fetchNews(id) {
this.setState({ loading: true });
fetch(`https://jsonplaceholder.typicode.com/posts/${id}`)
.then(response => response.json())
.then(json => {
this.setState({ newsItem: json, currentIndex: id, loading: false });
});
}
loadNews(index) {
this.setState({ currentIndex: index }, () => {
this.fetchNews(this.state.currentIndex);
});
}
render() {
const { newsItem, total, currentIndex } = this.state;
return (
<div className="grid-x plain-bk">
<div className="small-12 cell">
<NewsNavigation
total={total}
current={currentIndex}
loadNews={this.loadNews}
/>
</div>
<div className="large-4 cell">
<h2 className="main-section-title upper">
Lorem ipsum dolor sit amet, consectetur adipiscing elit
</h2>
</div>
<div className="large-8 cell">
<div className="grid-x">
<div
className={
this.state.loading
? "cell page-content fetching"
: "cell page-content"
}
>
{newsItem
? new Array(5)
.fill(newsItem.body, 0, 5)
.map((text, index) => <p key={index}>{text}</p>)
: null}
</div>
</div>
</div>
</div>
);
}
}
ReactDOM.render(<NewsApp />, document.querySelector("#news"));
/**
* Newsletter App
*/
const NewsletterConfirmed = props => {
const revealStyle = {
display: "block"
};
return (
<div className="reveal-overlay" style={{ display: "block" }}>
<div
className="reveal"
id="subscriptionMessage"
role="dialog"
aria-hidden="true"
tabIndex="-1"
style={revealStyle}
data-events="resize"
>
<h1>You have been subscribed</h1>
<p className="lead">Welcome to the Yoox Net-A-Porter Newsletter.</p>
<p>
You will receive an email to confirm your subscription and a link to
subscription options.
</p>
<button
className="close-button"
data-close=""
aria-label="Close"
type="button"
onClick={props.onClose}
>
<span aria-hidden="true">×</span>
</button>
</div>
</div>
);
};
class NewsletterApp extends React.Component {
constructor(props) {
super(props);
this.email = null;
this.state = {
subscribed: false,
overlayOpened: false,
invalidEmail: false,
};
this.handlerSubscription = this.handlerSubscription.bind(this);
this.checkValidity = this.checkValidity.bind(this);
}
handlerSubscription(ev) {
ev.preventDefault();
this.setState({ subscribed: false, overlayOpened: true });
}
checkValidity(ev) {
if (ev.target.value && !ev.target.checkValidity()) {
this.setState({ invalidEmail: true })
} else {
this.setState({ invalidEmail: false })
}
}
render() {
return this.state.subscribed ? (
<div className="subscription-done">
Well done! You already subscribed our newsletter!
</div>
) : (
<form className="newsletter" action="">
<input
type="email"
id="newsletter"
name="newsletter"
autocomplete="off"
placeholder="work@ynap.com"
ref={input => {
this.email = input;
}}
onChange={this.checkValidity}
className={this.state.invalidEmail ? 'error border-1' : 'border-1'}
/>
<button
className="button selectable-input border-1"
onClick={this.handlerSubscription}
disabled={!this.email || !this.email.value || this.state.invalidEmail ? true : false}
>
Subscribe
</button>
{this.state.overlayOpened ? (
<NewsletterConfirmed
onClose={() =>
this.setState({ overlayOpened: false, subscribed: true })}
/>
) : (
""
)}
</form>
);
}
}
ReactDOM.render(
<NewsletterApp />,
document.querySelector("#newsletter-section")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.0.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.0.0/umd/react-dom.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fetch/2.0.3/fetch.min.js"></script>
$color-darker: #333;
$color-dark: #666;
$color-lightest: #FFF;
$color-plain-bk: #F2F2F2;
$color-gradient-a: #CCC;
$color-gradient-b: #FFF;
$color-error: #E96565;
$color-fetching: #c0c0c0;
body,
h1,
h2,
h3,
h4,
h5,
h6 {
font-family: Open Sans;
margin-bottom: 1rem;
line-height: 1.1;
}
@media print, screen and (min-width: 40em) {
h2 {
font-size: 2.2rem;
}
}
.button[disabled]:hover {
color: black;
}
.dark-bk {
background-color: $color-dark;
}
.light-bk {
background-color: $color-lightest;
}
.plain-bk {
background-color: $color-plain-bk;
}
.fetching {
color: $color-fetching;
}
.full-size {
padding-left: 0;
padding-right: 0;
}
.padding-1 {
padding-left: 1em;
padding-right: 1em;
}
.margin-1 {
margin-left: 2em;
margin-right: 2em;
}
.margin-0 {
margin-left: 0;
margin-right: 0;
}
.main-title {
margin-top: 1.5rem;
margin-bottom: 1.5rem;
}
.intro {
padding-bottom: 1.5em;
}
.border-1,
.border-1:focus {
border: 1px solid $color-darker;
}
.upper {
text-transform: uppercase;
}
.lead {
font-size: 1.1rem;
}
.page-content {
@media (min-width: 640px) {
column-count: 2;
}
}
.main-section-title {
margin-right: 1rem;
}
.newsletter {
display: flex;
align-items: center;
.error {
background-color: $color-error;
}
input,
button {
margin-bottom: 0;
}
button {
border-left: 0;
padding-top: 0.8em;
padding-bottom: 0.8em;
}
}
.nl-intro {
font-size: 200%;
}
.main {
.pagination a,
.selectable-input {
background: $color-gradient-a;
background: linear-gradient($color-gradient-b, $color-gradient-a);
color: black;
&:hover,
&:selected,
&:focus {
background: linear-gradient($color-gradient-a, $color-gradient-b);
}
}
.pagination .current {
background: linear-gradient($color-gradient-a, $color-gradient-b);
color: black;
}
}
.pagination li {
display: inline-block;
margin-right: 0.7em;
border: 1px solid $color-darker;
}
.footer {
background-color: $color-darker;
color: white;
font-size: 80%;
font-weight: bold;
margin-top: 1em;
}
.copyright {
margin-top: 1rem;
margin-bottom: 1rem;
}
.loading {
text-align: center;
color: $color-darker;
}
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
<link href="https://cdnjs.cloudflare.com/ajax/libs/foundation/6.4.3/css/foundation.min.css" rel="stylesheet" />
<link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment