Commit 61a7e9c7 authored by Rafael Specht da Silva's avatar Rafael Specht da Silva Committed by GitHub

Marketplace page (#358)

parent b13f7f9a
---
layout: default
theme: light
---
<div class="app-header {% if page.theme == 'light' %}app-header--light{% else %}app-header--black{% endif %}">
<div class="app-header {% if page.theme == 'light' %}app-header--light{% elsif page.theme == 'dark' %}app-header--black{% endif %}">
<header class="app-header_wrap headroom--top">
<div class="container">
<h1 class="app-header_logo">
......@@ -34,7 +35,7 @@ layout: default
{{ content }}
<footer class="app-footer {% if page.theme == 'light' %}app-footer--light{% else %}app-footer--black{% endif %}">
<footer class="app-footer {% if page.theme == 'light' %}app-footer--light{% elsif page.theme == 'dark' %}app-footer--black{% endif %}">
<div class="container">
<div class="app-footer_col">
......
---
layout: en/default
class: landing
---
<div class="landing-container">
<div class="landing-wrapper">
{{ page.content }}
</div>
</div>
.landing .app-header--light {
background: transparent;
.app-header_logo .dark {
opacity: 0;
}
.app-header_wrap,
.app-header_menu {
background: transparent;
.app-header_install,
li {
color: #FFF;
}
&:after {
background: transparent;
}
}
}
.landing-container {
position: relative;
margin-top: -4.3em;
.display-none {
display: none;
}
.hero {
min-height: 532px;
padding-top: 4.3em;
background-image: url("../images/marketplace/hero@3x.jpg");
background-position: center;
color: #FFFFFF;
}
.hero h1 {
margin-top: 80px;
font-size: 32px;
font-weight: bold;
line-height: 1.3;
}
.hero h2 {
margin: 15px 0 65px;
font-size: 20px;
}
.search {
max-width: 352px;
margin-bottom: 8px;
border-radius: 2px;
border: 0;
background-color: #FFF;
}
.search-results-list {
position: absolute;
max-width: 352px;
margin: 0 auto;
background: #FFF;
z-index: 2;
box-shadow: 0 15px 30px 0 rgba(0, 0, 0, 0.15);
}
.search-result-item {
border-bottom: 1px solid $grey-smoke;
cursor: pointer;
}
.search-result {
display: flex;
padding: 24px 32px;
box-sizing: border-box;
color: $grey;
}
.search-result .icon-wrapper {
width: 56px;
height: 56px;
margin-right: 16px;
}
.search-result .icon-wrapper .icon {
height: 56px;
background-size: 100%;
background-position: center;
}
.search-result .content-wrapper {
max-width: 216px;
text-align: left;
}
.apps-list-container {
margin-top: 17px;
}
.apps-list {
max-width: 682px;
}
.app-card {
display: flex;
min-height: 176px;
box-sizing: border-box;
font-size: 14px;
}
.apps-list .app-card {
max-width: 326px;
padding: 22px 30px;
margin-bottom: 30px;
background: $grey-bg;
cursor: pointer;
&:first-child {
margin-right: 30px;
}
@media screen and (max-width: $tablet) {
margin-left: auto;
margin-right: auto;
&:first-child {
margin-left: auto;
margin-right: auto;
}
}
}
.apps-list .content-wrapper {
max-width: 216px;
}
.app-card .name, .search-result .name {
display: block;
font-weight: 500;
font-size: 20px;
color: #030c1a;
}
.app-card .name {
margin-bottom: 10px;
line-height: 1;
}
.app-card .icon-wrapper {
width: 62px;
height: 62px;
margin-right: 16px;
box-sizing: border-box;
}
.app-card .icon-wrapper .icon {
height: 62px;
background-size: 100%;
background-position: center;
}
.app-card .description {
display: block;
margin-bottom: 12px;
line-height: 1.25;
font-size: 16px;
}
.links-list {
font-size: 16px;
line-height: 1;
}
.links-list-item {
margin-bottom: 16px;
}
.links-list button {
color: $space;
}
.app-category-button.highlight {
color: $brand;
}
.categories-list-item {
margin-bottom: 8px;
min-height: 20px;
box-sizing: border-box;
line-height: 1;
}
.app-category {
display: inline-block;
height: 20px;
line-height: 20px;
padding: 0 8px;
border-radius: 2px;
font-size: 10px;
text-transform: uppercase;
letter-spacing: 0.5px;
background-color: $grey-smoke;
}
.add-app-modal-wrapper {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.modal-wrapper-background {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #000;
opacity: 0.35;
}
.modal-content {
position: fixed;
top: 50%;
left: 50%;
width: 416px;
min-height: 232px;
margin: -116px 0 0 -208px;
padding: 24px 32px;
box-sizing: border-box;
border-radius: 2px;
background: #FFF;
animation: scaleIn .5s ease;
}
.modal-content .close-button {
position: absolute;
right: 30px;
top: 30px;
width: 17px;
height: 17px;
cursor: pointer;
background-image: url(../images/marketplace/close.svg);
}
.modal-content .content-wrapper {
max-width: 280px;
}
.modal-content .categories-list {
margin-bottom: 24px;
&:last-child {
margin-bottom: 0
}
}
.modal-content .button--ghost {
color: #FFF;
}
.modal-content .buttons-list-item {
display: inline-block;
margin-right: 16px;
}
.error-message-wrapper {
position: relative;
margin: 20px 20px;
justify-content: center;
}
.error-message-wrapper .error-message {
position: static;
font-size: 1rem;
text-transform: none;
}
}
......@@ -88,6 +88,13 @@ fieldset {
padding-left: 3em;
}
.input--icon-search {
background-image: url(../images/marketplace/search.svg);
background-position: 1em center;
background-repeat: no-repeat;
padding-left: 3em;
}
// error
.error-message {
color: $warning;
......
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><defs><path id="a" d="M0 0h32v32H0z"/></defs><g fill="none" fill-rule="evenodd"><path fill="#FFF" d="M-851-743H589v2180H-851z"/><path d="M1.6 1.6l12.914 12.914M14.514 1.6L1.6 14.514" stroke="#9EA2A8" stroke-width="1.5" stroke-linecap="square"/></g></svg>
\ No newline at end of file
<svg width="15" height="15" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path fill="#FFF" d="M-562-321H878v2180H-562z"/><g transform="translate(-2 -2)"><path d="M0 0h20v20H0z"/><circle stroke="#030C1A" stroke-width="1.5" cx="8" cy="8" r="5"/><path d="M16 16l-4-4" stroke="#030C1A" stroke-width="1.5" stroke-linecap="square"/></g></g></svg>
\ No newline at end of file
This diff is collapsed.
---
layout: landing
class: landing
theme: light
---
<section class="hero">
<h1>Connect your favorite Apps</h1>
<h2>Short description about the value proposition of apps</h2>
<div class="">
<input class="search input input--icon-search" type="text" name="" placeholder="Search apps" />
<ul class="search-results-list"></ul>
</div>
</section>
<div class="container apps-list-container">
<div class="flex-grid">
<ul class="links-list col col--fourth">
<li class="links-list-item">
<button data-category="" class="app-category-button highlight">All Apps</button>
</li>
</ul>
<section class="col">
<ul class="apps-list"></ul>
</section>
</div>
</div>
<div class="add-app-modal-wrapper display-none"></div>
<script id="app-card-template" type="html/template">
<div class="app-card col--haft">
<div class="icon-wrapper">
<div class="icon"></div>
</div>
<div class="content-wrapper">
<span class="name"></span>
<span class="description"></span>
<ul class="categories-list"></ul>
</div>
</div>
</script>
<script id="search-result-template" type="html/template">
<div class="search-result">
<div class="icon-wrapper">
<div class="icon"></div>
</div>
<div class="content-wrapper">
<span class="name"></span>
<span class="description"></span>
</div>
</div>
</script>
<script id="add-app-modal-template" type="html/template">
<div class="modal-wrapper-background"></div>
<div class="modal-content">
<div class="app-card-wrapper"></div>
<div class="close-button"></div>
</div>
</script>
<script src="/jquery.js"></script>
<script src="/polyfill/clipboard.min.js"></script>
<script src="/marketplace.js"></script>
/* global fetch, $ */
;(function () {
var APP_CARD_TEMPLATE = $('#app-card-template')
var APPS_LIST_EL = $('.apps-list')
var SEARCH_RESULTS_EL = $('.search-results-list')
var SEARCH_FIELD = $('.hero .search')
var MODAL_WRAPPER_EL = $('.add-app-modal-wrapper')
var MODAL_TEMPLATE = $('#add-app-modal-template')
var APPS = []
var clipboard
var getAppsData = function () {
fetch('https://marketplace.rocket.chat/v1/apps')
.then(function (res) {
return res.json()
})
.then(function (data) {
var parsed = parseData(data)
APPS = parsed
createAppList(APPS)
})
.catch((err) => {
showFetchError()
})
}
var getCategoriesData = function () {
fetch('https://marketplace.rocket.chat/v1/categories')
.then(function (res) {
return res.json()
})
.then(function (data) {
createCategoriesMenu(data)
})
}
var parseData = function (data) {
var parsed = []
for (var i = 0; i < data.length; i++) {
parsed.push(data[i].latest)
}
return parsed
}
var findAppByName = function (name, apps) {
var app = {}
for (var i = 0; i < apps.length; i++) {
if (apps[i].name === name) {
app = apps[i]
return app
}
}
return app
}
var createCategoriesList = function (categories) {
var list = ''
for (var i = 0; i < categories.length; i++) {
list += '<li class="categories-list-item"><span class="app-category">' + categories[i] + '</span></li>'
}
return list
}
var createCategoriesMenu = function (categories) {
var list = $('.apps-list-container').find('.links-list')
var virtualList = ''
categories = categories || []
for (var i = 0; i < categories.length; i++) {
var title = categories[i].title
var li = '<li data-category="' + title + '" class="links-list-item">{{button}}</li>'
var button = '<button data-category="' + title + '" class="app-category-button">' + title + '</button>'
li = li.replace(/{{button}}/, button)
virtualList += li
}
list.append(virtualList)
bindCategoriesMenuEvents()
return list
}
var createAppCard = function (app) {
var templateToStr = APP_CARD_TEMPLATE.text()
var newCardEl = $(templateToStr)
newCardEl.find('.name').text(app.name || '')
newCardEl.find('.description').text(app.description || '')
var icon = newCardEl.find('.icon-wrapper').find('.icon')
setBase64BackgroundImage(icon, app.iconFileData)
var categories = app.categories || []
var categoriesList = createCategoriesList(categories)
newCardEl.find('.categories-list').html(categoriesList)
return newCardEl
}
var createAppListRow = function (apps) {
var listEl = $('<li class="flex-grid"></li>')
for (var i = 0; i < apps.length; i++) {
var current = apps[i]
if (current) {
var card = createAppCard(current)
bindAppCardEvents(card, apps[i])
listEl.append(card)
}
}
return listEl
}
var createSearchResult = function (result) {
var searchResultTemplate = $('#search-result-template').text()
var searchResultEl = $(searchResultTemplate)
searchResultEl.find('.name').text(result.name || '')
searchResultEl.find('.description').text(result.description || '')
var icon = searchResultEl.find('.icon-wrapper').find('.icon');
setBase64BackgroundImage(icon, result.iconFileData)
searchResultEl.data('name', result.name)
return searchResultEl
}
var setBase64BackgroundImage = function (el, iconFileData) {
el[0].style.backgroundImage = 'url(data:image/png;base64,' + iconFileData + ')'
}
var createAppList = function (appsData) {
var appsListEl = APPS_LIST_EL
appsListEl.empty()
for (var i = 0; i < appsData.length; i += 2) {
var row = createAppListRow([appsData[i], appsData[i + 1]])
appsListEl.append(row)
}
}
var showFetchError = function () {
var appsListEl = APPS_LIST_EL
appsListEl.empty()
var errorEl = $('<li class="flex-grid error-message-wrapper"><div class="error-message">sorry, an error occurred and we couldn\'t load the data</div></li>')
appsListEl.append(errorEl)
}
var setSearchListPosition = function () {
var searchFieldLeft = SEARCH_FIELD.offset().left
SEARCH_RESULTS_EL[0].style.left = searchFieldLeft + 'px'
}
var createSearchList = function (appsData) {
var searchListEl = SEARCH_RESULTS_EL
searchListEl.find('.app-card').off('click')
searchListEl.empty()
setSearchListPosition()
for (var i = 0; i < appsData.length; i++) {
var listEl = $('<li class="search-result-item"></li>')
var row = createSearchResult(appsData[i])
listEl.append(row)
searchListEl.append(listEl)
}
}
var filterBySearch = function (term, apps) {
var filtered = []
var regex = new RegExp('^' + term, 'i')
for (var i = 0; i < apps.length; i++) {
var current = apps[i]
var isMatch = regex.test(current.name)
if (isMatch) {
filtered.push(current)
}
}
createSearchList(filtered)
}
var filterByCategory = function (category, apps) {
var filtered = []
if (!category) {
createAppList(APPS)
return
}
for (var i = 0; i < apps.length; i++) {
var current = apps[i]
var categoriesList = current.categories || []
var isMatch = categoriesList.indexOf(category) !== -1
if (isMatch) {
filtered.push(current)
}
}
createAppList(filtered)
}
var createModalContent = function (app) {
var template = MODAL_TEMPLATE.text()
var newModalContent = $(template)
return newModalContent
}
var createModalButtons = function (app) {
var url = 'https://marketplace.rocket.chat/v1/apps/' + app.id + '/download'
var downloadButton = '<a class="button" target="_blank" href="' + url + '">Download</a>'
var copyUrlButton = '<button class="button--ghost copy-url-button" data-clipboard-action="copy" data-clipboard-text="' + url + '">Copy URL</button>'
var list = $('<ul class="buttons-list"></ul>')
list.append('<li class="buttons-list-item">' + downloadButton + '</li>')
list.append('<li class="buttons-list-item">' + copyUrlButton + '</li>')
return list
}
var openModal = function (app) {
MODAL_WRAPPER_EL.removeClass('display-none')
MODAL_WRAPPER_EL.empty()
var content = createModalContent(app)
var card = createAppCard(app)
content.find('.app-card-wrapper').html(card)
var buttons = createModalButtons(app)
content.find('.content-wrapper').append(buttons)
MODAL_WRAPPER_EL.html(content)
clipboard = new ClipboardJS(MODAL_WRAPPER_EL.find('.copy-url-button')[0])
bindModalEvents(app)
}
var closeModal = function () {
MODAL_WRAPPER_EL.addClass('display-none')
clipboard.destroy()
unbindModalEvents()
}
var onSearch = function (term) {
if (term) {
filterBySearch(term, APPS)
} else {
createSearchList([])
}
}
var bindCategoriesMenuEvents = function () {
var appCategoryButons = $('.app-category-button')
appCategoryButons.on('click', function (ev) {
var target = $(ev.target)
appCategoryButons.removeClass('highlight')
target.addClass('highlight')
filterByCategory(target.data().category, APPS)
})
}
var bindAppCardEvents = function (appCardEl, app) {
appCardEl.on('click', function () {
openModal(app)
})
}
var bindModalEvents = function () {
MODAL_WRAPPER_EL.find('.close-button').on('click', function () {
closeModal()
})
}
var unbindModalEvents = function () {
MODAL_WRAPPER_EL.find('.close-button').off('click')
}
var bindEvents = function () {
var searchEl = SEARCH_FIELD
searchEl.on('keyup', function (ev) {
onSearch(searchEl.val())
})
SEARCH_RESULTS_EL.on('click', function (ev) {
var name = $(ev.target).parents('.search-result').data().name
var app = findAppByName(name, APPS)
console.log(app)
if (app.name) {
createSearchList([])
openModal(app)
}
})
$(window).on('resize', function () {
setSearchListPosition()
})
}
bindEvents()
getAppsData()
getCategoriesData()
})()
This diff is collapsed.
......@@ -36,3 +36,4 @@
@import "pages/support";
@import "pages/webinars";
@import "pages/404";
@import "pages/landing";