* added glyphs

* updated other assets

* initial style updates

* initial detailed card updates

* fixed mobile spacing

* final desktop layout updates

* made btn-tag smaller on moderation entry

* layout updates

* testing bookmark feature

* go away button with no link

* optimized svgs

* moderation updates

* accidentally deleted bookmark icon

* style, a11y tweaks

* type & bug fixes

* pre-code review updates

* header padding fix

* updates

* a11y removal

* more updates

* let's see if this fixes it

* style updates

* Update components/project-card/project-card-detailed.scss

Co-Authored-By: Mavis Ou <mmmavis@users.noreply.github.com>
This commit is contained in:
Youri 2019-08-21 15:20:32 -04:00 коммит произвёл GitHub
Родитель 53e567b858
Коммит 5b518cddd9
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
28 изменённых файлов: 426 добавлений и 163 удалений

Просмотреть файл

@ -0,0 +1,3 @@
<svg width="16" height="14" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 14">
<path d="M12.57 8.57v2.86c0 .707-.25 1.313-.753 1.816A2.478 2.478 0 0 1 10 14H2.57c-.707 0-1.313-.25-1.816-.754A2.474 2.474 0 0 1 0 11.43V4c0-.708.25-1.314.754-1.817a2.478 2.478 0 0 1 1.817-.754h6.29c.087 0 .156.02.21.08.053.05.08.12.08.2v.57c0 .083-.027.15-.08.205a.279.279 0 0 1-.206.08H2.57c-.39 0-.728.14-1.007.42-.28.28-.42.616-.42 1.01v7.43c0 .39.14.727.42 1.007.28.28.616.42 1.008.42H10c.393 0 .73-.14 1.01-.42.28-.28.42-.617.42-1.01v-2.85a.274.274 0 0 1 .284-.285h.572c.083 0 .152.026.205.08.06.054.08.122.08.205zM16 .858V5.43a.549.549 0 0 1-.17.4.549.549 0 0 1-.4.17.55.55 0 0 1-.403-.17l-1.572-1.57-5.82 5.82a.286.286 0 0 1-.416 0L6.202 9.062a.28.28 0 0 1-.09-.205.28.28 0 0 1 .09-.205l5.822-5.822-1.572-1.57a.554.554 0 0 1-.17-.403c0-.155.057-.29.17-.402a.55.55 0 0 1 .402-.17h4.572c.15 0 .284.057.4.17.11.113.17.247.17.402z" fill="#fff" fill-rule="evenodd"/>
</svg>

После

Ширина:  |  Высота:  |  Размер: 966 B

Просмотреть файл

@ -1 +1,3 @@
<svg height="24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="m20 4-16 16m0-16 16 16" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/></svg> <svg height="24" width="24" xmlns="http://www.w3.org/2000/svg">
<path d="M20 4L4 20M4 4l16 16" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/>
</svg>

До

Ширина:  |  Высота:  |  Размер: 193 B

После

Ширина:  |  Высота:  |  Размер: 198 B

Просмотреть файл

@ -0,0 +1,6 @@
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(1 1)" fill="none" fill-rule="evenodd">
<circle stroke="#0d10bf" stroke-width="2" cx="18.5" cy="18.5" r="18.5"/>
<path d="M27.262 15.57c0-.198 0-.394-.014-.589a9.205 9.205 0 0 0 2.252-2.353 8.94 8.94 0 0 1-2.592.716 4.56 4.56 0 0 0 1.985-2.515 9.008 9.008 0 0 1-2.867 1.104 4.496 4.496 0 0 0-6.386-.198 4.572 4.572 0 0 0-1.306 4.344A12.79 12.79 0 0 1 9.03 11.33a4.568 4.568 0 0 0 1.398 6.067 4.456 4.456 0 0 1-2.049-.568v.058A4.54 4.54 0 0 0 12 21.342a4.46 4.46 0 0 1-2.037.078 4.522 4.522 0 0 0 4.216 3.157 9.015 9.015 0 0 1-6.68 1.885 12.717 12.717 0 0 0 6.919 2.038c8.302.003 12.842-6.924 12.842-12.93z" fill="#0d10bf"/>
</g>
</svg>

После

Ширина:  |  Высота:  |  Размер: 747 B

Просмотреть файл

