diff --git a/website/src/article-vew-components/game-article.js b/website/src/article-vew-components/game-article.js index f0f757e4bb118f6ac81d9b0ca9d4316e651bf97e..fbded902361839479e095a7e1287cc20e2727546 100644 --- a/website/src/article-vew-components/game-article.js +++ b/website/src/article-vew-components/game-article.js @@ -12,10 +12,11 @@ const t = translator.trad.bind(translator); class GameArticle { constructor(data) { this.data = data; - this.parse_body(); + this.init_body(); + this.init_details(); } - parse_body() { + init_body() { let body = getArticleBody(this.data.body); const play_btn_regex = /\[PLAY_BUTTON\s\{.+\}\]/g; const found_play_buttons = body.match(play_btn_regex); @@ -26,6 +27,17 @@ class GameArticle { this.body = body; } + init_details() { + this.details = this.data.details.filter(d => { + const { label, value } = d; + if (label === "video") { + this.video_link = value; + return false; + } + return true; + }); + } + build_play_button(button_data) { this.render_play_button = { tag: "button", @@ -92,11 +104,12 @@ class GameArticle { render() { const { images, - details, title, subtitle, } = this.data; + const trad_ready = Object.keys(translator.translations).length > 0; + return { tag: "article", class: "game-article", @@ -130,6 +143,7 @@ class GameArticle { { tag: "div", id: "article-contents", + class: "page-contents-center", contents: [ { tag: "div", @@ -138,22 +152,26 @@ class GameArticle { { tag: "p", contents: this.body - } + }, + this.video_link && { + tag: "iframe", + src: this.video_link, + frameborder: 0, + allowfullscreen: true, + width: 400, + height: 250, + }, ], }, - new ImageCarousel({ images: images.map(img => `${images_url}/${img}`) }).render(), - details.length > 0 && { + trad_ready && new ImageCarousel({ images: images.map(img => `${images_url}/${img}`) }).render(), + this.details.length > 0 && { tag: "div", class: "article-details", contents: [ - { - tag: "h2", - contents: "Details", - }, { tag: "ul", class: "details-list", - contents: details.map(detail => { + contents: this.details.map(detail => { return { tag: "li", class: "detail", @@ -162,7 +180,7 @@ class GameArticle { { tag: "div", class: "detail-value", - contents: detail.value + contents: detail.value, }, ], }; diff --git a/website/src/article-view.scss b/website/src/article-view.scss index d1a549703e3a4d8757723c6bbc1a28cfac3483f2..767cb8bd0201f9f7c92f388c0705156a0eed8051 100644 --- a/website/src/article-view.scss +++ b/website/src/article-view.scss @@ -2,7 +2,7 @@ position: relative; article { - background-image: $wp_bin; + background-color: black; &.game-article { #article-banner { @@ -38,12 +38,13 @@ font-style: italic; margin: 0 0 0 150px; font-size: 32px; + max-width: 50%; } } .play-button { position: absolute; - right: 10px; + right: 40px; bottom: 10px; padding: 20px 40px; border: 1px solid; @@ -62,27 +63,156 @@ } #article-contents { - // background-color: #1f2122; - // color: $light_1; - - // * { - // color: $light_1; - // } - background-color: white; padding: 40px; - display: grid; - grid-template-columns: 1fr 500px; - max-width: 1000px; - margin: 0 auto; #article-body { + display: grid; + grid-template-columns: 1fr auto; + gap: 20px; + p { margin: 0; + color: $light_1; + font-size: 20px; + font-weight: 600; + text-align: justify; + } + + iframe { + width: 400px; + height: 250px; } } .image-carousel { - height: 400px; + width: 100%; + height: 600px; + } + + .article-details { + margin-top: 80px; + + .details-list { + * { + color: $light_1; + } + + .detail { + display: grid; + grid-template-columns: 1fr auto; + border-bottom: 1px solid #fff5; + padding: 5px 0; + + label { + font-weight: bold; + } + } + } + } + } + + @media screen and (max-width:1000px) { + #article-contents { + max-width: 100%; + + #article-body { + grid-template-columns: 1fr; + + iframe { + width: 100%; + height: 400px; + } + } + + + } + } + + @media screen and (max-width:850px) { + #article-banner { + height: 350px; + + #header-text-container { + padding: 20px; + + .header-text { + + &.h1 { + font-size: 60px; + } + + &.h2 { + margin: 0 0 0 50px; + font-size: 25px; + } + } + + .play-button { + bottom: unset; + top: 10px; + right: 20px; + background-color: #0005; + } + } + } + + #article-contents { + padding: 20px; + + #article-body { + iframe { + height: 300px; + } + } + + .image-carousel { + height: 300px; + } + + .article-details { + margin-top: 40px; + } + } + } + + @media screen and (max-width:750px) { + #article-banner { + height: 250px; + + #header-text-container { + .header-text { + &.h1 { + font-size: 40px; + } + + &.h2 { + font-style: italic; + margin: 0; + font-size: 20px; + } + } + + .play-button { + // TODO handle cases where game can be played on a mobile device + display: none; + } + } + } + + #article-contents { + #article-body { + p { + font-size: initial; + } + + iframe { + height: 250px; + } + } + + .image-carousel { + height: 250px; + } } } } diff --git a/website/src/generic-components/image-carousel.js b/website/src/generic-components/image-carousel.js index 8d5b6586203f36d6bba42146371cd685723f7f3b..2910a55607e810a9ac1410e93def9adaadcf26b1 100644 --- a/website/src/generic-components/image-carousel.js +++ b/website/src/generic-components/image-carousel.js @@ -1,5 +1,5 @@ "use strict"; - +const translator = require("ks-cheap-translator") class ImageCarousel { constructor(props) { this.props = props; @@ -16,54 +16,66 @@ class ImageCarousel { let { showImageIndex } = this.state; const { images } = this.props; this.state.showImageIndex = showImageIndex < images.length - 1 ? ++showImageIndex : 0; - this.refreshImage(); + this.refresh(); }, this.RUN_INTERVAL); obj2htm.registerAsyncSubscription(this.id, function () { - clearInterval(runningInterval) + clearInterval(runningInterval); }); this.runningInterval = runningInterval; } - setImageIndex(i) { + set_image_index(i) { clearInterval(this.runningInterval); this.state.showImageIndex = i; - this.refreshImage(); + this.refresh(); } - refreshImage() { - obj2htm.subRender(this.render(), document.getElementById(this.id), { + refresh() { + obj2htm.subRender(this.render_image(), document.getElementById(this.id + "-img-element"), { + mode: "replace", + }); + obj2htm.subRender(this.render_bullets(), document.getElementById(this.id + "-bullets"), { mode: "replace", }); } + + render_image() { + return { + tag: "img", + id: this.id + "-img-element", + property: "image", + alt: `image carousel ${this.props.images[this.state.showImageIndex].replace(/\.[A-Za-z]+/, "")}`, + src: this.props.images[this.state.showImageIndex], + }; + } + + render_bullets() { + return this.props.images.length > 1 ? { + tag: "div", + id: this.id + "-bullets", + class: "carousel-bullets", + contents: this.props.images.map((_, i) => { + const active = this.state.showImageIndex === i; + return { + tag: "span", + class: `bullet ${active ? "active" : ""}`, + onclick: this.set_image_index.bind(this, i), + }; + }), + } : { tag: "span" }; + } + render() { - const { showImageIndex } = this.state; - const { images } = this.props; return { tag: "div", id: this.id, class: "image-carousel", contents: [ - { - tag: "img", - property: "image", - alt: `image carousel ${images[showImageIndex].replace(/\.[A-Za-z]+/, "")}`, - src: images[showImageIndex], - }, - images.length > 1 && { - tag: "div", - class: "carousel-bullets", - contents: images.map((_, i) => { - const active = showImageIndex === i; - return { - tag: "span", - class: `bullet ${active ? "active" : ""}`, - onclick: this.setImageIndex.bind(this, i), - }; - }), - }, + this.render_image(), + this.render_bullets(), ], }; } diff --git a/website/src/pages/games/components/game-article.js b/website/src/pages/games/components/game-article.js deleted file mode 100644 index 790dbc1bdfd69a5c9c1ce5cae9b1822d310d119c..0000000000000000000000000000000000000000 --- a/website/src/pages/games/components/game-article.js +++ /dev/null @@ -1,168 +0,0 @@ -"use strict"; - -const { images_url } = require("../../../../../admin-frontend/src/constants"); -const { data_url } = require("../../../../constants"); -const ImageCarousel = require("../../../generic-components/image-carousel"); -const { getArticleBody } = require("../../../lib/article-utils"); -const { fetch_json_or_error_text } = require("../../../lib/fetch"); -const { MentaloEngine } = require("mentalo-engine"); -const translator = require("ks-cheap-translator"); -const t = translator.trad.bind(translator); - -class GameArticle { - constructor(props) { - this.props = props; - this.parse_body(); - } - - parse_body() { - let body = getArticleBody(this.props.body); - const play_btn_regex = /\[PLAY_BUTTON\s\{.+\}\]/g; - const found_play_buttons = body.match(play_btn_regex); - if (found_play_buttons) { - this.build_play_button(JSON.parse(found_play_buttons[0].replace(/[\[\]PLAY_BUTTON\s]/g, ""))); - body = body.replace(play_btn_regex, ""); - } - this.body = body; - } - - build_play_button(button_data) { - this.render_play_button = { - tag: "button", - class: "play-button", - contents: t("Jouer"), - onclick: this.handle_click_play.bind(this, button_data.filename, button_data.engine) - }; - } - - load_and_run_mentalo_game(filename, button_element) { - const button_text = button_element.innerHTML; - button_element.innerHTML = "Loading ..."; - button_element.style.pointerEvents = "none"; - - fetch_json_or_error_text(`${data_url}/${filename}`) - .then(game_data => { - const container = document.createElement("div"); - container.style.position = "fixed"; - container.style.top = 0; - container.style.left = 0; - container.style.right = 0; - container.style.bottom = 0; - container.style.zIndex = 10; - container.style.display = "flex"; - container.style.justifyContent = "center"; - container.style.alignItems = "center"; - - container.id = "kuadrado-tmp-game-player-container"; - document.body.appendChild(container); - document.body.style.overflow = "hidden"; - - const engine = new MentaloEngine({ - game_data, - fullscreen: true, - frame_rate: 30, - container, - on_quit_game: () => { - container.remove(); - document.body.style.overflow = "visible"; - } - }); - - engine.init(); - engine.run_game(); - }) - .catch(err => console.log(err)) - .finally(() => { - button_element.innerHTML = button_text; - button_element.style.pointerEvents = "unset"; - }); - } - - handle_click_play(filename, engine, e) { - switch (engine) { - case "mentalo": - this.load_and_run_mentalo_game(filename, e.target); - break; - default: - console.log("Error, unkown engine") - return; - } - } - - render() { - const { - title, - subtitle, - images, - details, - } = this.props; - - return { - tag: "article", - typeof: "VideoGame", - additionalType: "Article", - class: "game-article", - contents: [ - { - tag: "h2", - property: "name", - class: "game-title", - contents: title, - }, - { - tag: "div", - class: "game-banner", - contents: [ - { tag: "img", class: "pixelated", src: `${images_url}/${images[0]}` }, - ], - }, - { - tag: "h3", - class: "game-subtitle", - contents: subtitle, - property: "alternativeHeadline", - }, - { - tag: "div", - class: "game-description", - property: "description", - contents: [{ tag: "p", style_rules: { margin: 0 }, contents: this.body }] - .concat(this.render_play_button - ? [this.render_play_button] - : []), - }, - new ImageCarousel({ images: images.map(img => `${images_url}/${img}`) }).render(), - details.length > 0 && { - tag: "div", - class: "article-details", - contents: [ - { - tag: "h2", - contents: "Details", - }, - { - tag: "ul", - class: "details-list", - contents: details.map(detail => { - return { - tag: "li", - class: "detail", - contents: [ - { tag: "label", contents: detail.label }, - { - tag: "div", - class: "detail-value", - contents: detail.value - }, - ], - }; - }), - }, - ], - }, - ], - }; - } -} - -module.exports = GameArticle; diff --git a/website/src/pages/games/components/game-articles.js b/website/src/pages/games/components/game-articles.js index 194904c6361acd901409d1e0a2d7427a79c5e24f..4ba58ab848d998cefa73e87ad64b875bb54184c3 100644 --- a/website/src/pages/games/components/game-articles.js +++ b/website/src/pages/games/components/game-articles.js @@ -1,7 +1,7 @@ "use strict"; const { loadArticles, sortArticles } = require("../../../lib/article-utils"); -const GameArticle = require("./game-article"); +const GameThumb = require("./game-thumb"); const translator = require("ks-cheap-translator"); class GameArticles { @@ -45,7 +45,7 @@ class GameArticles { id: this.id, contents: articles.length > 0 - ? articles.map(article => new GameArticle({ ...article }).render()) + ? articles.map(article => new GameThumb({ ...article }).render()) : [this.renderPlaceholder()], }; } diff --git a/website/src/pages/games/components/game-thumb.js b/website/src/pages/games/components/game-thumb.js new file mode 100644 index 0000000000000000000000000000000000000000..f2c31385bf7ba2f8abbb2d0531906a1b4ece4974 --- /dev/null +++ b/website/src/pages/games/components/game-thumb.js @@ -0,0 +1,54 @@ +"use strict"; + +const { images_url } = require("../../../../../admin-frontend/src/constants"); + +class GameThumb { + constructor(props) { + this.props = props; + } + + render() { + const { + title, + subtitle, + images, + metadata, + } = this.props; + + return { + tag: metadata.view_uri ? "a" : "div", + href: metadata.view_uri, + target: "_blank", + class: "game-thumb", + style_rules: { + backgroundImage: `url(${images_url}/${images[0]})`, + }, + contents: [ + { + tag: "div", + class: "text-card", + contents: [ + { + tag: "strong", + class: "game-title", + contents: title, + }, + + { + tag: "strong", + class: "game-subtitle", + contents: subtitle, + }, + { + tag: "span", + class: "game-description", + contents: metadata.description + }, + ] + }, + ], + }; + } +} + +module.exports = GameThumb; diff --git a/website/src/pages/games/games.scss b/website/src/pages/games/games.scss index d06b1586df318b1305215460ec9d56da78780ef1..d8ed283223d19c14b456988606b77fd92354ad5b 100644 --- a/website/src/pages/games/games.scss +++ b/website/src/pages/games/games.scss @@ -1,115 +1,78 @@ #games-page { - .game-articles { - article { - display: grid; - grid-template-columns: 0.7fr 1fr; - gap: 30px 50px; - margin: 20px 0; + #game-articles-section { + display: grid; + gap: 20px; + grid-template-columns: 1fr 1fr; + padding: 40px; + + .game-thumb { + min-width: 350px; + overflow: hidden; + height: 350px; + background-position: center center; + background-size: cover; + background-repeat: no-repeat; + image-rendering: pixelated; + image-rendering: -moz-crisp-edges; + image-rendering: crisp-edges; + + padding: 15px; + transition: transform 0.3s, opacity 0.3s; + display: flex; + align-items: flex-end; + opacity: .9; + + &:hover { + transform: scale(1.03); + opacity: 1; + } - &.game-article { - grid-template-rows: repeat(7, auto); + .text-card { + background-color: #0008; + display: flex; + flex-direction: column; + padding: 20px; width: 100%; - .game-title { - grid-column: 1 / span 2; - margin: 0; - padding: 30px 20px; - color: $light_2; - font-size: 35px; - font-style: italic; - } - - .game-banner { - grid-column: 1 / span 2; - @include flex-center-col; - background-color: black; - overflow: hidden; - height: 300px; - - img { - width: 100%; - } + * { + color: $light_0; } - .game-tags { - display: flex; - gap: 10px; - flex-wrap: wrap; - margin: 10px 20px; - - span { - font-size: 12px; - padding: 4px; - background-color: $light_0; - color: $medium_grey; - border-radius: 5px; - font-weight: 600; - } + .game-title { + font-size: 30px; } .game-subtitle { - grid-column: 1; - margin: 10px 20px; - color: $medium_grey; + font-size: 17px; + text-align: center; } .game-description { - grid-column: 1; - text-align: justify; - margin: 10px 20px 30px; - } - - .image-carousel { - grid-column: 2; - grid-row: 3 / span 4; - height: 400px; + font-style: italic; + padding: 10px 0; } } - &.placeholder { - * { - background-color: $light_0; - } + } - height: 400px; - } + @media screen and (max-width:790px) { + grid-template-columns: 1fr; + padding: 20px; - @media screen and (max-width: $screen_l) { - grid-template-columns: 1fr; + .game-thumb { + min-width: unset; + width: 100%; + height: 250px; - &.game-article { - grid-template-rows: repeat(6, auto); + .text-card { .game-title { - grid-column: 1; - padding: 0; - font-size: 25px; + font-size: 20px; } - .game-banner { - grid-column: 1; - margin: 0 -20px; - height: 200px; + .game-subtitle { + font-size: 14px; } - - .image-carousel { - grid-column: 1; - grid-row: 3; - margin: 0 -20px; - } - } - } - - .play-button { - border: none; - background-color: unset; - font-weight: bold; - font-size: 20px; - cursor: pointer; - color: $blue_2; - - &:hover { - color: $blue_3; } } } diff --git a/website/src/style.scss b/website/src/style.scss index 9f4e0434af77a335b8fd3efcb35ac247e9813083..2998612270742b2aeab06d856d23e63b05e5550c 100644 --- a/website/src/style.scss +++ b/website/src/style.scss @@ -7,7 +7,8 @@ body { line-height: 1.3em; } - font-family: Arial, + font-family: Lato, + Arial, Helvetica, sans-serif; margin: 0; @@ -19,11 +20,11 @@ body { } a { - color: $blue_2; + color: $blue_2 !important; text-decoration: none; &:hover { - color: $blue_3; + color: $blue_3 !important; } }