mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2024-11-23 15:47:53 -05:00
Merge branch 'develop' of https://github.com/LLK/scratch-www into issue/gh-543-teacher-banner
* 'develop' of https://github.com/LLK/scratch-www: (29 commits) Add explore redirects Allow access to 2014 and 2015 pages use route aliases for generalizable route patterns remove `/components` from `routes.json` Remove Make it Fly banner Fix issues in FAQ of /developers page. move shuffle to utility module fix error with shuffleArray function add shuffleArray function, shuffle most loved and most remixed use `image` to get studio thumbnail quick formatting fix for box header on search Fix merge issue Add localizations, minor style changes Removed rows from grid, changed explore tabs Removed extraneous image file Whitespace added Small fix Support for studios Refactoring, new grid component Style changes ... # Conflicts: # src/views/splash/splash.jsx # src/views/splash/splash.scss
This commit is contained in:
commit
626b3f008e
28 changed files with 852 additions and 166 deletions
|
@ -44,9 +44,13 @@ var getStaticPaths = function (pathToStatic) {
|
|||
* the express route and a static view file associated with the route
|
||||
*/
|
||||
var getViewPaths = function (routes) {
|
||||
return routes.map(function (route) {
|
||||
return route.pattern;
|
||||
});
|
||||
return routes.reduce(function (paths, route) {
|
||||
var path = route.routeAlias || route.pattern;
|
||||
if (paths.indexOf(path) === -1) {
|
||||
paths.push(path);
|
||||
}
|
||||
return paths;
|
||||
}, []);
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -7,6 +7,7 @@ var Box = React.createClass({
|
|||
type: 'Box',
|
||||
propTypes: {
|
||||
title: React.PropTypes.string.isRequired,
|
||||
subtitle: React.PropTypes.string,
|
||||
moreTitle: React.PropTypes.string,
|
||||
moreHref: React.PropTypes.string,
|
||||
moreProps: React.PropTypes.object
|
||||
|
@ -20,6 +21,7 @@ var Box = React.createClass({
|
|||
<div className={classes}>
|
||||
<div className="box-header">
|
||||
<h4>{this.props.title}</h4>
|
||||
<h5>{this.props.subtitle}</h5>
|
||||
<p>
|
||||
<a href={this.props.moreHref} {...this.props.moreProps}>
|
||||
{this.props.moreTitle}
|
||||
|
|
|
@ -13,7 +13,8 @@ $base-bg: $ui-white;
|
|||
width: $cols4;
|
||||
|
||||
.box-header {
|
||||
h4 {
|
||||
h4,
|
||||
h5 {
|
||||
line-height: .9rem;
|
||||
font-size: .9rem;
|
||||
}
|
||||
|
@ -25,7 +26,8 @@ $base-bg: $ui-white;
|
|||
width: $cols6;
|
||||
|
||||
.box-header {
|
||||
h4 {
|
||||
h4,
|
||||
h5 {
|
||||
line-height: 1rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
@ -37,7 +39,8 @@ $base-bg: $ui-white;
|
|||
width: $cols8;
|
||||
|
||||
.box-header {
|
||||
h4 {
|
||||
h4,
|
||||
h5 {
|
||||
line-height: 1.1rem;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
@ -49,7 +52,8 @@ $base-bg: $ui-white;
|
|||
width: $cols12;
|
||||
|
||||
.box-header {
|
||||
h4 {
|
||||
h4,
|
||||
h5 {
|
||||
line-height: 1.1rem;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
@ -72,17 +76,25 @@ $base-bg: $ui-white;
|
|||
height: 20px;
|
||||
overflow: hidden;
|
||||
|
||||
h4 {
|
||||
h4,
|
||||
h5 {
|
||||
display: inline-block;
|
||||
float: left;
|
||||
}
|
||||
|
||||
h5 {
|
||||
margin: 0;
|
||||
padding-left: 5px;
|
||||
text-transform: none;
|
||||
letter-spacing: normal;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
p {
|
||||
display: inline-block;
|
||||
float: right;
|
||||
margin: 1px 0 0 0;
|
||||
padding: 0;
|
||||
|
||||
font-size: .85rem;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
var classNames = require('classnames');
|
||||
var FormattedMessage = require('react-intl').FormattedMessage;
|
||||
var React = require('react');
|
||||
|
||||
var TitleBanner = require('../title-banner/title-banner.jsx');
|
||||
var Button = require('../forms/button.jsx');
|
||||
var FlexRow = require('../flex-row/flex-row.jsx');
|
||||
|
||||
require('./cn-banner.scss');
|
||||
|
||||
/**
|
||||
* Homepage banner for Cartoon Network collaboration
|
||||
*/
|
||||
var CNBanner = React.createClass({
|
||||
type: 'CNBanner',
|
||||
render: function () {
|
||||
var classes = classNames(
|
||||
'cn-banner',
|
||||
this.props.className
|
||||
);
|
||||
return (
|
||||
<TitleBanner className={classes}>
|
||||
<FlexRow className="inner">
|
||||
<div className="cta">
|
||||
<h1>
|
||||
<FormattedMessage id='cnbanner.makeItFly' />
|
||||
</h1>
|
||||
<p>
|
||||
<FormattedMessage id='cnbanner.flyDescription' />
|
||||
</p>
|
||||
<div className="button-row">
|
||||
<a href="/studios/2050750/">
|
||||
<Button>
|
||||
<FormattedMessage id='cnbanner.seeExamples' />
|
||||
</Button>
|
||||
</a>
|
||||
<a href="/fly/">
|
||||
<Button>
|
||||
<FormattedMessage id='cnbanner.makeYourOwn' />
|
||||
</Button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flying">
|
||||
<img src="/svgs/make-it-fly-graphic.svg" alt="" />
|
||||
</div>
|
||||
</FlexRow>
|
||||
</TitleBanner>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = CNBanner;
|
|
@ -1,69 +0,0 @@
|
|||
@import "../../colors";
|
||||
@import "../../frameless";
|
||||
|
||||
.cn-banner {
|
||||
background-color: $ui-blue;
|
||||
background-image: url("/images/ppg-bg.jpg");
|
||||
|
||||
&.title-banner {
|
||||
transition: none;
|
||||
margin-bottom: 20px;
|
||||
padding: 0;
|
||||
text-align: left;
|
||||
|
||||
h1,
|
||||
p {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
p {
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.cta,
|
||||
.flying {
|
||||
display: flex;
|
||||
height: 235px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.cta {
|
||||
margin-right: $cols1;
|
||||
width: $cols5;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.flying {
|
||||
margin: 0 $cols1;
|
||||
width: $cols4;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.button-row {
|
||||
display: flex;
|
||||
margin-top: $gutter;
|
||||
width: $cols5;
|
||||
align-content: stretch;
|
||||
|
||||
a {
|
||||
margin-right: $gutter;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.button {
|
||||
background-color: $ui-white;
|
||||
padding: 13px 20px;
|
||||
width: 100%;
|
||||
color: $ui-blue;
|
||||
font-size: .9rem;
|
||||
}
|
||||
}
|
||||
}
|
130
src/components/grid/grid.json
Normal file
130
src/components/grid/grid.json
Normal file
|
@ -0,0 +1,130 @@
|
|||
[
|
||||
{
|
||||
"id": 1,
|
||||
"type": "project",
|
||||
"title": "Project",
|
||||
"thumbnailUrl": "",
|
||||
"creator": "",
|
||||
"href": "#"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"type": "project",
|
||||
"title": "Project",
|
||||
"thumbnailUrl": "",
|
||||
"creator": "",
|
||||
"href": "#"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"type": "project",
|
||||
"title": "Project",
|
||||
"thumbnailUrl": "",
|
||||
"creator": "",
|
||||
"href": "#"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"type": "project",
|
||||
"title": "Project",
|
||||
"thumbnailUrl": "",
|
||||
"creator": "",
|
||||
"href": "#"
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"type": "project",
|
||||
"title": "Project",
|
||||
"thumbnailUrl": "",
|
||||
"creator": "",
|
||||
"href": "#"
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"type": "project",
|
||||
"title": "Project",
|
||||
"thumbnailUrl": "",
|
||||
"creator": "",
|
||||
"href": "#"
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"type": "project",
|
||||
"title": "Project",
|
||||
"thumbnailUrl": "",
|
||||
"creator": "",
|
||||
"href": "#"
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"type": "project",
|
||||
"title": "Project",
|
||||
"thumbnailUrl": "",
|
||||
"creator": "",
|
||||
"href": "#"
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"type": "project",
|
||||
"title": "Project",
|
||||
"thumbnailUrl": "",
|
||||
"creator": "",
|
||||
"href": "#"
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"type": "project",
|
||||
"title": "Project",
|
||||
"thumbnailUrl": "",
|
||||
"creator": "",
|
||||
"href": "#"
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"type": "project",
|
||||
"title": "Project",
|
||||
"thumbnailUrl": "",
|
||||
"creator": "",
|
||||
"href": "#"
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"type": "project",
|
||||
"title": "Project",
|
||||
"thumbnailUrl": "",
|
||||
"creator": "",
|
||||
"href": "#"
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"type": "project",
|
||||
"title": "Project",
|
||||
"thumbnailUrl": "",
|
||||
"creator": "",
|
||||
"href": "#"
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"type": "project",
|
||||
"title": "Project",
|
||||
"thumbnailUrl": "",
|
||||
"creator": "",
|
||||
"href": "#"
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"type": "project",
|
||||
"title": "Project",
|
||||
"thumbnailUrl": "",
|
||||
"creator": "",
|
||||
"href": "#"
|
||||
},
|
||||
{
|
||||
"id": 16,
|
||||
"type": "project",
|
||||
"title": "Project",
|
||||
"thumbnailUrl": "",
|
||||
"creator": "",
|
||||
"href": "#"
|
||||
}
|
||||
]
|
67
src/components/grid/grid.jsx
Normal file
67
src/components/grid/grid.jsx
Normal file
|
@ -0,0 +1,67 @@
|
|||
var classNames = require('classnames');
|
||||
var React = require('react');
|
||||
|
||||
var Thumbnail = require('../thumbnail/thumbnail.jsx');
|
||||
var FlexRow = require('../flex-row/flex-row.jsx');
|
||||
|
||||
require('./grid.scss');
|
||||
|
||||
var Grid = React.createClass({
|
||||
type: 'Grid',
|
||||
getDefaultProps: function () {
|
||||
return {
|
||||
items: require('./grid.json'),
|
||||
itemType: 'projects',
|
||||
showLoves: false,
|
||||
showFavorites: false,
|
||||
showRemixes: false,
|
||||
showViews: false
|
||||
};
|
||||
},
|
||||
render: function () {
|
||||
var classes = classNames(
|
||||
'grid',
|
||||
this.props.className
|
||||
);
|
||||
return (
|
||||
<div className={classes}>
|
||||
<FlexRow>
|
||||
{this.props.items.map(function (item) {
|
||||
var href = '/' + this.props.itemType + '/' + item.id + '/';
|
||||
|
||||
if (this.props.itemType == 'projects') {
|
||||
return (
|
||||
<Thumbnail key={item.id}
|
||||
showLoves={this.props.showLoves}
|
||||
showFavorites={this.props.showFavorites}
|
||||
showRemixes={this.props.showRemixes}
|
||||
showViews={this.props.showViews}
|
||||
type={'project'}
|
||||
href={href}
|
||||
title={item.title}
|
||||
src={item.image}
|
||||
creator={item.creator}
|
||||
loves={item.stats.loves}
|
||||
favorites={item.stats.favorites}
|
||||
remixes={item.stats.remixes}
|
||||
views={item.stats.views} />
|
||||
);
|
||||
}
|
||||
else {
|
||||
return (
|
||||
<Thumbnail key={item.id}
|
||||
type={'gallery'}
|
||||
href={href}
|
||||
title={item.title}
|
||||
src={item.image}
|
||||
owner={item.owner} />
|
||||
);
|
||||
}
|
||||
}.bind(this))}
|
||||
</FlexRow>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = Grid;
|
43
src/components/grid/grid.scss
Normal file
43
src/components/grid/grid.scss
Normal file
|
@ -0,0 +1,43 @@
|
|||
@import "../../frameless";
|
||||
|
||||
.grid {
|
||||
display: inline-block;
|
||||
padding: 12px;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
|
||||
.thumbnail {
|
||||
padding: 12px;
|
||||
|
||||
&.project {
|
||||
$project-width: 200px;
|
||||
$project-height: 150px;
|
||||
width: $project-width;
|
||||
|
||||
img {
|
||||
width: $project-width;
|
||||
height: $project-height;
|
||||
}
|
||||
}
|
||||
|
||||
&.gallery {
|
||||
$gallery-width: 200px;
|
||||
$gallery-height: 118px;
|
||||
width: $gallery-width;
|
||||
|
||||
img {
|
||||
width: $gallery-width;
|
||||
height: $gallery-height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.column {
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $tablet - 1) {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
|
@ -194,7 +194,7 @@ var Navigation = React.createClass({
|
|||
</a>
|
||||
</li>
|
||||
<li className="link explore">
|
||||
<a href="/explore?date=this_month">
|
||||
<a href="/explore/projects/all">
|
||||
<FormattedMessage id="general.explore" />
|
||||
</a>
|
||||
</li>
|
||||
|
@ -215,14 +215,12 @@ var Navigation = React.createClass({
|
|||
</li>
|
||||
|
||||
<li className="search">
|
||||
<form action="/search/google_results" method="get">
|
||||
<form action="/search/projects" method="get">
|
||||
<Button type="submit" className="btn-search" />
|
||||
<Input type="text"
|
||||
aria-label={formatMessage({id: 'general.search'})}
|
||||
placeholder={formatMessage({id: 'general.search'})}
|
||||
name="q" />
|
||||
<Input type="hidden" name="date" value="anytime" />
|
||||
<Input type="hidden" name="sort_by" value="datetime_shared" />
|
||||
</form>
|
||||
</li>
|
||||
{this.props.session.status === sessionActions.Status.FETCHED ? (
|
||||
|
|
26
src/components/tabs/tabs.jsx
Normal file
26
src/components/tabs/tabs.jsx
Normal file
|
@ -0,0 +1,26 @@
|
|||
var classNames = require('classnames');
|
||||
var SubNavigation = require('../../components/subnavigation/subnavigation.jsx');
|
||||
var React = require('react');
|
||||
|
||||
require('./tabs.scss');
|
||||
|
||||
/**
|
||||
* Container for a custom, horizontal list of navigation elements
|
||||
* that can be displayed within a view or component.
|
||||
*/
|
||||
var Tabs = React.createClass({
|
||||
type: 'Tabs',
|
||||
render: function () {
|
||||
var classes = classNames(
|
||||
'tabs',
|
||||
this.props.className
|
||||
);
|
||||
return (
|
||||
<SubNavigation className={classes}>
|
||||
{this.props.children}
|
||||
</SubNavigation>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = Tabs;
|
39
src/components/tabs/tabs.scss
Normal file
39
src/components/tabs/tabs.scss
Normal file
|
@ -0,0 +1,39 @@
|
|||
@import "../../colors";
|
||||
|
||||
.tabs {
|
||||
background-color: $ui-gray;
|
||||
padding: 0 0 0 20px;
|
||||
width: calc(100% - 20px);
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.tabs li {
|
||||
opacity: .75;
|
||||
margin-bottom: -4px;
|
||||
border-top-left-radius: 10px;
|
||||
border-top-right-radius: 10px;
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
background-color: $ui-white;
|
||||
color: $header-gray;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
border-color: $active-gray;
|
||||
background-color: $ui-white;
|
||||
color: $header-gray;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.tabs li.active {
|
||||
opacity: 1;
|
||||
border-bottom: 4px solid $ui-white;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
border-bottom: 4px solid $ui-white;
|
||||
background-color: $ui-white;
|
||||
color: $header-gray;
|
||||
}
|
||||
}
|
|
@ -15,7 +15,9 @@ var Thumbnail = React.createClass({
|
|||
src: '',
|
||||
type: 'project',
|
||||
showLoves: false,
|
||||
showFavorites: false,
|
||||
showRemixes: false,
|
||||
showViews: false,
|
||||
linkTitle: true,
|
||||
alt: ''
|
||||
};
|
||||
|
@ -40,23 +42,40 @@ var Thumbnail = React.createClass({
|
|||
key="loves"
|
||||
className="thumbnail-loves"
|
||||
title={this.props.loves + ' loves'}>
|
||||
|
||||
{this.props.loves}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (this.props.favorites && this.props.showFavorites) {
|
||||
extra.push(
|
||||
<div
|
||||
key="favorites"
|
||||
className="thumbnail-favorites"
|
||||
title={this.favorites + ' favorites'}>
|
||||
{this.props.favorites}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (this.props.remixes && this.props.showRemixes) {
|
||||
extra.push(
|
||||
<div
|
||||
key="remixes"
|
||||
className="thumbnail-remixes"
|
||||
title={this.props.remixes + ' remixes'}>
|
||||
|
||||
{this.props.remixes}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (this.props.views && this.props.showViews) {
|
||||
extra.push(
|
||||
<div
|
||||
key="views"
|
||||
className="thumbnail-views"
|
||||
title={this.props.views + ' views'}>
|
||||
{this.props.views}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
var imgElement,titleElement;
|
||||
if (this.props.linkTitle) {
|
||||
imgElement = <a className="thumbnail-image" href={this.props.href}>
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
$extras: ".thumbnail-creator, .thumbnail-loves, .thumbnail-remixes";
|
||||
$extras: ".thumbnail-creator, .thumbnail-loves, .thumbnail-favorites,.thumbnail-remixes,.thumbnail-views";
|
||||
|
||||
.thumbnail-title,
|
||||
#{$extras} {
|
||||
|
@ -41,7 +41,13 @@
|
|||
}
|
||||
|
||||
.thumbnail-loves,
|
||||
.thumbnail-remixes {
|
||||
.thumbnail-favorites,
|
||||
.thumbnail-remixes,
|
||||
.thumbnail-views {
|
||||
|
||||
display: inline;
|
||||
margin-right: 10px;
|
||||
|
||||
&:before {
|
||||
display: inline-block;
|
||||
margin-right: .1rem;
|
||||
|
@ -61,12 +67,24 @@
|
|||
}
|
||||
}
|
||||
|
||||
.thumbnail-favorites {
|
||||
&:before {
|
||||
background-image: url("/svgs/favorite/favorite_type-gray.svg");
|
||||
}
|
||||
}
|
||||
|
||||
.thumbnail-remixes {
|
||||
&:before {
|
||||
background-image: url("/svgs/remix/remix_type-gray.svg");
|
||||
}
|
||||
}
|
||||
|
||||
.thumbnail-views {
|
||||
&:before {
|
||||
background-image: url("/svgs/view/view_type-gray.svg");
|
||||
}
|
||||
}
|
||||
|
||||
&.project {
|
||||
$project-width: 144px;
|
||||
$project-height: 108px;
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
"general.jobs": "Jobs",
|
||||
"general.joinScratch": "Join Scratch",
|
||||
"general.legal": "Legal",
|
||||
"general.loadMore": "Load More",
|
||||
"general.learnMore": "Learn More",
|
||||
"general.messages": "Messages",
|
||||
"general.myClass": "My Class",
|
||||
|
@ -28,6 +29,7 @@
|
|||
"general.offlineEditor": "Offline Editor",
|
||||
"general.press": "Press",
|
||||
"general.privacyPolicy": "Privacy Policy",
|
||||
"general.projects": "Projects",
|
||||
"general.profile": "Profile",
|
||||
"general.scratchConference": "Scratch Conference",
|
||||
"general.scratchday": "Scratch Day",
|
||||
|
@ -37,6 +39,7 @@
|
|||
"general.search": "Search",
|
||||
"general.signIn": "Sign in",
|
||||
"general.statistics": "Statistics",
|
||||
"general.studios": "Studios",
|
||||
"general.support": "Support",
|
||||
"general.tipsWindow": "Tips Window",
|
||||
"general.tipsAnimateYourNameTitle": "Animate Your Name",
|
||||
|
@ -51,6 +54,14 @@
|
|||
"general.whatsHappening": "What's Happening?",
|
||||
"general.wiki": "Scratch Wiki",
|
||||
|
||||
"general.all": "All",
|
||||
"general.animations": "Animations",
|
||||
"general.art": "Art",
|
||||
"general.games": "Games",
|
||||
"general.music": "Music",
|
||||
"general.stories": "Stories",
|
||||
"general.results": "Results",
|
||||
|
||||
"footer.discuss": "Discussion Forums",
|
||||
"footer.help": "Help Page",
|
||||
"footer.scratchFamily": "Scratch Family",
|
||||
|
@ -58,11 +69,6 @@
|
|||
"login.forgotPassword": "Forgot Password?",
|
||||
|
||||
"navigation.signOut": "Sign out",
|
||||
|
||||
"cnbanner.makeItFly": "Make It Fly",
|
||||
"cnbanner.seeExamples": "See examples",
|
||||
"cnbanner.makeYourOwn": "Create your own",
|
||||
"cnbanner.flyDescription": "With Scratch, you can program anything to fly. Animate the Scratch Cat, The Powerpuff Girls, or even a taco!",
|
||||
|
||||
"parents.FaqAgeRangeA": "While Scratch is primarily designed for 8 to 16 year olds, it is also used by people of all ages, including younger children with their parents.",
|
||||
"parents.FaqAgeRangeQ": "What is the age range for Scratch?",
|
||||
|
|
21
src/lib/shuffle.js
Normal file
21
src/lib/shuffle.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Function that shuffles an array using a Fisher-Yates shuffle.
|
||||
*/
|
||||
|
||||
module.exports.shuffle = function (arr) {
|
||||
var i, j = 0;
|
||||
var temp = null;
|
||||
if (arr) {
|
||||
var tempArray = arr.slice(0);
|
||||
} else {
|
||||
return arr;
|
||||
}
|
||||
|
||||
for (i = arr.length - 1; i > 0; i -= 1) {
|
||||
j = Math.floor(Math.random() * (i + 1));
|
||||
temp = tempArray[i];
|
||||
tempArray[i] = tempArray[j];
|
||||
tempArray[j] = temp;
|
||||
}
|
||||
return tempArray;
|
||||
};
|
|
@ -11,12 +11,6 @@
|
|||
"view": "about/about",
|
||||
"title": "About"
|
||||
},
|
||||
{
|
||||
"name": "components",
|
||||
"pattern": "^/components/?$",
|
||||
"view": "components/components",
|
||||
"title": "Components"
|
||||
},
|
||||
{
|
||||
"name": "developers",
|
||||
"pattern": "^/developers/?$",
|
||||
|
@ -29,6 +23,38 @@
|
|||
"view": "hoc/hoc",
|
||||
"title": "Hour of Code"
|
||||
},
|
||||
{
|
||||
"name": "explore",
|
||||
"pattern": "^/explore/:projects/:all/?$",
|
||||
"routeAlias": "^/explore",
|
||||
"view": "explore/explore",
|
||||
"title": "Explore"
|
||||
},
|
||||
{
|
||||
"name": "explore-redirect",
|
||||
"pattern": "^/explore/?$",
|
||||
"routeAlias": "^/explore",
|
||||
"redirect": "/explore/projects/all"
|
||||
},
|
||||
{
|
||||
"name": "explore-projects-redirect",
|
||||
"pattern": "^/explore/projects/?$",
|
||||
"routeAlias": "^/explore",
|
||||
"redirect": "/explore/projects/all"
|
||||
},
|
||||
{
|
||||
"name": "explore-studios-redirect",
|
||||
"pattern": "^/explore/studios/?$",
|
||||
"routeAlias": "^/explore",
|
||||
"redirect": "/explore/studios/all"
|
||||
},
|
||||
{
|
||||
"name": "search",
|
||||
"pattern": "^/search/:projects?$/?$",
|
||||
"routeAlias": "^/search",
|
||||
"view": "search/search",
|
||||
"title": "Search"
|
||||
},
|
||||
{
|
||||
"name": "credits",
|
||||
"pattern": "^/info/credits/?$",
|
||||
|
@ -68,6 +94,7 @@
|
|||
{
|
||||
"name": "conference-index",
|
||||
"pattern": "^/conference/?$",
|
||||
"routeAlias": "^/conference(?!/201[4-5])",
|
||||
"view": "conference/index/index",
|
||||
"title": "Scratch Conference",
|
||||
"viewportWidth": "device-width"
|
||||
|
@ -75,6 +102,7 @@
|
|||
{
|
||||
"name": "conference-plan",
|
||||
"pattern": "^/conference/plan/?$",
|
||||
"routeAlias": "^/conference(?!/201[4-5])",
|
||||
"view": "conference/plan/plan",
|
||||
"title": "Plan Your Visit",
|
||||
"viewportWidth": "device-width"
|
||||
|
@ -82,6 +110,7 @@
|
|||
{
|
||||
"name": "conference-expectations",
|
||||
"pattern": "^/conference/expect/?$",
|
||||
"routeAlias": "^/conference(?!/201[4-5])",
|
||||
"view": "conference/expect/expect",
|
||||
"title": "What to Expect",
|
||||
"viewportWidth": "device-width"
|
||||
|
@ -89,6 +118,7 @@
|
|||
{
|
||||
"name": "conference-schedule",
|
||||
"pattern": "^/conference/schedule/?$",
|
||||
"routeAlias": "^/conference(?!/201[4-5])",
|
||||
"view": "conference/schedule/schedule",
|
||||
"title": "Conference Schedule",
|
||||
"viewportWidth": "device-width"
|
||||
|
@ -96,6 +126,7 @@
|
|||
{
|
||||
"name": "conference-details",
|
||||
"pattern": "^/conference/:id/details/?$",
|
||||
"routeAlias": "^/conference(?!/201[4-5])",
|
||||
"view": "conference/details/details",
|
||||
"title": "Event Details",
|
||||
"viewportWidth": "device-width"
|
||||
|
|
|
@ -6,7 +6,6 @@ var Page = require('../../components/page/www/page.jsx');
|
|||
var Box = require('../../components/box/box.jsx');
|
||||
var Button = require('../../components/forms/button.jsx');
|
||||
var Carousel = require('../../components/carousel/carousel.jsx');
|
||||
var CNBanner = require('../../components/cn-banner/cn-banner.jsx');
|
||||
var Input = require('../../components/forms/input.jsx');
|
||||
var Spinner = require('../../components/spinner/spinner.jsx');
|
||||
|
||||
|
@ -66,7 +65,6 @@ var Components = React.createClass({
|
|||
<span className="splash-blue">$splash-blue</span>
|
||||
</div>
|
||||
</div>
|
||||
<CNBanner />
|
||||
</div>);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -52,7 +52,7 @@ var Developers = React.createClass({
|
|||
</SubNavigation>
|
||||
</div>
|
||||
</TitleBanner>
|
||||
|
||||
|
||||
<div className="inner">
|
||||
<section id="projects">
|
||||
<span className="nav-spacer"></span>
|
||||
|
@ -228,11 +228,10 @@ var Developers = React.createClass({
|
|||
<div className="faq column">
|
||||
<h4>Are there rules to using this code in my application?</h4>
|
||||
<p>
|
||||
You may use this code in accordance with the{' '}
|
||||
<a href="http://www.apache.org/licenses/LICENSE-2.0">Apache 2.0</a> license
|
||||
which governs this project. We also strongly encourage you to consider{' '}
|
||||
the learning and design principles (above, on this page) when building{' '}
|
||||
creative learning experiences for kids of all ages.
|
||||
You may use this code in accordance with the license which governs{' '}
|
||||
each project. We also strongly encourage you to consider the learning{' '}
|
||||
and design principles (above, on this page) when building creative{' '}
|
||||
learning experiences for kids of all ages.
|
||||
</p>
|
||||
</div>
|
||||
<div className="faq column">
|
||||
|
@ -243,7 +242,7 @@ var Developers = React.createClass({
|
|||
<p>
|
||||
If you wish, you can publicly state that your application is powered by{' '}
|
||||
Scratch Blocks. If you do so, we would also encourage you to link back to{' '}
|
||||
code repository.
|
||||
the code repository.
|
||||
</p>
|
||||
</div>
|
||||
<div className="faq column">
|
||||
|
|
154
src/views/explore/explore.jsx
Normal file
154
src/views/explore/explore.jsx
Normal file
|
@ -0,0 +1,154 @@
|
|||
var injectIntl = require('react-intl').injectIntl;
|
||||
var FormattedMessage = require('react-intl').FormattedMessage;
|
||||
var React = require('react');
|
||||
var render = require('../../lib/render.jsx');
|
||||
|
||||
var Api = require('../../mixins/api.jsx');
|
||||
|
||||
var Page = require('../../components/page/www/page.jsx');
|
||||
var Box = require('../../components/box/box.jsx');
|
||||
var Tabs = require('../../components/tabs/tabs.jsx');
|
||||
var SubNavigation = require('../../components/subnavigation/subnavigation.jsx');
|
||||
var Grid = require('../../components/grid/grid.jsx');
|
||||
|
||||
require('./explore.scss');
|
||||
|
||||
// @todo migrate to React-Router once available
|
||||
var Explore = injectIntl(React.createClass({
|
||||
type: 'Explore',
|
||||
mixins: [
|
||||
Api
|
||||
],
|
||||
getDefaultProps: function () {
|
||||
var categoryOptions = ['all','animations','art','games','music','stories'];
|
||||
var typeOptions = ['projects','studios'];
|
||||
|
||||
var pathname = window.location.pathname.toLowerCase();
|
||||
if (pathname[pathname.length - 1] === '/') {
|
||||
pathname = pathname.substring(0, pathname.length - 1);
|
||||
}
|
||||
var slash = pathname.lastIndexOf('/');
|
||||
var currentCategory = pathname.substring(slash + 1,pathname.length);
|
||||
var typeStart = pathname.indexOf('explore/');
|
||||
var type = pathname.substring(typeStart + 8,slash);
|
||||
if (categoryOptions.indexOf(currentCategory) === -1 || typeOptions.indexOf(type) === -1) {
|
||||
window.location = window.location.origin + '/explore/projects/all/';
|
||||
}
|
||||
|
||||
return {
|
||||
category: currentCategory,
|
||||
acceptableTabs: categoryOptions,
|
||||
acceptableTypes: typeOptions,
|
||||
itemType: type,
|
||||
loadNumber: 16
|
||||
};
|
||||
},
|
||||
getInitialState: function () {
|
||||
return {
|
||||
loaded: [],
|
||||
offset: 0
|
||||
};
|
||||
},
|
||||
componentDidMount: function () {
|
||||
this.getExploreMore();
|
||||
},
|
||||
getExploreMore: function () {
|
||||
var qText = '';
|
||||
if (this.props.tab != 'all') {
|
||||
qText = '&q=' + this.props.category;
|
||||
}
|
||||
this.api({
|
||||
uri: '/search/' + this.props.itemType +
|
||||
'?limit=' + this.props.loadNumber +
|
||||
'&offset=' + this.state.offset +
|
||||
qText
|
||||
}, function (err, body) {
|
||||
if (!err) {
|
||||
var loadedSoFar = this.state.loaded;
|
||||
Array.prototype.push.apply(loadedSoFar,body);
|
||||
this.setState({loaded: loadedSoFar});
|
||||
var currentOffset = this.state.offset + this.props.loadNumber;
|
||||
this.setState({offset: currentOffset});
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
changeItemType: function () {
|
||||
var newType;
|
||||
for (var t in this.props.acceptableTypes) {
|
||||
if (this.props.itemType !== t) {
|
||||
newType = t;
|
||||
break;
|
||||
}
|
||||
}
|
||||
window.location = window.location.origin + '/explore/' + newType + '/' + this.props.tab;
|
||||
},
|
||||
getBubble: function (type) {
|
||||
var allBubble = <a href={'/explore/' + this.props.itemType + '/' + type + '/'}>
|
||||
<li>
|
||||
<FormattedMessage id={'general.' + type} />
|
||||
</li>
|
||||
</a>;
|
||||
if (this.props.category === type) {
|
||||
allBubble = <a href={'/explore/' + this.props.itemType+'/' + type + '/'}>
|
||||
<li className='active'>
|
||||
<FormattedMessage id={'general.' + type} />
|
||||
</li>
|
||||
</a>;
|
||||
}
|
||||
return allBubble;
|
||||
},
|
||||
getTab: function (type) {
|
||||
var allTab = <a href={'/explore/' + type + '/' + this.props.category + '/'}>
|
||||
<li>
|
||||
<FormattedMessage id={'general.' + type} />
|
||||
</li>
|
||||
</a>;
|
||||
if (this.props.itemType === type) {
|
||||
allTab = <a href={'/explore/' + type +' /' + this.props.category + '/'}>
|
||||
<li className='active'>
|
||||
<FormattedMessage id={'general.' + type} />
|
||||
</li>
|
||||
</a>;
|
||||
}
|
||||
return allTab;
|
||||
},
|
||||
render: function () {
|
||||
return (
|
||||
<div>
|
||||
<div className='outer'>
|
||||
<Box title={'Explore'}>
|
||||
<SubNavigation className='categories'>
|
||||
{this.getBubble('all')}
|
||||
{this.getBubble('animations')}
|
||||
{this.getBubble('art')}
|
||||
{this.getBubble('games')}
|
||||
{this.getBubble('music')}
|
||||
{this.getBubble('stories')}
|
||||
</SubNavigation>
|
||||
<Tabs>
|
||||
{this.getTab('projects')}
|
||||
{this.getTab('studios')}
|
||||
</Tabs>
|
||||
<div id='projectBox' key='projectBox'>
|
||||
<Grid items={this.state.loaded}
|
||||
itemType={this.props.itemType}
|
||||
showLoves={true}
|
||||
showFavorites={true}
|
||||
showViews={true} />
|
||||
<SubNavigation className='load'>
|
||||
<button onClick={this.getExploreMore}>
|
||||
<li>
|
||||
<FormattedMessage id='general.loadMore' />
|
||||
</li>
|
||||
</button>
|
||||
</SubNavigation>
|
||||
</div>
|
||||
</Box>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
}));
|
||||
|
||||
render(<Page><Explore /></Page>, document.getElementById('app'));
|
65
src/views/explore/explore.scss
Normal file
65
src/views/explore/explore.scss
Normal file
|
@ -0,0 +1,65 @@
|
|||
@import "../../colors";
|
||||
@import "../../frameless";
|
||||
|
||||
$base-bg: $ui-white;
|
||||
|
||||
#view {
|
||||
.box {
|
||||
display: block;
|
||||
margin-right: auto;
|
||||
margin-bottom: 20px;
|
||||
margin-left: auto;
|
||||
|
||||
.box-content {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.categories {
|
||||
display: inline-block;
|
||||
background-color: $ui-gray;
|
||||
padding-left: 10px;
|
||||
width: calc(100% - 10px);
|
||||
justify-content: left;
|
||||
|
||||
li {
|
||||
opacity: .75;
|
||||
background-color: $ui-white;
|
||||
color: $header-gray;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
border-color: $active-dark-gray;
|
||||
}
|
||||
}
|
||||
|
||||
li.active {
|
||||
opacity: 1;
|
||||
border-color: $active-dark-gray;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
border-color: $active-dark-gray;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#projectBox {
|
||||
border-top: 2px solid;
|
||||
border-color: $active-gray;
|
||||
background-color: $ui-white;
|
||||
padding-bottom: 30px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.load button {
|
||||
outline: None;
|
||||
border: None;
|
||||
background-color: $ui-white;
|
||||
padding: 0;
|
||||
|
||||
li {
|
||||
color: $header-gray;
|
||||
}
|
||||
}
|
||||
}
|
126
src/views/search/search.jsx
Normal file
126
src/views/search/search.jsx
Normal file
|
@ -0,0 +1,126 @@
|
|||
var injectIntl = require('react-intl').injectIntl;
|
||||
var FormattedMessage = require('react-intl').FormattedMessage;
|
||||
var React = require('react');
|
||||
var render = require('../../lib/render.jsx');
|
||||
|
||||
var Api = require('../../mixins/api.jsx');
|
||||
|
||||
var Page = require('../../components/page/www/page.jsx');
|
||||
var Box = require('../../components/box/box.jsx');
|
||||
var SubNavigation = require('../../components/subnavigation/subnavigation.jsx');
|
||||
var Tabs = require('../../components/tabs/tabs.jsx');
|
||||
var Grid = require('../../components/grid/grid.jsx');
|
||||
|
||||
require('./search.scss');
|
||||
|
||||
// @todo migrate to React-Router once available
|
||||
var Search = injectIntl(React.createClass({
|
||||
type: 'Search',
|
||||
mixins: [
|
||||
Api
|
||||
],
|
||||
getDefaultProps: function () {
|
||||
var query = window.location.search;
|
||||
var pathname = window.location.pathname.toLowerCase();
|
||||
if (pathname[pathname.length - 1] === '/') {
|
||||
pathname = pathname.substring(0, pathname.length - 1);
|
||||
}
|
||||
var start = pathname.lastIndexOf('/');
|
||||
var type = pathname.substring(start + 1, pathname.length);
|
||||
var q = query.lastIndexOf('q=');
|
||||
var term = '';
|
||||
if (q !== -1) {
|
||||
term = query.substring(q + 2, query.length).toLowerCase();
|
||||
}
|
||||
while (term.indexOf('/') > -1) {
|
||||
term = term.substring(0, term.indexOf('/'));
|
||||
}
|
||||
while (term.indexOf('&') > -1) {
|
||||
term = term.substring(0, term.indexOf('&'));
|
||||
}
|
||||
term = term.split('+').join(' ');
|
||||
|
||||
return {
|
||||
tab: type,
|
||||
searchTerm: term,
|
||||
loadNumber: 16
|
||||
};
|
||||
},
|
||||
getInitialState: function () {
|
||||
return {
|
||||
loaded: [],
|
||||
offset: 0
|
||||
};
|
||||
},
|
||||
componentDidMount: function () {
|
||||
this.getSearchMore();
|
||||
},
|
||||
getSearchMore: function () {
|
||||
var termText = '';
|
||||
if (this.props.searchTerm !== '') {
|
||||
termText = '&q=' + this.props.searchTerm;
|
||||
}
|
||||
this.api({
|
||||
uri: '/search/' + this.props.tab +
|
||||
'?limit=' + this.props.loadNumber +
|
||||
'&offset=' + this.state.offset +
|
||||
termText
|
||||
}, function (err, body) {
|
||||
var loadedSoFar = this.state.loaded;
|
||||
Array.prototype.push.apply(loadedSoFar, body);
|
||||
this.setState({loaded: loadedSoFar});
|
||||
var currentOffset = this.state.offset + this.props.loadNumber;
|
||||
this.setState({offset: currentOffset});
|
||||
}.bind(this));
|
||||
},
|
||||
getTab: function (type) {
|
||||
var term = this.props.searchTerm.split(' ').join('+');
|
||||
var allTab = <a href={'/search/' + type + '?q=' + term + '/'}>
|
||||
<li>
|
||||
<FormattedMessage id={'general.' + type} />
|
||||
</li>
|
||||
</a>;
|
||||
if (this.props.tab == type) {
|
||||
allTab = <a href={'/search/' + type + '?q=' + term + '/'}>
|
||||
<li className='active'>
|
||||
<FormattedMessage id={'general.' + type} />
|
||||
</li>
|
||||
</a>;
|
||||
}
|
||||
return allTab;
|
||||
},
|
||||
render: function () {
|
||||
var formatMessage = this.props.intl.formatMessage;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className='outer'>
|
||||
<Box title={formatMessage({id: 'general.results'}) + ':'}
|
||||
subtitle={this.props.searchTerm}
|
||||
moreProps={{className: 'subnavigation'}}>
|
||||
<Tabs>
|
||||
{this.getTab('projects')}
|
||||
{this.getTab('studios')}
|
||||
</Tabs>
|
||||
<div id='projectBox' key='projectBox'>
|
||||
<Grid items={this.state.loaded}
|
||||
itemType={this.props.tab}
|
||||
showLoves={true}
|
||||
showFavorites={true}
|
||||
showViews={true} />
|
||||
<SubNavigation className='load'>
|
||||
<button onClick={this.getSearchMore}>
|
||||
<li>
|
||||
<FormattedMessage id='general.loadMore' />
|
||||
</li>
|
||||
</button>
|
||||
</SubNavigation>
|
||||
</div>
|
||||
</Box>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}));
|
||||
|
||||
render(<Page><Search /></Page>, document.getElementById('app'));
|
36
src/views/search/search.scss
Normal file
36
src/views/search/search.scss
Normal file
|
@ -0,0 +1,36 @@
|
|||
@import "../../colors";
|
||||
@import "../../frameless";
|
||||
|
||||
$base-bg: $ui-white;
|
||||
|
||||
#view {
|
||||
.box {
|
||||
display: block;
|
||||
margin-right: auto;
|
||||
margin-bottom: 20px;
|
||||
margin-left: auto;
|
||||
|
||||
.box-content {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
#projectBox {
|
||||
border-top: 2px solid;
|
||||
border-color: $active-gray;
|
||||
background-color: $ui-white;
|
||||
padding-bottom: 30px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.load button {
|
||||
outline: None;
|
||||
border: None;
|
||||
background-color: $ui-white;
|
||||
padding: 0;
|
||||
|
||||
li {
|
||||
color: $header-gray;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,12 +6,12 @@ var render = require('../../lib/render.jsx');
|
|||
|
||||
var permissionsActions = require('../../redux/permissions.js');
|
||||
var sessionActions = require('../../redux/session.js');
|
||||
var shuffle = require('../../lib/shuffle.js').shuffle;
|
||||
|
||||
var Api = require('../../mixins/api.jsx');
|
||||
|
||||
var Activity = require('../../components/activity/activity.jsx');
|
||||
var AdminPanel = require('../../components/adminpanel/adminpanel.jsx');
|
||||
var CNBanner = require('../../components/cn-banner/cn-banner.jsx');
|
||||
var DropdownBanner = require('../../components/dropdown-banner/banner.jsx');
|
||||
var Box = require('../../components/box/box.jsx');
|
||||
var Button = require('../../components/forms/button.jsx');
|
||||
|
@ -315,7 +315,8 @@ var Splash = injectIntl(React.createClass({
|
|||
defaultMessage: 'What the Community is Remixing' })}
|
||||
key="community_most_remixed_projects">
|
||||
|
||||
<Carousel items={this.state.featuredGlobal.community_most_remixed_projects} showRemixes={true} />
|
||||
<Carousel items={shuffle(this.state.featuredGlobal.community_most_remixed_projects)}
|
||||
showRemixes={true} />
|
||||
</Box>,
|
||||
<Box title={
|
||||
formatMessage({
|
||||
|
@ -323,7 +324,8 @@ var Splash = injectIntl(React.createClass({
|
|||
defaultMessage: 'What the Community is Loving' })}
|
||||
key="community_most_loved_projects">
|
||||
|
||||
<Carousel items={this.state.featuredGlobal.community_most_loved_projects} showLoves={true} />
|
||||
<Carousel items={shuffle(this.state.featuredGlobal.community_most_loved_projects)}
|
||||
showLoves={true} />
|
||||
</Box>
|
||||
);
|
||||
|
||||
|
@ -386,7 +388,6 @@ var Splash = injectIntl(React.createClass({
|
|||
{this.props.permissions.educator ? [
|
||||
<TeacherBanner messages={messages} />
|
||||
] : []}
|
||||
<CNBanner />
|
||||
<div key="inner" className="inner">
|
||||
{this.props.session.status === sessionActions.Status.FETCHED ? (
|
||||
this.props.session.session.user ? [
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.cn-banner,
|
||||
.teacher-banner {
|
||||
margin-top: -20px;
|
||||
}
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 64 KiB |
3
static/svgs/favorite/favorite_type-gray.svg
Normal file
3
static/svgs/favorite/favorite_type-gray.svg
Normal file
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 22 22">
|
||||
<path d="M16.265 18.413l-5.33-2.82-5.344 2.793 1.034-5.94-4.307-4.22 5.97-.852 2.682-5.4 2.654 5.413 5.965.882-4.328 4.198z" stroke="#6b6b6b" stroke-linejoin="round" stroke-width="1.6" fill="none"/>
|
||||
</svg>
|
After Width: | Height: | Size: 291 B |
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 42 KiB |
12
static/svgs/view/view_type-gray.svg
Normal file
12
static/svgs/view/view_type-gray.svg
Normal file
|
@ -0,0 +1,12 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 22 22">
|
||||
<ellipse ry="5.47" rx="8.403" cy="10.566" cx="11.132" fill="#6b6b6b" stroke="#6b6b6b" stroke-width="1.74" stroke-linejoin="bevel"/>
|
||||
<ellipse cx="11.085" cy="10.605" rx="3.67" ry="3.598" fill="none" stroke="#fff" stroke-width="2.121" stroke-linejoin="round"/>
|
||||
<g stroke="#fff" fill="none" stroke-linejoin="bevel">
|
||||
<path transform="matrix(1.03203 0 0 -1.1522 -.523 -1.591)" d="M20.575-18.124a12.19 9.241 0 0 1-3.78 12.088A12.19 9.241 0 0 1 .558-7.724" stroke-width="1.417"/>
|
||||
<path d="M20.166 3.088a12.138 9.243 0 0 1-3.765 12.09A12.138 9.243 0 0 1 .234 13.489" stroke-width="1.414" transform="matrix(1.03203 0 0 1.1522 -.523 -1.591)"/>
|
||||
</g>
|
||||
<g stroke="#fff" fill="none" stroke-linejoin="bevel">
|
||||
<path transform="matrix(-1.03217 0 0 -1.14523 22.758 -1.517)" d="M20.575-18.124a12.19 9.241 0 0 1-3.78 12.088A12.19 9.241 0 0 1 .558-7.724" stroke-width="1.417"/>
|
||||
<path d="M20.166 3.088a12.138 9.243 0 0 1-3.765 12.09A12.138 9.243 0 0 1 .234 13.489" stroke-width="1.414" transform="matrix(-1.03217 0 0 1.14523 22.758 -1.517)"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
Loading…
Reference in a new issue