Bug 1514018 - Hero component refinement (WIP) (#4653)
- enforce image aspect ratios - wire up links & add interaction states (active/focus/hover) - generally tighten up styling - set count of cards based on remote layout definition - enforce character counts
This commit is contained in:
Родитель
abb7a49a25
Коммит
915ef9df72
|
@ -18,6 +18,7 @@ export class _DiscoveryStreamBase extends React.PureComponent {
|
|||
return (<CardGrid feed={component.feed} />);
|
||||
case "Hero":
|
||||
return (<Hero
|
||||
title={component.header.title}
|
||||
feed={component.feed}
|
||||
style={component.properties.style}
|
||||
items={component.properties.items} />);
|
||||
|
|
|
@ -3,14 +3,16 @@ import React from "react";
|
|||
export class DSCard extends React.PureComponent {
|
||||
render() {
|
||||
return (
|
||||
<div className="ds-card">
|
||||
<img src={this.props.image_src} />
|
||||
<a href={this.props.url} className="ds-card">
|
||||
<div className="img-wrapper">
|
||||
<div className="img" style={{backgroundImage: `url(${this.props.image_src}`}} />
|
||||
</div>
|
||||
<div className="meta">
|
||||
<header>{this.props.title}</header>
|
||||
<p>{this.props.excerpt}</p>
|
||||
<p>{this.props.source}</p>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,24 @@
|
|||
.ds-card {
|
||||
img {
|
||||
border: 5px solid transparent;
|
||||
|
||||
&:hover {
|
||||
border: 5px solid $grey-30;
|
||||
box-shadow: 0 1px 4px $grey-30;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.img-wrapper {
|
||||
width: 100%;
|
||||
border: 0.5px solid $black-10;
|
||||
box-sizing: border-box;
|
||||
border-radius: 4px;
|
||||
margin: 0 0 12px;
|
||||
}
|
||||
|
||||
.img {
|
||||
height: 0;
|
||||
padding-top: 50%; // 2:1 aspect ratio
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.meta {
|
||||
|
@ -14,11 +29,20 @@
|
|||
line-height: 24px;
|
||||
font-size: 17px;
|
||||
color: $grey-90;
|
||||
|
||||
&:hover {
|
||||
color: $blue-60;
|
||||
}
|
||||
|
||||
&:active {
|
||||
color: $blue-70;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
color: $grey-50;
|
||||
margin: 8px 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,30 +14,35 @@ export class _Hero extends React.PureComponent {
|
|||
}
|
||||
|
||||
let [heroRec, ...otherRecs] = feed.data.recommendations;
|
||||
let truncateText = (text, cap) => `${text.substring(0, cap)}${text.length > cap ? `...` : ``}`;
|
||||
|
||||
// TODO: Let this count be determined by the endpoint
|
||||
let cards = otherRecs.slice(1, 5).map((rec, index) => (
|
||||
let cards = otherRecs.slice(1, this.props.items).map((rec, index) => (
|
||||
<DSCard
|
||||
key={`dscard-${index}`}
|
||||
image_src={rec.image_src}
|
||||
title={rec.title}
|
||||
excerpt={rec.excerpt}
|
||||
source="TODO: SOURCE" />
|
||||
title={truncateText(rec.title, 44)}
|
||||
url={rec.url}
|
||||
source={truncateText(`TODO: SOURCE`, 22)} />
|
||||
));
|
||||
|
||||
return (
|
||||
<div className={`ds-hero ds-hero-${this.props.style}`}>
|
||||
<div className="wrapper">
|
||||
<img src={heroRec.image_src} />
|
||||
<div className="meta">
|
||||
<header>{heroRec.title}</header>
|
||||
<p>{heroRec.excerpt}</p>
|
||||
<p>TODO: SOURCE</p>
|
||||
<div>
|
||||
<div className="ds-header">{this.props.title}</div>
|
||||
<div className={`ds-hero ds-hero-${this.props.style}`}>
|
||||
<a href={heroRec.url} className="wrapper">
|
||||
<div className="img-wrapper">
|
||||
<div className="img" style={{backgroundImage: `url(${heroRec.image_src})`}} />
|
||||
</div>
|
||||
<div className="meta">
|
||||
<header>{truncateText(heroRec.title, 28)}</header>
|
||||
<p>{truncateText(heroRec.excerpt, 114)}</p>
|
||||
<p>{truncateText(`TODO: SOURCE`, 22)}</p>
|
||||
</div>
|
||||
</a>
|
||||
<div className="cards">
|
||||
{ cards }
|
||||
</div>
|
||||
</div>
|
||||
<div className="cards">
|
||||
{ cards }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,14 +1,63 @@
|
|||
.ds-header {
|
||||
font-size: 17px;
|
||||
line-height: 24px;
|
||||
color: $grey-90;
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
.ds-hero {
|
||||
.img {
|
||||
border: 0.5px solid $black-10;
|
||||
box-sizing: border-box;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.ds-card {
|
||||
border: 0;
|
||||
|
||||
&:hover {
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.meta {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// "1/3 width layout" (aka "Mobile First")
|
||||
.wrapper {
|
||||
img {
|
||||
color: $grey-50;
|
||||
margin: 18px 0;
|
||||
padding: 20px 0;
|
||||
border-top: 1px solid transparentize($grey-40, 0.64);
|
||||
border-bottom: 1px solid transparentize($grey-40, 0.64);
|
||||
|
||||
.img-wrapper {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.img {
|
||||
height: 0;
|
||||
padding-top: 50%; // 2:1 aspect ratio
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
background-color: $grey-30;
|
||||
}
|
||||
|
||||
.meta {
|
||||
header {
|
||||
font-size: 22px;
|
||||
color: $grey-90;
|
||||
|
||||
&:hover {
|
||||
color: $blue-60;
|
||||
}
|
||||
|
||||
&:active {
|
||||
color: $blue-70;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
|
@ -18,60 +67,51 @@
|
|||
}
|
||||
|
||||
// "2/3 width layout"
|
||||
.column-5 &,
|
||||
.column-6 &,
|
||||
.column-7 &,
|
||||
.column-8 & {
|
||||
.ds-column-5 &,
|
||||
.ds-column-6 &,
|
||||
.ds-column-7 &,
|
||||
.ds-column-8 & {
|
||||
.wrapper {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
grid-column-gap: 24px;
|
||||
|
||||
img {
|
||||
width: 50%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.meta {
|
||||
width: 50%;
|
||||
.img {
|
||||
height: 0;
|
||||
padding-top: 100%; // 1:1 aspect ratio
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.cards {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.ds-card {
|
||||
width: 50%;
|
||||
padding: 12px;
|
||||
|
||||
&:nth-child(odd) {
|
||||
padding: 12px 12px 12px 0;
|
||||
}
|
||||
|
||||
&:nth-child(even) {
|
||||
padding: 12px 0 12px 12px;
|
||||
}
|
||||
}
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
grid-column-gap: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
// "Full width layout"
|
||||
.column-9 &,
|
||||
.column-10 &,
|
||||
.column-11 &,
|
||||
.column-12 & {
|
||||
.ds-column-9 &,
|
||||
.ds-column-10 &,
|
||||
.ds-column-11 &,
|
||||
.ds-column-12 & {
|
||||
.wrapper {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
flex-direction: row-reverse;
|
||||
|
||||
img {
|
||||
.img-wrapper {
|
||||
width: 67%;
|
||||
}
|
||||
|
||||
.img {
|
||||
height: 0;
|
||||
padding-top: 50%; // 2:1 aspect ratio
|
||||
}
|
||||
|
||||
.meta {
|
||||
width: 33%;
|
||||
padding: 24px;
|
||||
padding: 0 24px 0 0;
|
||||
|
||||
header {
|
||||
font-size: 22px;
|
||||
|
@ -84,24 +124,9 @@
|
|||
}
|
||||
|
||||
.cards {
|
||||
display: flex;
|
||||
|
||||
.ds-card {
|
||||
width: 25%;
|
||||
padding: 12px;
|
||||
|
||||
.meta {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
padding: 12px 12px 12px 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
padding: 12px 0 12px 12px;
|
||||
}
|
||||
}
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
grid-column-gap: 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче