Style books like a bookshelf with different widths and heights
This commit is contained in:
parent
3564783664
commit
a117a9fce8
|
@ -1,4 +1,110 @@
|
|||
.is-clickable,
|
||||
.modal-button {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
/* Bookshelf Styling */
|
||||
.bookshelf {
|
||||
border: 4px solid saddlebrown;
|
||||
background: sienna;
|
||||
}
|
||||
|
||||
.book-slot {
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
width: 100px;
|
||||
height: 300px; /* The tallest a book could be */
|
||||
border-bottom: 4px solid saddlebrown;
|
||||
margin: 10px 0 -4px !important;
|
||||
}
|
||||
.book-slot.is-thin {
|
||||
width: 80px;
|
||||
}
|
||||
.book-slot.is-thick {
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
.book {
|
||||
position: absolute;
|
||||
overflow: visible;
|
||||
top: 30px;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
.book.is-short {
|
||||
top: 60px;
|
||||
}
|
||||
.book.is-tall {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.book .spine {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
background: #c0ffee;
|
||||
border: 1px solid #aaa;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin-bottom: 4px;
|
||||
z-index: 1;
|
||||
transition: all 0.25s;
|
||||
}
|
||||
.book:hover .spine {
|
||||
width: 120%;
|
||||
margin-left: -10%;
|
||||
height: 120%;
|
||||
margin-top: -20%;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.spine .text-container {
|
||||
width: 260px;
|
||||
transform: rotate(90deg) translateX(108px) translateY(58px);
|
||||
}
|
||||
.spine .title {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
.spine .subtitle {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.book-slot.is-thin .book .spine .text-container {
|
||||
transform: rotate(90deg) translateX(108px) translateY(78px);
|
||||
}
|
||||
.book-slot.is-thick .book .spine .text-container {
|
||||
transform: rotate(90deg) translateX(108px) translateY(40px);
|
||||
}
|
||||
|
||||
.book.is-short .spine .text-container {
|
||||
width: 230px;
|
||||
transform: rotate(90deg) translateX(98px) translateY(40px);
|
||||
}
|
||||
.book-slot.is-thin .book.is-short .spine .text-container {
|
||||
transform: rotate(90deg) translateX(98px) translateY(60px);
|
||||
}
|
||||
.book-slot.is-thick .book.is-short .spine .text-container {
|
||||
transform: rotate(90deg) translateX(98px) translateY(20px);
|
||||
}
|
||||
.book.is-short .spine .title {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
.book.is-short .spine .subtitle {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
.book.is-tall .spine .text-container {
|
||||
width: 290px;
|
||||
transform: rotate(90deg) translateX(120px) translateY(78px);
|
||||
}
|
||||
.book-slot.is-thin .book.is-tall .spine .text-container {
|
||||
transform: rotate(90deg) translateX(120px) translateY(98px);
|
||||
}
|
||||
.book-slot.is-thick .book.is-tall .spine .text-container {
|
||||
transform: rotate(90deg) translateX(120px) translateY(58px);
|
||||
}
|
||||
.book.is-tall .spine .title {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
.book.is-tall .spine .subtitle {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
|
|
@ -3,20 +3,27 @@ const fs = require('fs');
|
|||
const snarkdown = require('snarkdown');
|
||||
const fecha = require('fecha');
|
||||
|
||||
const settings = require('../settings.json');
|
||||
|
||||
module.exports = function (app) {
|
||||
app.server.get('/', (req, res) => {
|
||||
const files = fs.readdirSync(app.fileLocation).filter(fileName => fileName.includes('.json'))
|
||||
.map(fileName => { // Cache the file data so sorting doesn't need to re-check each file
|
||||
return { name: fileName, time: fs.statSync(path.resolve(app.fileLocation, fileName)).mtime.getTime() };
|
||||
}).sort((a, b) => a.time - b.time).map(v => v.name); // Sort from oldest to newest.
|
||||
const stats = fs.statSync(path.resolve(app.fileLocation, fileName));
|
||||
return {
|
||||
name: fileName,
|
||||
size: stats.size / (1000 * 1000),
|
||||
time: stats.mtime.getTime(),
|
||||
};
|
||||
}).sort((a, b) => a.time - b.time); // Sort from oldest to newest.
|
||||
|
||||
let books = files.map(fileName => {
|
||||
const bookData = JSON.parse(fs.readFileSync(path.resolve(app.fileLocation, fileName), 'utf8'));
|
||||
let books = files.map(fileDetails => {
|
||||
const bookData = JSON.parse(fs.readFileSync(path.resolve(app.fileLocation, fileDetails.name), 'utf8'));
|
||||
if (bookData.hasOwnProperty('fileName')) return '';
|
||||
bookData.author = bookData.author ? bookData.author : '<em>author not provided</em>';
|
||||
bookData.contributor = bookData.contributor ? bookData.contributor : 'Anonymous';
|
||||
|
||||
const id = fileName.replace('.json', '');
|
||||
const id = fileDetails.name.replace('.json', '');
|
||||
const confirmId = 'confirm_' + id;
|
||||
const added = fecha.format(new Date(bookData.added), 'hh:mm:ssA on dddd MMMM Do, YYYY');
|
||||
const modal = app.templater.fill('./templates/elements/modalCard.html', {
|
||||
|
@ -37,10 +44,13 @@ module.exports = function (app) {
|
|||
}),
|
||||
footer: '<a class="button close">Close</a> <a class="button is-success modal-button" data-modal="' + confirmId + '">Take Book</a>',
|
||||
});
|
||||
const maxSize = settings.maxFileSize > 0 ? settings.maxFileSize : 10;
|
||||
return app.templater.fill('./templates/elements/book.html', {
|
||||
id,
|
||||
title: bookData.title,
|
||||
author: bookData.author,
|
||||
thickness: fileDetails.size > (maxSize * 0.3) ? 'is-thick' : (fileDetails.size < (maxSize * 0.6) ? 'is-thin' : ''),
|
||||
tallness: bookData.title.length > 15 ? 'is-tall' : (bookData.title.length < 8 ? 'is-short' : ''),
|
||||
fileType: bookData.fileType,
|
||||
modal,
|
||||
});
|
||||
|
@ -50,7 +60,7 @@ module.exports = function (app) {
|
|||
books = '<div class="column"><div class="content">The shelf is empty. Would you like to <a href="/give">add a book</a>?</div></div>';
|
||||
}
|
||||
|
||||
const body = '<h2 class="title">Available Books</h2><div class="columns is-multiline">' + books + '</div>';
|
||||
const body = '<h2 class="title">Available Books</h2><div class="bookshelf columns is-gapless is-multiline">' + books + '</div>';
|
||||
const html = app.templater.fill('./templates/htmlContainer.html', {
|
||||
title: 'View',
|
||||
resourcePath: (req.url.substr(-1) === '/' ? '../' : './'),
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
<div class="column is-one-quarter" id="book_{{id}}">
|
||||
<div class="box modal-button has-text-centered" data-modal="{{id}}">
|
||||
<h2 class="title is-4">{{title}}</h2>
|
||||
<h4 class="subtitle">{{author}}</h4>
|
||||
<div class="tags has-addons">
|
||||
<span class="tag">File Format</span>
|
||||
<span class="tag is-info">{{fileType}}</span>
|
||||
<div class="book-slot {{thickness}} column is-narrow is-paddingless" id="book_{{id}}">
|
||||
<div class="book {{tallness}} modal-button" data-modal="{{id}}">
|
||||
<div class="spine">
|
||||
<div class="text-container">
|
||||
<h2 class="title">{{title}}</h2>
|
||||
<h4 class="subtitle">{{author}}</h4>
|
||||
</div>
|
||||
<!-- div class="tags has-addons">
|
||||
<span class="tag">File Format</span>
|
||||
<span class="tag is-info">{{fileType}}</span>
|
||||
</div -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
<div class="column is-one-quarter" id="book_{{id}}">
|
||||
<div class="box modal-button has-text-centered" data-modal="{{id}}">
|
||||
<h2 class="title is-4">{{title}}</h2>
|
||||
<h4 class="subtitle">{{author}}</h4>
|
||||
<div class="tags has-addons">
|
||||
<span class="tag">File Format</span>
|
||||
<span class="tag is-info">{{fileType}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{modal}}
|
||||
</div>
|
Loading…
Reference in New Issue