@ -0,0 +1,6 @@
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(1 1)" fill="none" fill-rule="evenodd">
<circle stroke="#000" stroke-width="2" cx="18.5" cy="18.5" r="18.5"/>
<path d="M27.262 15.57c0-.198 0-.394-.014-.589a9.205 9.205 0 0 0 2.252-2.353 8.94 8.94 0 0 1-2.592.716 4.56 4.56 0 0 0 1.985-2.515 9.008 9.008 0 0 1-2.867 1.104 4.496 4.496 0 0 0-6.386-.198 4.572 4.572 0 0 0-1.306 4.344A12.79 12.79 0 0 1 9.03 11.33a4.568 4.568 0 0 0 1.398 6.067 4.456 4.456 0 0 1-2.049-.568v.058A4.54 4.54 0 0 0 12 21.342a4.46 4.46 0 0 1-2.037.078 4.522 4.522 0 0 0 4.216 3.157 9.015 9.015 0 0 1-6.68 1.885 12.717 12.717 0 0 0 6.919 2.038c8.302.003 12.842-6.924 12.842-12.93z" fill="#000"/>
</g>
</svg>

После

Ширина:  |  Высота:  |  Размер: 741 B

Двоичные данные
assets/placeholder-thumbnail.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 344 KiB

После

Ширина:  |  Высота:  |  Размер: 207 KiB

Просмотреть файл

@ -1 +1,3 @@
<svg width="15" height="10" xmlns="http://www.w3.org/2000/svg"><path d="M1.354 2.01l5.52 5.52m.49.49l6.01-6.01" stroke="#000" stroke-width="2" fill="none" fill-rule="evenodd" stroke-linecap="square"/></svg> <svg width="15" height="10" xmlns="http://www.w3.org/2000/svg">
<path d="M1.354 2.01l5.52 5.52m.49.49l6.01-6.01" stroke="#000" stroke-width="2" fill="none" stroke-linecap="square"/>
</svg>

До

Ширина:  |  Высота:  |  Размер: 207 B

После

Ширина:  |  Высота:  |  Размер: 193 B

Просмотреть файл

@ -1,3 +1,3 @@
<svg width="12" height="12" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12"> <svg width="12" height="12" xmlns="http://www.w3.org/2000/svg">
<path d="M7.4 3.95l2.85.416-2.063 2.01.487 2.837-2.549-1.34-2.55 1.34.488-2.838L2 4.366l2.85-.416 1.275-2.583z" stroke="#FFF" stroke-width="1.5" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round"/> <path d="M7.4 3.95l2.85.416-2.063 2.01.487 2.837-2.549-1.34-2.55 1.34.488-2.838L2 4.366l2.85-.416 1.275-2.583z" stroke="#FFF" stroke-width="1.5" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
</svg> </svg>

До

Ширина:  |  Высота:  |  Размер: 319 B

После

Ширина:  |  Высота:  |  Размер: 279 B

Просмотреть файл

@ -0,0 +1,6 @@
<svg width="37" height="37" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40">
<g transform="translate(1 1)" fill="none" fill-rule="evenodd" stroke="#000" stroke-width="2">
<circle cx="18.5" cy="18.5" r="18.5"/>
<path d="M27.804 11.602a5.789 5.789 0 0 0-8.189 0L18.5 12.718l-1.116-1.116a5.79 5.79 0 0 0-8.188 8.188l1.115 1.116 8.189 8.188 8.188-8.188 1.116-1.116a5.789 5.789 0 0 0 0-8.188z" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

После

Ширина:  |  Высота:  |  Размер: 481 B

Просмотреть файл

@ -0,0 +1,6 @@
<svg width="37" height="37" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40">
<g transform="translate(1 1)" fill="none" fill-rule="evenodd" stroke="#000" stroke-width="2">
<circle cx="18.5" cy="18.5" r="18.5"/>
<path d="M27.804 11.602a5.789 5.789 0 0 0-8.189 0L18.5 12.718l-1.116-1.116a5.79 5.79 0 0 0-8.188 8.188l1.115 1.116 8.189 8.188 8.188-8.188 1.116-1.116a5.789 5.789 0 0 0 0-8.188z" fill="#ff4f5e" stroke="#ff4f5e" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

После

Ширина:  |  Высота:  |  Размер: 513 B

Просмотреть файл

@ -1,3 +1,3 @@
<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg"> <svg width="24" height="24" xmlns="http://www.w3.org/2000/svg">
<path d="M21.304 4.102a5.789 5.789 0 0 0-8.189 0L12 5.218l-1.116-1.116a5.79 5.79 0 0 0-8.188 8.188l1.115 1.116L12 21.594l8.188-8.188 1.116-1.116a5.789 5.789 0 0 0 0-8.188z" stroke="#999" stroke-width="2" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round"/> <path d="M21.304 4.102a5.789 5.789 0 0 0-8.189 0L12 5.218l-1.116-1.116a5.79 5.79 0 0 0-8.188 8.188l1.115 1.116L12 21.594l8.188-8.188 1.116-1.116a5.789 5.789 0 0 0 0-8.188z" stroke="#999" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
</svg> </svg>

