diff --git a/custom-locales.json b/custom-locales.json index ad3e37405..83c4a0b11 100644 --- a/custom-locales.json +++ b/custom-locales.json @@ -1,4 +1,8 @@ { + "ab": { + "locale": "ab", + "parentLocale": "az" + }, "an": { "locale": "an", "parentLocale": "ca" diff --git a/languages.json b/languages.json index 411834551..85679754d 100644 --- a/languages.json +++ b/languages.json @@ -21,7 +21,7 @@ "eu": "Euskara", "fa": "فارسی", "fr": "Français", - "fur": "furlan", + "fur": "Furlan", "ga": "Gaeilge", "gd": "Gàidhlig", "gl": "Galego", @@ -61,11 +61,11 @@ "ro": "Română", "ru": "Русский", "sc": "Sardu", - "sq": "shqiptar", + "sq": "Shqiptar", "sk": "Slovenčina", "sl": "Slovenščina", "sr": "Српски", - "fi": "suomi", + "fi": "Suomi", "sv": "Svenska", "te": "తెలుగు", "nai": "Tepehuan", diff --git a/src/_colors.scss b/src/_colors.scss index 7a8947665..dff8ef60a 100644 --- a/src/_colors.scss +++ b/src/_colors.scss @@ -11,6 +11,7 @@ $background-color: hsla(0, 0, 99, 1); //#FDFDFD /* UI Secondary Colors */ $ui-aqua: hsla(170, 70, 50, 1); //#26D9BB $ui-purple: hsla(265, 55, 55, 1); //#824DCB +$ui-yellow: hsla(45, 100, 50, 1); //#FFBF00 $ui-white: #fff; $ui-border: hsla(0, 0, 85, 1); //#D9D9D9 diff --git a/src/components/grid/grid.jsx b/src/components/grid/grid.jsx index 04f8a0b42..93572840d 100644 --- a/src/components/grid/grid.jsx +++ b/src/components/grid/grid.jsx @@ -15,7 +15,8 @@ var Grid = React.createClass({ showLoves: false, showFavorites: false, showRemixes: false, - showViews: false + showViews: false, + showAvatar: false }; }, render: function () { @@ -36,10 +37,13 @@ var Grid = React.createClass({ showFavorites={this.props.showFavorites} showRemixes={this.props.showRemixes} showViews={this.props.showViews} + showAvatar={this.props.showAvatar} type={'project'} href={href} title={item.title} src={item.image} + avatar={'https://cdn2.scratch.mit.edu/get_image/user/' + + item.author.id + '_32x32.png'} creator={item.author.username} loves={item.stats.loves} favorites={item.stats.favorites} diff --git a/src/components/grid/grid.scss b/src/components/grid/grid.scss index 1f72e99d5..2afed6b19 100644 --- a/src/components/grid/grid.scss +++ b/src/components/grid/grid.scss @@ -1,40 +1,89 @@ @import "../../frameless"; +@import "../../colors"; .grid { display: inline-block; width: 100%; - $project-width: 200px; - $project-height: 150px; - - $gallery-width: 200px; - $gallery-height: 118px; + $thumbnail-width: 220px; + $thumbnail-inner-width: 204px; + + $project-height: 208px; + $gallery-height: 164px; .flex-row { margin: 0 auto; - padding: 12px; - width: (96px + (4 * $project-width)) / $em; + padding: 12px 0; + width: $cols12; justify-content: flex-start; } .thumbnail { - padding: 12px; + margin: 7px; + border-radius: 4px; + box-shadow: 0 0 3px $box-shadow-gray; + background-color: $ui-white; + padding-bottom: 4px; + width: $thumbnail-width; + + .thumbnail-image { + margin: 8px auto; + width: $thumbnail-inner-width; + } + + .thumbnail-info { + margin: 0 auto; + width: $thumbnail-inner-width; + + .creator-image { + float: left; + + img { + margin-right: 8px; + border-radius: 4px; + width: 32px; + height: 32px; + } + } + + .thumbnail-title { + float: left; + max-width: 164px; + + .thumbnail-creator a { + color: $type-gray; + } + } + } &.project { - width: $project-width; + height: $project-height; - img { - width: $project-width; - height: $project-height; + .thumbnail-image { + height: 152px; + + img { + margin: 0 auto; + border: 0; + border-radius: 4px; + width: $thumbnail-inner-width; + height: 152px; + } } } &.gallery { - width: $gallery-width; + height: $gallery-height; - img { - width: $gallery-width; - height: $gallery-height; + .thumbnail-image { + height: 120px; + + img { + border: 0; + border-radius: 4px; + width: $thumbnail-inner-width; + height: 120px; + } } } } @@ -44,15 +93,24 @@ justify-content: center; } - @media only screen and (max-width: $tablet - 1) { - flex-direction: column; + //4 columns + @media only screen and (max-width: $mobile - 1) { + .flex-row { + width: $cols4; + } } - @media only screen and (max-width: $desktop - 1) { + //6 columns + @media only screen and (min-width: $mobile) and (max-width: $tablet - 1) { .flex-row { - padding: 12px 0; - width: 100%; - justify-content: space-around; + width: $cols6; + } + } + + // 8 columns + @media only screen and (min-width: $tablet) and (max-width: $desktop - 1) { + .flex-row { + width: $cols9; } } } diff --git a/src/components/registration/steps.scss b/src/components/registration/steps.scss index 0355fbf7d..d9ef9e135 100644 --- a/src/components/registration/steps.scss +++ b/src/components/registration/steps.scss @@ -20,6 +20,10 @@ .row { margin-left: .5rem; + + .validation-message { + transform: translate(14rem, 0); + } } } @@ -102,14 +106,12 @@ } &.usescratch-step { - .form { - .form-group { - margin-bottom: 0; + .form-group { + margin-bottom: 0; - &.has-error { - .textarea { - border: 1px solid $ui-orange; - } + &.has-error { + .textarea { + border: 1px solid $ui-orange; } } } diff --git a/src/components/tabs/tabs.jsx b/src/components/tabs/tabs.jsx index 814a3e02e..dd816c1ac 100644 --- a/src/components/tabs/tabs.jsx +++ b/src/components/tabs/tabs.jsx @@ -16,9 +16,11 @@ var Tabs = React.createClass({ this.props.className ); return ( - - {this.props.children} - +
+ + {this.props.children} + +
); } }); diff --git a/src/components/tabs/tabs.scss b/src/components/tabs/tabs.scss index 3d3939717..a612c3f76 100644 --- a/src/components/tabs/tabs.scss +++ b/src/components/tabs/tabs.scss @@ -1,38 +1,53 @@ @import "../../colors"; +@import "../../frameless"; + +.tab-background { + box-shadow: 0 0 1px $box-shadow-gray; + background-color: $ui-white; + width: 100%; +} .tabs { - background-color: $ui-gray; - padding: 0 0 0 20px; - width: calc(100% - 20px); - justify-content: flex-start; + background-color: $ui-white; + padding: 0; + width: $cols12; + justify-content: center; } .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; + margin: 0; + border: 0; + border-radius: 0; + width: $cols2; + text-align: center; color: $header-gray; - + + &.active { + border-bottom: 3px solid $ui-aqua; + + &:hover { + border-bottom: 3px solid $ui-aqua; + color: $header-gray; + } + } + + &:active { + box-shadow: none; + padding: .75em 1.5em; + } + + &:hover { + border-bottom: 3px solid $active-dark-gray; + } + + .tab-icon { + display: block; + margin: 0 auto; + margin-bottom: 4px; + width: 24px; + } + &: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; } diff --git a/src/components/thumbnail/thumbnail.jsx b/src/components/thumbnail/thumbnail.jsx index f85dc18c4..ce96cf529 100644 --- a/src/components/thumbnail/thumbnail.jsx +++ b/src/components/thumbnail/thumbnail.jsx @@ -1,5 +1,4 @@ var classNames = require('classnames'); -var FormattedMessage = require('react-intl').FormattedMessage; var React = require('react'); require('./thumbnail.scss'); @@ -14,11 +13,13 @@ var Thumbnail = React.createClass({ href: '#', title: 'Project', src: '', + avatar: '', type: 'project', showLoves: false, showFavorites: false, showRemixes: false, showViews: false, + showAvatar: false, linkTitle: true, alt: '' }; @@ -30,16 +31,8 @@ var Thumbnail = React.createClass({ this.props.className ); var extra = []; - if (this.props.creator) { - extra.push( -
- {' '} - - {this.props.creator} - -
- ); - } + var info = []; + if (this.props.loves && this.props.showLoves) { extra.push(
); } - var imgElement,titleElement; + var imgElement,titleElement,avatarElement; if (this.props.linkTitle) { imgElement = {this.props.alt} @@ -91,11 +84,30 @@ var Thumbnail = React.createClass({ titleElement = this.props.title; } + info.push(titleElement); + + if (this.props.creator) { + info.push( +
+ {this.props.creator} +
+ ); + } + + if (this.props.avatar && this.props.showAvatar) { + avatarElement = + + {this.props.creator} + ; + } return (
{imgElement} -
- {titleElement} +
+ {avatarElement} +
+ {info} +
{extra}
diff --git a/src/components/thumbnail/thumbnail.scss b/src/components/thumbnail/thumbnail.scss index f119b78ec..f731449ab 100644 --- a/src/components/thumbnail/thumbnail.scss +++ b/src/components/thumbnail/thumbnail.scss @@ -90,9 +90,11 @@ $project-height: 108px; width: $project-width; - img { - width: $project-width; - height: $project-height; + .thumbnail-image { + img { + width: $project-width; + height: $project-height; + } } } diff --git a/src/routes.json b/src/routes.json index 0520c20cd..478b94c9e 100644 --- a/src/routes.json +++ b/src/routes.json @@ -4,8 +4,7 @@ "pattern": "^/?$", "routeAlias": "/?$", "view": "splash/splash", - "title": "Imagine, Program, Share", - "viewportWidth": "device-width" + "title": "Imagine, Program, Share" }, { "name": "about", @@ -48,32 +47,28 @@ "pattern": "^/conference/plan/?$", "routeAlias": "/conference(?!/201[4-5])", "view": "conference/plan/plan", - "title": "Plan Your Visit", - "viewportWidth": "device-width" + "title": "Plan Your Visit" }, { "name": "conference-expectations", "pattern": "^/conference/expect/?$", "routeAlias": "/conference(?!/201[4-5])", "view": "conference/expect/expect", - "title": "What to Expect", - "viewportWidth": "device-width" + "title": "What to Expect" }, { "name": "conference-schedule", "pattern": "^/conference/schedule/?$", "routeAlias": "/conference(?!/201[4-5])", "view": "conference/schedule/schedule", - "title": "Conference Schedule", - "viewportWidth": "device-width" + "title": "Conference Schedule" }, { "name": "conference-details", "pattern": "^/conference/:id/details/?$", "routeAlias": "/conference(?!/201[4-5])", "view": "conference/details/details", - "title": "Event Details", - "viewportWidth": "device-width" + "title": "Event Details" }, { "name": "developers", @@ -108,8 +103,7 @@ "pattern": "^/educators/register/?$", "routeAlias": "/educators(?:/(faq|register|waiting))?/?$", "view": "teacherregistration/teacherregistration", - "title": "Teacher Registration", - "viewportWidth": "device-width" + "title": "Teacher Registration" }, { "name": "teacherwaitingroom", diff --git a/src/template-config.js b/src/template-config.js index 297328d74..33cd26ca7 100644 --- a/src/template-config.js +++ b/src/template-config.js @@ -7,7 +7,7 @@ module.exports = { 'and animations.', // override if mobile-friendly - viewportWidth: 942, + viewportWidth: 'device-width', // Open graph og_image: 'https://scratch.mit.edu/images/scratch-og.png', diff --git a/src/views/explore/explore.jsx b/src/views/explore/explore.jsx index 2149be580..c1c0e00df 100644 --- a/src/views/explore/explore.jsx +++ b/src/views/explore/explore.jsx @@ -7,8 +7,11 @@ var render = require('../../lib/render.jsx'); var api = require('../../lib/api'); var Page = require('../../components/page/www/page.jsx'); -var Box = require('../../components/box/box.jsx'); var Tabs = require('../../components/tabs/tabs.jsx'); +var TitleBanner = require('../../components/title-banner/title-banner.jsx'); +var Button = require('../../components/forms/button.jsx'); +var Form = require('../../components/forms/form.jsx'); +var Select = require('../../components/forms/select.jsx'); var SubNavigation = require('../../components/subnavigation/subnavigation.jsx'); var Grid = require('../../components/grid/grid.jsx'); @@ -27,16 +30,19 @@ var Explore = injectIntl(React.createClass({ stories: 'stories' }; var typeOptions = ['projects','studios']; + var modeOptions = ['trending', 'popular', 'recent', '']; 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 (Object.keys(categoryOptions).indexOf(currentCategory) === -1 || typeOptions.indexOf(type) === -1) { + var options = pathname.split('/'); + var type = options[2]; + var currentCategory = options[3]; + var currentMode = options.length > 4 ? options[4] : ''; + if (Object.keys(categoryOptions).indexOf(currentCategory) === -1 || + typeOptions.indexOf(type) === -1 || + modeOptions.indexOf(currentMode) === -1){ window.location = window.location.origin + '/explore/projects/all/'; } @@ -44,7 +50,9 @@ var Explore = injectIntl(React.createClass({ category: currentCategory, acceptableTabs: categoryOptions, acceptableTypes: typeOptions, + acceptableModes: modeOptions, itemType: type, + mode: currentMode, loadNumber: 16 }; }, @@ -59,12 +67,13 @@ var Explore = injectIntl(React.createClass({ }, getExploreMore: function () { var qText = '&q=' + this.props.acceptableTabs[this.props.category] || '*'; - + api({ uri: '/search/' + this.props.itemType + '?limit=' + this.props.loadNumber + '&offset=' + this.state.offset + '&language=' + this.props.intl.locale + + '&mode=' + (this.props.mode ? this.props.mode : 'trending') + qText }, function (err, body) { if (!err) { @@ -84,14 +93,20 @@ var Explore = injectIntl(React.createClass({ break; } } - window.location = window.location.origin + '/explore/' + newType + '/' + this.props.tab; + window.location = window.location.origin + '/explore/' + newType + '/' + this.props.tab + '/' + this.props.mode; + }, + changeSortMode: function (name, value) { + if (this.props.acceptableModes.indexOf(value) !== -1) { + window.location = window.location.origin + '/explore/' + + this.props.itemType + '/' + this.props.category + '/' + value; + } }, getBubble: function (type) { var classes = classNames({ active: (this.props.category === type) }); return ( - +
  • @@ -103,20 +118,33 @@ var Explore = injectIntl(React.createClass({ active: (this.props.itemType === type) }); return ( -
    +
  • + {this.props.itemType === type ? [ + + ] : [ + + ]}
  • ); }, render: function () { - var formatMessage = this.props.intl.formatMessage; return (
    - + +
    +

    Explore

    +
    +
    + + {this.getTab('projects')} + {this.getTab('studios')} + +
    {this.getBubble('all')} {this.getBubble('animations')} @@ -125,25 +153,29 @@ var Explore = injectIntl(React.createClass({ {this.getBubble('music')} {this.getBubble('stories')} - - {this.getTab('projects')} - {this.getTab('studios')} - -
    - - - - -
    - +
    + +
    +
    +
    + {this.getTab('projects')} {this.getTab('studios')}
    - - - - + +
    -
    ); diff --git a/src/views/search/search.scss b/src/views/search/search.scss index 49752fb82..0a9d4f583 100644 --- a/src/views/search/search.scss +++ b/src/views/search/search.scss @@ -4,33 +4,181 @@ $base-bg: $ui-white; #view { - .box { - display: block; - margin-right: auto; - margin-bottom: 20px; - margin-left: auto; + background-color: $ui-gray; + padding: 0; +} - .box-content { +.outer { + .title-banner { + &.masthead { + margin-bottom: 0; + background-color: darken($ui-blue, 10%); padding: 0; - } - } - #projectBox { - border-top: 2px solid; - border-color: $active-gray; + h1 { + text-align: center; + color: $ui-white; + font-size: 3rem; + } + + p { + margin: 0; + width: $cols6; + text-align: left; + color: $ui-white; + + a { + border-bottom: 1px solid $ui-white; + color: $ui-white; + } + } + } + } + + .search { + margin: 0 auto; + border-right: 0; + width: $cols6; + color: $type-white; + + .form { + margin: 0; + } + + .row { + .help-block { + display: none; + } + } + + .input, + .button { + display: inline-block; + margin-top: 5px; + outline: none; + border: 0; + background-color: $active-gray; + height: 14px; + + &[type=text] { + transition: .15s ease background-color; + padding: 0; + padding-right: 10px; + padding-left: 40px; + width: calc(100% - 50px); + height: 40px; + color: $type-white; + font-size: .85em; + + &::placeholder { + $placeholder-transparent: rgba(255, 255, 255, .75); + color: $placeholder-transparent; + } + + &:focus { + transition: .15s ease background-color; + background-color: $active-dark-gray; + } + + .ie9 & { + width: 70px; + } + } + } + + .btn-search { + position: absolute; + + box-shadow: none; + background-color: transparent; + background-image: url("/images/nav-search-glass.png"); + background-repeat: no-repeat; + background-position: center center; + background-size: 14px 14px; + + width: 40px; + height: 40px; + + &:hover { + box-shadow: none; + } + } + } + + .select { + select { + margin-bottom: 0; + color: $header-gray; + } + + .help-block { + display: none; + } + } + + .tab-background { + box-shadow: 0 0 1px $box-shadow-gray; background-color: $ui-white; - padding-bottom: 30px; width: 100%; } - .load button { - outline: None; - border: None; - background-color: $ui-white; - padding: 0; + #projectBox { + margin-top: 16px; + background-color: $ui-gray; + padding-bottom: 32px; + width: 100%; - li { - color: $header-gray; + .button { + display: block; + margin: 0 auto; + } + } +} + +//4 columns +@media only screen and (max-width: $mobile - 1) { + .outer { + .search { + width: $cols4; + + .btn-search { + left: 40px; + } + } + + .tabs { + width: $cols4; + } + + .sort-controls { + width: $cols4; + } + } +} + + +//6 columns +@media only screen and (min-width: $mobile) and (max-width: $tablet - 1) { + .outer { + .tabs { + width: $cols6; + } + + .sort-controls { + width: $cols6; + } + } +} + +// 8 columns +@media only screen and (min-width: $tablet) and (max-width: $desktop - 1) { + .outer { + .tabs { + width: $cols8; + } + + .sort-controls { + width: $cols8; } } } diff --git a/static/svgs/tabs/projects-active.svg b/static/svgs/tabs/projects-active.svg new file mode 100644 index 000000000..3738946b1 --- /dev/null +++ b/static/svgs/tabs/projects-active.svg @@ -0,0 +1 @@ +Icons \ No newline at end of file diff --git a/static/svgs/tabs/projects-inactive.svg b/static/svgs/tabs/projects-inactive.svg new file mode 100644 index 000000000..c86555b0c --- /dev/null +++ b/static/svgs/tabs/projects-inactive.svg @@ -0,0 +1 @@ +Icons \ No newline at end of file diff --git a/static/svgs/tabs/studios-active.svg b/static/svgs/tabs/studios-active.svg new file mode 100644 index 000000000..71e9fac64 --- /dev/null +++ b/static/svgs/tabs/studios-active.svg @@ -0,0 +1 @@ +Icons \ No newline at end of file diff --git a/static/svgs/tabs/studios-inactive.svg b/static/svgs/tabs/studios-inactive.svg new file mode 100644 index 000000000..241faf900 --- /dev/null +++ b/static/svgs/tabs/studios-inactive.svg @@ -0,0 +1 @@ +Icons \ No newline at end of file