diff --git a/src/css/viewer.css b/src/css/viewer.css index 9ae0d42..0dd7377 100644 --- a/src/css/viewer.css +++ b/src/css/viewer.css @@ -1,5 +1,9 @@ @import "vendor/normalize.css"; +html { + font-size: calc(60% + 3vmin); +} + html, body, #presenter { @@ -24,3 +28,67 @@ body, width: 100%; height: 100%; } + +.controls { + display: flex; + align-items: center; + justify-content: center; + position: absolute; + left: 0; + right: 0; + bottom: 0; + padding: 1rem; + background: linear-gradient( + 0deg, + rgba(255, 255, 255, 0.5) 0%, + rgba(255, 255, 255, 0) 100% + ); +} + +@media (hover: hover) { + .controls { + opacity: 0.3; + transition: opacity 1s; + } + .controls:hover { + opacity: 1; + transition: opacity 0.2s; + } +} + +.controls button { + display: flex; + align-items: center; + justify-content: center; + width: 2rem; + height: 2rem; + border: none; + border-radius: 50%; + color: #fff; + padding: 0.5rem; + cursor: pointer; + background-color: #0003; +} + +@media (hover: hover) { + .controls button { + background-color: #0000; + transition: background-color 0.2s; + } + + .controls:hover button { + background-color: #0003; + } + + .controls button:hover { + background-color: #0005; + } +} + +.controls button svg { + height: 100%; +} + +.controls .progress { + margin: 0 2rem; +} diff --git a/src/js/viewer/index.js b/src/js/viewer/index.js index e04c408..2e9be13 100644 --- a/src/js/viewer/index.js +++ b/src/js/viewer/index.js @@ -40,8 +40,8 @@ export const init = (tale, imgData) => { mount( document.querySelector("#app"), withInputSignals( - () => connect("tale"), - tale => viewer(tale, imgData), + () => [connect("tale"), connect("slide/active")], + ([tale, activeSlide]) => viewer(tale, imgData, activeSlide), ), ); }; diff --git a/src/js/viewer/viewer.js b/src/js/viewer/viewer.js index a1ecedd..212da64 100644 --- a/src/js/viewer/viewer.js +++ b/src/js/viewer/viewer.js @@ -2,22 +2,29 @@ import { h } from "flyps-dom-snabbdom"; import { viewport } from "../viewport"; import i18n from "../i18n"; +import { chevronLeft, chevronRight } from "../icons"; +import { trigger } from "flyps"; const notFound = () => h("div", i18n("editor.unwritten-tale")); -export const viewer = (tale, imgData) => { +export const viewer = (tale, imgData, activeSlide) => { if (!tale) { return notFound(); } - return h("div#presenter", [ - viewport( - tale.dimensions.width, - tale.dimensions.height, - {}, - poster(imgData, tale.fileType), - ), - ]); + return h( + "div#presenter", + { on: { click: () => trigger("slide/fly-to-next") } }, + [ + viewport( + tale.dimensions.width, + tale.dimensions.height, + {}, + poster(imgData, tale.fileType), + ), + controls(tale, activeSlide), + ], + ); }; const poster = (imgData, fileType) => { @@ -28,3 +35,20 @@ const poster = (imgData, fileType) => { }, }); }; + +const controls = (tale, activeSlide) => { + const currentSlide = activeSlide === undefined ? 0 : activeSlide + 1; + return h("div.controls", { on: { click: ev => ev.stopPropagation() } }, [ + h( + "button", + { on: { click: () => trigger("slide/fly-to-prev") } }, + chevronLeft(), + ), + h("div.progress", `${currentSlide} / ${tale.slides.length}`), + h( + "button", + { on: { click: () => trigger("slide/fly-to-next") } }, + chevronRight(), + ), + ]); +};