До

Ширина:  |  Высота:  |  Размер: 358 B

После

Ширина:  |  Высота:  |  Размер: 338 B

Просмотреть файл

@ -4,7 +4,6 @@ import PropTypes from "prop-types";
import bookmarkManager from "../js/bookmarks-manager"; import bookmarkManager from "../js/bookmarks-manager";
import Service from "../js/service.js"; import Service from "../js/service.js";
import user from "../js/app-user.js"; import user from "../js/app-user.js";
import classNames from "classnames";
class BookmarkControl extends React.Component { class BookmarkControl extends React.Component {
constructor(props) { constructor(props) {
@ -122,11 +121,9 @@ class BookmarkControl extends React.Component {
} }
render() { render() {
let classnames = classNames(`heart`, this.props.className);
return ( return (
<a <a
className={classnames} className={this.props.className}
ref="heart" ref="heart"
onClick={() => this.handleBookmarkClick()} onClick={() => this.handleBookmarkClick()}
/> />

Просмотреть файл

@ -1,12 +1,10 @@
import React from "react"; import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import classNames from "classnames";
const Creators = props => { const Creators = props => {
if (props.creators.length === 0) return null; if (props.creators.length === 0) return null;
let classnames = classNames(`my-1 body-small creator`, props.className);
let labelText = props.showLabelText ? `Created by ` : ``; let labelText = props.showLabelText ? `Created by ` : ``;
let creators = []; let creators = [];
@ -39,7 +37,7 @@ const Creators = props => {
} }
return ( return (
<p className={classnames}> <p>
{labelText} {labelText}
{creators} {creators}
</p> </p>

Просмотреть файл

@ -6,13 +6,17 @@ const Description = props => {
let paragraphs = props.description.split(`\n`).map(paragraph => { let paragraphs = props.description.split(`\n`).map(paragraph => {
if (!paragraph) return null; if (!paragraph) return null;
return <p key={paragraph}>{paragraph}</p>; return (
<p key={paragraph} className="body-large">
{paragraph}
</p>
);
}); });
if (paragraphs.length < 1) return null; if (paragraphs.length < 1) return null;
return ( return (
<div className={classNames(`description`, props.className)}> <div className={classNames(`description mt-3`, props.className)}>
{paragraphs} {paragraphs}
</div> </div>
); );

Просмотреть файл

@ -1,6 +1,7 @@
import React from "react"; import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import classNames from "classnames";
const DEFAULT_TEXT = `Help with this, or find other projects that have similar ways to get involved.`; const DEFAULT_TEXT = `Help with this, or find other projects that have similar ways to get involved.`;
@ -20,40 +21,53 @@ class GetInvolved extends React.Component {
let props = this.props; let props = this.props;
let getInvolvedText = props.getInvolved ? props.getInvolved : null; let getInvolvedText = props.getInvolved ? props.getInvolved : null;
let getInvolvedLink = props.getInvolvedUrl ? ( let getInvolvedLink = props.getInvolvedUrl ? (
<a <p className="mt-2">
href={props.getInvolvedUrl} <strong>
target="_blank" <a
onClick={() => this.handleGetInvolvedLinkClick()} href={props.getInvolvedUrl}
> target="_blank"
Get Involved onClick={() => this.handleGetInvolvedLinkClick()}
</a> >
{props.getInvolvedUrl}
</a>
</strong>
</p>
) : null; ) : null;
if (!getInvolvedText && !getInvolvedLink) return <p>{DEFAULT_TEXT}</p>; if (!getInvolvedText && !getInvolvedLink) return <p>{DEFAULT_TEXT}</p>;
return ( return (
<p> <div>
{getInvolvedText} {getInvolvedLink} <p>{getInvolvedText}</p>
</p> {getInvolvedLink}
</div>
); );
} }
renderHelpLabels() { renderHelpLabels() {
if (!this.props.helpTypes) return null; if (!this.props.helpTypes) return null;
return this.props.helpTypes.map(helpType => { let classes = classNames(`btn btn-tag`, {
"btn-xs": this.props.onModerationMode
});
let helpLabels = this.props.helpTypes.map(helpType => {
return ( return (
<Link <Link
to={`/projects?helpType=${encodeURIComponent(helpType)}`} to={`/projects?helpType=${encodeURIComponent(helpType)}`}
className="btn btn-xs btn-tag" className={classes}
key={helpType} key={helpType}
> >
{helpType} {helpType}
</Link> </Link>
); );
}); });
return <div className="mt-4">{helpLabels}</div>;
} }
render() { render() {
let classnames = classNames(`help-needed`, this.props.className);
if ( if (
!this.props.getInvolved && !this.props.getInvolved &&
!this.props.getInvolvedUrl && !this.props.getInvolvedUrl &&
@ -62,8 +76,8 @@ class GetInvolved extends React.Component {
return null; return null;
return ( return (
<div className="get-involved pb-3 mb-3"> <div className={classnames}>
<h2>Help needed</h2> <h3>Help needed</h3>
{this.renderGetInvolvedText()} {this.renderGetInvolvedText()}
{this.renderHelpLabels()} {this.renderHelpLabels()}
</div> </div>

Просмотреть файл

@ -10,8 +10,8 @@ const IssuesAndTags = props => {
let issues = props.issues.map(issue => { let issues = props.issues.map(issue => {
return ( return (
<Link <Link
to={`/issues/${Utility.getUriPathFromIssueName(issue)}`} to={`/issues/${Utility.getUriPathFromIssueName(issue)}`}
key={issue} key={issue}
> >
{issue} {issue}
</Link> </Link>
@ -20,10 +20,7 @@ const IssuesAndTags = props => {
let tags = props.tags.map(tag => { let tags = props.tags.map(tag => {
return ( return (
<Link <Link to={`/tags/${encodeURIComponent(tag)}`} key={tag}>
to={`/tags/${encodeURIComponent(tag)}`}
key={tag}
>
#{tag} #{tag}
</Link> </Link>
); );
@ -34,11 +31,7 @@ const IssuesAndTags = props => {
issues_and_tags = issues_and_tags.reduce((accu, curr) => [accu, ", ", curr]); issues_and_tags = issues_and_tags.reduce((accu, curr) => [accu, ", ", curr]);
return ( return <div className={classnames}>{issues_and_tags}</div>;
<div className={classnames}>
{issues_and_tags}
</div>
);
}; };
IssuesAndTags.propTypes = { IssuesAndTags.propTypes = {

Просмотреть файл

@ -1,6 +1,7 @@
import React from "react"; import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import classNames from "classnames";
class Thumbnail extends React.Component { class Thumbnail extends React.Component {
constructor(props) { constructor(props) {
@ -19,9 +20,10 @@ class Thumbnail extends React.Component {
<div <div
className="img-container" className="img-container"
style={{ backgroundImage: `url(${imgSrc})` }} style={{ backgroundImage: `url(${imgSrc})` }}
role="presentation"
/> />
); );
let classnames = `thumbnail`; let classnames = classNames(`thumbnail`, this.props.className);
if (this.props.link) { if (this.props.link) {
return ( return (

Просмотреть файл

@ -13,18 +13,23 @@ class Title extends React.Component {
} }
render() { render() {
let classnames = classNames(`title mb-0 h4-heading`, this.props.className);
let title = this.props.title; let title = this.props.title;
let simpleView = this.props.simpleView;
let content = <h1 className="title h1-heading mb-1 mb-md-3">{title}</h1>;
if (this.props.link) { if (simpleView) {
title = ( if (this.props.link) {
<Link to={this.props.link} onClick={() => this.handleTitleClick()}> title = (
{this.props.title} <Link to={this.props.link} onClick={() => this.handleTitleClick()}>
</Link> {this.props.title}
); </Link>
);
}
content = <h4 className="title h4-heading mb-0">{title}</h4>;
} }
return <h4 className={classnames}>{title}</h4>; return content;
} }
} }

Просмотреть файл

@ -4,7 +4,7 @@ import PropTypes from "prop-types";
const WhyInteresting = props => { const WhyInteresting = props => {
if (!props.interest) return null; if (!props.interest) return null;
return <p className="interest">{props.interest}</p>; return <p className="interest body-large mt-3">{props.interest}</p>;
}; };
WhyInteresting.propTypes = { WhyInteresting.propTypes = {

Просмотреть файл

@ -40,6 +40,7 @@ class DetailedProjectCard extends React.Component {
componentDidMount() { componentDidMount() {
this.setInitialBookmarkedStatus(); this.setInitialBookmarkedStatus();
this.setSocialPanelHeight();
} }
setInitialBookmarkedStatus() { setInitialBookmarkedStatus() {
@ -91,6 +92,35 @@ class DetailedProjectCard extends React.Component {
}); });
} }
// Sets height for social icons grid cell to match the height of entry content
setSocialPanelHeight() {
let article = this.article; //entry content
let socialIcons = this.socialIcons; //social icons
// We want to set the social icons height at large+ devices
let breakpoint = window.matchMedia("(min-width: 992px)");
function renderHeight(breakpoint) {
// If screen size is at large+ breakpoint
if (breakpoint.matches) {
// Set the height of the social icons grid cell to the height of loaded entry
socialIcons.style.height = `${article.offsetHeight}px`;
// Since social icons is in a CSS Grid cell, the cell is now the height of the entry &
// the entry has been pushed below the grid; so, we want to offset the social icons height with
// a negative margin-top on the entry (article) to bring the content back up.
article.style.marginTop = `calc(0px - (${article.offsetHeight}px + 80px))`;
} else {
// If not at large+ breakpoint we want these values to be 'auto'
socialIcons.style.height = `auto`;
article.style.marginTop = `auto`;
}
}
renderHeight(breakpoint); // Calls our function at run time
breakpoint.addListener(renderHeight); // Runs our callback function in response to any media query status changes
}
renderVisitButton() { renderVisitButton() {
if (!this.props.contentUrl) return null; if (!this.props.contentUrl) return null;
@ -98,7 +128,7 @@ class DetailedProjectCard extends React.Component {
<a <a
href={this.props.contentUrl} href={this.props.contentUrl}
target="_blank" target="_blank"
className="btn btn-block btn-info btn-visit text-capitalize mt-3" className="btn btn-primary mb-3 mb-md-0 mr-md-3 d-flex justify-content-center align-items-center"
onClick={() => this.handleVisitBtnClick()} onClick={() => this.handleVisitBtnClick()}
> >
Visit Visit
@ -106,6 +136,20 @@ class DetailedProjectCard extends React.Component {
); );
} }
renderGetInvolvedButton() {
if (!this.props.getInvolvedUrl) return null;
return (
<a
href={this.props.getInvolvedUrl}
target="_blank"
className="btn btn-secondary d-flex justify-content-center"
>
Get Involved
</a>
);
}
renderTimePosted() { renderTimePosted() {
if (!this.props.created && !this.props.publishedBy) return null; if (!this.props.created && !this.props.publishedBy) return null;
@ -128,25 +172,29 @@ class DetailedProjectCard extends React.Component {
) : null; ) : null;
return ( return (
<p> <p className="time-posted mb-4">
<small className="time-posted d-block"> Added{timePosted}
Added{timePosted} {publishedBy}
{publishedBy}
</small>
</p> </p>
); );
} }
renderActionPanel() { renderSocialPanel() {
let twitterUrl = `https://twitter.com/intent/tweet?text=${encodeURIComponent( let twitterUrl = `https://twitter.com/intent/tweet?text=${encodeURIComponent(
this.props.title this.props.title
)}&url=${encodeURIComponent(window.location.href)}`; )}&url=${encodeURIComponent(window.location.href)}`;
return ( return (
<div className="action-panel pb-3 mb-3"> <aside
<div className="d-flex share"> className="social-panel-wrapper mb-4 pl-md-3 pb-xl-4"
ref={socialIcons => {
this.socialIcons = socialIcons;
}}
>
<div className="social-panel">
<BookmarkControl <BookmarkControl
id={this.props.id} id={this.props.id}
className="circle-heart large mr-3 mr-lg-0 mb-lg-2"
title={this.props.title} title={this.props.title}
isBookmarked={this.props.isBookmarked} isBookmarked={this.props.isBookmarked}
updateCardBookmarkedState={bookmarked => { updateCardBookmarkedState={bookmarked => {
@ -156,86 +204,124 @@ class DetailedProjectCard extends React.Component {
<a <a
href={twitterUrl} href={twitterUrl}
onClick={evt => this.handleTwitterShareClick(evt)} onClick={evt => this.handleTwitterShareClick(evt)}
className="btn twitter-share d-inline-block align-self-center mx-3" className="circle-twitter large"
/> />
</div> </div>
</div> </aside>
); );
} }
renderTopHeader() { renderTitleAuthor() {
return ( return (
<div className="col-12 mb-3"> <div className="title-author-wrapper mb-4 mb-md-5 mb-lg-0">
<div className="row"> <div className="title-author">
<div className="col-12 col-sm-8"> <header className="mb-md-4">
<Title title={this.props.title} className="mb-1" /> <Title title={this.props.title} />
<Creators <Creators
creators={this.props.relatedCreators} creators={this.props.relatedCreators}
showLabelText={true} showLabelText={true}
creatorClickHandler={(event, name) => creatorClickHandler={(event, name) =>
this.handleCreatorClick(event, name) this.handleCreatorClick(event, name)
} }
className="mb-4"
/> />
</header>
<div className="action-panel d-flex flex-column flex-md-row">
{this.renderVisitButton()}
{this.renderGetInvolvedButton()}
</div> </div>
</div> </div>
</div> </div>
); );
} }
renderLeftColumn() { renderThumbnail() {
let wrapperClassnames = classNames(`col-12 col-md-8`);
return ( return (
<div className={wrapperClassnames}> <div className="thumbnail-wrapper mb-4 mb-md-0">
<Thumbnail thumbnail={this.props.thumbnail} /> <div className="thumbnail-container">
{this.renderVisitButton()} <Thumbnail thumbnail={this.props.thumbnail} />
<Description description={this.props.description} className="mt-3" /> </div>
<WhyInteresting interest={this.props.interest} />
{this.renderTimePosted()}
<IssuesAndTags issues={this.props.issues} tags={this.props.tags} />
</div> </div>
); );
} }
renderRightColumn() { renderMainContent() {
let wrapperClassnames = classNames(`col-12 col-md-4 mt-3 mt-md-0`); let getInvolved = classNames("get-involved w-100 mb-5", {
"d-none":
!this.props.getInvolved &&
!this.props.getInvolvedUrl &&
!this.props.helpTypes.length > 0
});
return ( return (
<div className={wrapperClassnames}> <article
{this.renderActionPanel()} className="main-content"
<GetInvolved ref={article => {
getInvolved={this.props.getInvolved} this.article = article;
getInvolvedUrl={this.props.getInvolvedUrl} }}
helpTypes={this.props.helpTypes} >
sendGaEvent={config => this.sendGaEvent(config)} <section className="summary-info mb-5">
/> <div className="container">
</div> <div className="offset-lg-2">
<Description description={this.props.description} />
<WhyInteresting interest={this.props.interest} />
</div>
</div>
</section>
<section className={getInvolved}>
<div className="container">
<div className="offset-lg-2 py-5">
<GetInvolved
getInvolved={this.props.getInvolved}
getInvolvedUrl={this.props.getInvolvedUrl}
helpTypes={this.props.helpTypes}
sendGaEvent={config => this.sendGaEvent(config)}
/>
</div>
</div>
</section>
<section className="issues-and-tags-container mb-4 mb-md-5">
<div className="container">
<div className="offset-lg-2">
<IssuesAndTags
issues={this.props.issues}
tags={this.props.tags}
className="mb-4 mb-md-5"
/>
</div>
</div>
</section>
<section>
<div className="container">
<div className="offset-lg-2">
{this.renderTimePosted()}
<p className="report-correction">
Correction?{" "}
<a href="https://mzl.la/pulse-contact">Contact us</a>.
</p>
</div>
</div>
</section>
</article>
); );
} }
render() { render() {
let wrapperClassnames = classNames(`col-12 pt-3 project-card detail-view`, { let wrapperClassnames = classNames(`project-card detail-view`, {
bookmarked: this.state.bookmarked bookmarked: this.state.bookmarked
}); });
return ( return (
<div className={wrapperClassnames}> <main className={wrapperClassnames}>
<div className="row">{this.renderTopHeader()}</div> <div className="thumbnail-title-social-wrapper mb-4 mb-md-5">
<div className="row"> <div className="thumbnail-title-social">
{this.renderLeftColumn()} {this.renderThumbnail()}
{this.renderRightColumn()} {this.renderTitleAuthor()}
</div> {this.renderSocialPanel()}
<div className="row">
<div className="col-12 col-md-8">
<p className="report-correction mt-md-3 pt-md-3">
<small>
Correction?{" "}
<a href="https://mzl.la/pulse-contact">Contact us</a>.
</small>
</p>
</div> </div>
</div> </div>
</div> {this.renderMainContent()}
</main>
); );
} }
} }

Просмотреть файл

@ -0,0 +1,160 @@
.project-card.detail-view {
background: inherit;
.circle-heart {
display: inline-block;
width: 37px;
height: 37px;
background-image: url(/assets/svg/icon-bookmark-entry-detailed.svg);
background-position: center center;
background-repeat: no-repeat;
cursor: pointer;
&.beating {
animation-name: beating;
animation-duration: 0.4s;
animation-fill-mode: both;
}
}
&.bookmarked .circle-heart {
background-image: url(/assets/svg/icon-bookmark-selected-entry-detailed.svg);
}
.issues-and-tags-container {
@include scaleText(16px, 24px, 20px, 28px);
color: $dark-blue;
@media (min-width: $bp-md) {
font-weight: 300;
}
}
.get-involved {
background-color: $gray-05;
}
.description {
p:last-child {
margin-bottom: 0;
}
}
.thumbnail-wrapper {
grid-area: image;
@media (min-width: $bp-md) {
grid-column: 1 / 2;
grid-row: 2;
}
@media (min-width: $bp-lg) {
grid-column: 1 / 13;
grid-row: 1;
}
}
.title-author-wrapper {
grid-area: info;
@media (min-width: $bp-lg) {
grid-column: 10 / 25;
grid-row: 1;
z-index: 1;
align-self: center;
}
}
.social-panel-wrapper {
grid-area: icons;
z-index: 1;
}
.thumbnail-title-social {
display: grid;
grid-template:
[row1-start] "image image image" 1fr [row1-end]
[row2-start] ". info ." 0.5fr [row2-end]
[row3-start] ". icons ." 40px [row3-end]
/ 15px 1fr 15px;
@media (min-width: $bp-sm) {
grid-template-columns: 100px 1fr 100px;
}
@media (min-width: $bp-md) {
margin-top: 48px;
grid-template:
[row1-start] "info info" 0.5fr [row1-end]
[row2-start] "image icons" 1.5fr [row2-end]
/ 2fr 1fr;
}
@media (min-width: $bp-lg) {
grid-template-columns: repeat(24, 1fr);
grid-template-rows: 0.25fr 0.25fr;
grid-row-gap: 48px;
}
&-wrapper {
@media (min-width: $bp-md) {
width: map-get($container-max-widths, md);
max-width: 100%;
padding: 0 15px;
margin: 0 auto;
}
@media (min-width: $bp-lg) {
width: map-get($container-max-widths, lg);
}
@media (min-width: $bp-xl) {
width: map-get($container-max-widths, xl);
}
}
}
.title-author {
@media (min-width: $bp-lg) {
background-color: $white;
padding: 24px 0 24px 24px;
}
}
.social-panel {
@media (min-width: $bp-lg) {
position: sticky;
top: 50px;
}
}
.action-panel {
@media (min-width: $bp-lg) {
width: 80%;
}
a {
flex: 1;
@media (min-width: $bp-md) {
flex: unset;
}
@media (min-width: $bp-lg) {
flex: 1;
}
}
}
.description,
.interest,
.help-needed,
.issues-and-tags,
.time-posted,
.report-correction {
@media (min-width: $bp-lg) {
width: 80%;
}
}
}

Просмотреть файл

@ -86,7 +86,7 @@ class ProjectCard extends React.Component {
updateCardBookmarkedState={bookmarked => { updateCardBookmarkedState={bookmarked => {
this.updateCardBookmarkedState(bookmarked); this.updateCardBookmarkedState(bookmarked);
}} }}
className="align-middle" className="heart align-middle"
/> />
); );
} }
@ -109,16 +109,14 @@ class ProjectCard extends React.Component {
return ( return (
<div> <div>
<WhyInteresting interest={this.props.interest} /> <WhyInteresting interest={this.props.interest} />
<IssuesAndTags <IssuesAndTags issues={this.props.issues} tags={this.props.tags} />
issues={this.props.issues}
tags={this.props.tags}
className={`pb-3 mb-3`}
/>
<GetInvolved <GetInvolved
getInvolved={this.props.getInvolved} getInvolved={this.props.getInvolved}
getInvolvedUrl={this.props.getInvolvedUrl} getInvolvedUrl={this.props.getInvolvedUrl}
helpTypes={this.props.helpTypes} helpTypes={this.props.helpTypes}
sendGaEvent={config => this.sendGaEvent(config)} sendGaEvent={config => this.sendGaEvent(config)}
onModeration={true}
className="py-3 my-4"
/> />
</div> </div>
); );
@ -180,6 +178,7 @@ class ProjectCard extends React.Component {
thumbnail={this.props.thumbnail} thumbnail={this.props.thumbnail}
link={!this.props.onModerationMode ? detailViewLink : ``} link={!this.props.onModerationMode ? detailViewLink : ``}
sendGaEvent={() => this.handleReadMoreClick()} sendGaEvent={() => this.handleReadMoreClick()}
className="simple-view"
/> />
<div className="content mt-2"> <div className="content mt-2">
<div className="d-flex"> <div className="d-flex">
@ -187,7 +186,7 @@ class ProjectCard extends React.Component {
title={this.props.title} title={this.props.title}
link={!this.props.onModerationMode ? detailViewLink : ``} link={!this.props.onModerationMode ? detailViewLink : ``}
sendGaEvent={() => this.handleReadMoreClick()} sendGaEvent={() => this.handleReadMoreClick()}
className="pr-2" simpleView={true}
/> />
{this.renderActionPanel()} {this.renderActionPanel()}
</div> </div>
@ -196,6 +195,7 @@ class ProjectCard extends React.Component {
creatorClickHandler={(event, name) => creatorClickHandler={(event, name) =>
this.handleCreatorClick(event, name) this.handleCreatorClick(event, name)
} }
className="my-1 body-small"
/> />
{this.props.onModerationMode && ( {this.props.onModerationMode && (
<Description description={this.props.description} /> <Description description={this.props.description} />

Просмотреть файл

@ -10,11 +10,15 @@
} }
.thumbnail { .thumbnail {
@include aspect-ratio(1200, 630); @include aspect-ratio(1200, 900);
display: block; display: block;
overflow: hidden; overflow: hidden;
&.simple-view {
@include aspect-ratio(1200, 630);
}
.img-container { .img-container {
background-color: #eee; background-color: #eee;
background-blend-mode: multiply; background-blend-mode: multiply;
@ -30,19 +34,6 @@
} }
} }
.action-panel {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
}
.get-involved,
&.moderation-mode .issues-and-tags,
&.detail-view .action-panel {
border-bottom: 1px solid $black;
}
.share { .share {
.btn { .btn {
width: 20px; width: 20px;
@ -103,12 +94,6 @@
.moderation-panel { .moderation-panel {
background: $gray-20; background: $gray-20;
} }
@media screen and (min-width: $bp-md) {
&.detail-view .report-correction {
border-top: 1px solid $gray-lighter;
}
}
} }
.project-card.regular-list-mode { .project-card.regular-list-mode {
@ -133,31 +118,12 @@
.project-card.moderation-mode { .project-card.moderation-mode {
margin-bottom: 3rem; margin-bottom: 3rem;
.get-involved { .help-needed {
border-top: 1px solid $black;
border-bottom: 1px solid $black;
h2 { h2 {
font-size: inherit; font-size: inherit;
} }
} }
} }
// Detailed view styling
.project-card.detail-view {
background: inherit;
h2 {
font-size: 1.5rem;
font-weight: 500;
}
.heart {
height: auto;
}
.btn-visit {
border-radius: 0;
}
}
.issues-and-tags {
color: $dark-blue;
}

Просмотреть файл

@ -155,9 +155,7 @@ class Bookmarks extends React.Component {
<Helmet> <Helmet>
<title>Favs</title> <title>Favs</title>
</Helmet> </Helmet>
<main className="favs container mt-5"> <main className="favs container mt-5">{this.renderContent()}</main>
{this.renderContent()}
</main>
</div> </div>
); );
} }

Просмотреть файл

@ -115,13 +115,7 @@ class Entry extends React.Component {
} }
render() { render() {
return ( return this.renderEntry();
<div className="container mt-5">
<div className="row justify-content-center">
{this.renderEntry()}
</div>
</div>
);
} }
} }

Просмотреть файл

@ -94,7 +94,7 @@
display: block; display: block;
width: 16px; width: 16px;
height: 14px; height: 14px;
background: url(../_images/glyphs/external.svg) no-repeat; background: url(../assets/glyphs/external.svg) no-repeat 0 0 / contain;
margin-left: 8px; margin-left: 8px;
position: relative; position: relative;
bottom: 1px; bottom: 1px;

Просмотреть файл

@ -41,6 +41,13 @@
} }
} }
} }
&.large {
&::before {
width: 37px;
height: 37px;
}
}
} }
.twitter-glyph { .twitter-glyph {
@ -84,6 +91,11 @@
@include hoverGlyph("signupin-hover"); @include hoverGlyph("signupin-hover");
} }
.circle-twitter {
@include compositeGlyph("twitter-entry-detailed");
@include hoverGlyph("twitter-entry-detailed-hover");
}
.dark-theme { .dark-theme {
.twitter-glyph { .twitter-glyph {
@include compositeGlyph("twitter-dark-theme"); @include compositeGlyph("twitter-dark-theme");

Просмотреть файл

@ -224,6 +224,7 @@ fieldset {
@import '../components/navbar/navbar'; @import '../components/navbar/navbar';
@import '../components/project-card/project-card'; @import '../components/project-card/project-card';
@import '../components/project-card/project-card-detailed';
@import '../components/profile-card/profile-card'; @import '../components/profile-card/profile-card';
@import '../components/footer/footer'; @import '../components/footer/footer';
@import '../components/issue-selector/issue-selector'; @import '../components/issue-selector/issue-selector';

Просмотреть файл

@ -1,4 +1,6 @@
.btn-secondary { .btn-secondary {
justify-content: center;
@mixin active-style() { @mixin active-style() {
color: $white; color: $white;
background: $black; background: $black;