Add switch to list view in web ui

This commit is contained in:
Radhi Fadlillah 2018-05-21 16:37:48 +07:00
parent 7f31d67043
commit f2afeab26f
4 changed files with 260 additions and 180 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -43,6 +43,11 @@
</a> </a>
</yla-tooltip> </yla-tooltip>
<div class="spacer"></div> <div class="spacer"></div>
<yla-tooltip placement="right" :content="listView ? 'Switch to grid view' : 'Switch to list view'">
<a @click="toggleListView">
<i class="fas fa-fw" :class="listView ? 'fa-th-large' : 'fa-th-list'"></i>
</a>
</yla-tooltip>
<yla-tooltip placement="right" content="Toggle night mode"> <yla-tooltip placement="right" content="Toggle night mode">
<a> <a>
<i class="fas fa-moon fa-fw"></i> <i class="fas fa-moon fa-fw"></i>
@ -61,65 +66,67 @@
<i class="fas fa-search fa-fw"></i> <i class="fas fa-search fa-fw"></i>
</a> </a>
</div> </div>
<div id="grid"> <div id="content" ref="content">
<div class="pagination-box" v-if="maxPage > 0"> <div id="grid" :class="{list: listView}">
<p>Page</p> <div class="pagination-box" v-if="maxPage > 0">
<input type="text" placeholder="1" :value="page+1" @focus="$event.target.select()" @keyup.enter="changePage($event.target.value-1)"> <p>Page</p>
<p>{{maxPage+1}}</p> <input type="text" placeholder="1" :value="page+1" @focus="$event.target.select()" @keyup.enter="changePage($event.target.value-1)">
<div class="spacer"></div> <p>{{maxPage+1}}</p>
<a v-if="page > 1" title="Go to first page" @click="changePage(0)"> <div class="spacer"></div>
<i class="fas fa-fw fa-angle-double-left"></i> <a v-if="page > 1" title="Go to first page" @click="changePage(0)">
</a> <i class="fas fa-fw fa-angle-double-left"></i>
<a v-if="page > 0" title="Go to previous page" @click="changePage(page-1)">
<i class="fa fa-fw fa-angle-left"></i>
</a>
<a v-if="page < maxPage" title="Go to next page" @click="changePage(page+1)">
<i class="fa fa-fw fa-angle-right"></i>
</a>
<a v-if="page < maxPage - 1" title="Go to last page" @click="changePage(maxPage)">
<i class="fas fa-fw fa-angle-double-right"></i>
</a>
</div>
<div class="bookmark" v-for="(book, idx) in visibleBookmarks">
<a class="bookmark-content" :href="book.hasContent ? '/bookmark/'+book.id : null" :title="book.hasContent ? 'View cache' : null" target="_blank">
<img v-if="book.imageURL !== ''" :src="book.imageURL">
<p class="title">{{book.title}}</p>
<p class="excerpt" v-if="book.imageURL === ''">{{book.excerpt}}</p>
</a>
<div class="bookmark-menu">
<a class="url" title="View original" :href="book.url" target="_blank">
{{getHostname(book.url)}}
</a> </a>
<a title="Edit bookmark" @click="showDialogEdit(idx)"> <a v-if="page > 0" title="Go to previous page" @click="changePage(page-1)">
<i class="fas fa-pencil-alt"></i> <i class="fa fa-fw fa-angle-left"></i>
</a> </a>
<a title="Delete bookmark" @click="showDialogDelete([idx])"> <a v-if="page < maxPage" title="Go to next page" @click="changePage(page+1)">
<i class="fas fa-trash-alt"></i> <i class="fa fa-fw fa-angle-right"></i>
</a> </a>
<a title="Update cache" @click="showDialogUpdateCache([idx])"> <a v-if="page < maxPage - 1" title="Go to last page" @click="changePage(maxPage)">
<i class="fas fa-cloud-download-alt"></i> <i class="fas fa-fw fa-angle-double-right"></i>
</a> </a>
</div> </div>
<div class="bookmark" v-for="(book, idx) in visibleBookmarks">
<a class="bookmark-content" :href="book.hasContent ? '/bookmark/'+book.id : null" :title="book.hasContent ? 'View cache' : null" target="_blank">
<img v-if="book.imageURL !== ''" :src="book.imageURL">
<p class="title">{{book.title}}</p>
<p class="excerpt" v-if="book.imageURL === ''">{{book.excerpt}}</p>
</a>
<div class="bookmark-menu">
<a class="url" title="View original" :href="book.url" target="_blank">
{{getHostname(book.url)}}
</a>
<a title="Edit bookmark" @click="showDialogEdit(idx)">
<i class="fas fa-pencil-alt"></i>
</a>
<a title="Delete bookmark" @click="showDialogDelete([idx])">
<i class="fas fa-trash-alt"></i>
</a>
<a title="Update cache" @click="showDialogUpdateCache([idx])">
<i class="fas fa-cloud-download-alt"></i>
</a>
</div>
</div>
<div class="pagination-box" v-if="maxPage > 0">
<p>Page</p>
<input type="text" placeholder="1" :value="page+1" @focus="$event.target.select()" @keyup.enter="changePage($event.target.value-1)">
<p>{{maxPage+1}}</p>
<div class="spacer"></div>
<a v-if="page > 1" title="Go to first page" @click="changePage(0)">
<i class="fas fa-fw fa-angle-double-left"></i>
</a>
<a v-if="page > 0" title="Go to previous page" @click="changePage(page-1)">
<i class="fa fa-fw fa-angle-left"></i>
</a>
<a v-if="page < maxPage" title="Go to next page" @click="changePage(page+1)">
<i class="fa fa-fw fa-angle-right"></i>
</a>
<a v-if="page < maxPage - 1" title="Go to last page" @click="changePage(maxPage)">
<i class="fas fa-fw fa-angle-double-right"></i>
</a>
</div>
<div id="grid-padding"></div>
</div> </div>
<div class="pagination-box" v-if="maxPage > 0">
<p>Page</p>
<input type="text" placeholder="1" :value="page+1" @focus="$event.target.select()" @keyup.enter="changePage($event.target.value-1)">
<p>{{maxPage+1}}</p>
<div class="spacer"></div>
<a v-if="page > 1" title="Go to first page" @click="changePage(0)">
<i class="fas fa-fw fa-angle-double-left"></i>
</a>
<a v-if="page > 0" title="Go to previous page" @click="changePage(page-1)">
<i class="fa fa-fw fa-angle-left"></i>
</a>
<a v-if="page < maxPage" title="Go to next page" @click="changePage(page+1)">
<i class="fa fa-fw fa-angle-right"></i>
</a>
<a v-if="page < maxPage - 1" title="Go to last page" @click="changePage(maxPage)">
<i class="fas fa-fw fa-angle-double-right"></i>
</a>
</div>
<div id="grid-padding"></div>
</div> </div>
</div> </div>
<yla-dialog v-bind="dialog"></yla-dialog> <yla-dialog v-bind="dialog"></yla-dialog>
@ -148,6 +155,8 @@
search: '', search: '',
page: 0, page: 0,
maxPage: 0, maxPage: 0,
listView: false,
nightMode: false
}, },
computed: { computed: {
visibleBookmarks() { visibleBookmarks() {
@ -183,7 +192,7 @@
this.bookmarks = response.data; this.bookmarks = response.data;
this.page = 0; this.page = 0;
this.maxPage = Math.ceil(this.bookmarks.length / pageSize) - 1; this.maxPage = Math.ceil(this.bookmarks.length / pageSize) - 1;
document.getElementById('grid').scrollTop = 0; this.$refs.content.scrollTop = 0;
}) })
.catch((error) => { .catch((error) => {
var errorMsg = (error.response ? error.response.data : error.message).trim(); var errorMsg = (error.response ? error.response.data : error.message).trim();
@ -191,7 +200,11 @@
this.showErrorDialog(errorMsg); this.showErrorDialog(errorMsg);
}); });
}, },
reloadData() {}, reloadData() {
if (this.loading) return;
this.search = '';
this.loadData();
},
changePage(target) { changePage(target) {
target = parseInt(target, 10) || 0; target = parseInt(target, 10) || 0;
@ -199,7 +212,12 @@
else if (target <= 0) this.page = 0; else if (target <= 0) this.page = 0;
else this.page = target; else this.page = target;
document.getElementById('grid').scrollTop = 0; this.$refs.content.scrollTop = 0;
},
toggleListView() {
this.listView = !this.listView;
this.$refs.content.scrollTop = 0;
localStorage.setItem('shiori-list-view', this.listView ? '1' : '0');
}, },
showDialogAdd() { showDialogAdd() {
this.showDialog({ this.showDialog({
@ -403,6 +421,14 @@
} }
}, },
mounted() { mounted() {
// Read config from local storage
var listView = localStorage.getItem('shiori-list-view'),
nightMode = localStorage.getItem('shiori-night-mode');
this.listView = listView === '1';
this.nightMode = nightMode === '1';
// Load data
this.loadData(); this.loadData();
} }
}); });

View file

@ -198,141 +198,195 @@ a {
} }
} }
} }
#grid { #content {
overflow-y: auto; overflow-y: auto;
display: grid; #grid {
grid-template-rows: auto; display: grid;
grid-template-columns: repeat(4, 1fr); grid-template-rows: auto;
grid-gap: 16px; grid-template-columns: repeat(4, 1fr);
padding: 16px 16px 0; grid-gap: 16px;
.bookmark { padding: 16px 16px 0;
display: flex; .bookmark {
flex-flow: column nowrap; display: flex;
min-width: 0; flex-flow: column nowrap;
border: 1px solid @border; min-width: 0;
background-color: @contentBg; border: 1px solid @border;
height: 100%; background-color: @contentBg;
&:hover, height: 100%;
&:focus { &:hover,
.bookmark-menu>a { &:focus {
display: block; .bookmark-menu>a {
display: block;
}
} }
} .bookmark-content {
.bookmark-content { display: block;
display: block; flex: 1;
position: relative; cursor: default;
flex: 1; &[href] {
cursor: default; cursor: pointer;
&[href] { &:hover,
cursor: pointer; &:focus {
&:hover, .title {
&:focus { color: @main;
.title { }
color: @main;
} }
} }
} img {
img { width: 100%;
width: 100%; max-height: 13em;
max-height: 13em; object-fit: cover;
object-fit: cover; margin-bottom: 8px;
margin-bottom: 8px; }
} .title {
.title { text-overflow: ellipsis;
text-overflow: ellipsis; word-wrap: break-word;
word-wrap: break-word; overflow: hidden;
overflow: hidden; font-size: 1.2em;
font-size: 1.2em; line-height: 1.3em;
line-height: 1.3em; max-height: 5.2em;
max-height: 5.2em; font-weight: 600;
font-weight: 600; padding: 0 16px;
padding: 0 16px; color: @color;
color: @color; &:first-child {
&:first-child { margin-top: 16px;
margin-top: 16px; }
}
.excerpt {
color: @color;
margin-top: 8px;
padding: 0 16px;
text-overflow: ellipsis;
word-wrap: break-word;
overflow: hidden;
font-size: 0.9em;
line-height: 1.5em;
max-height: 10.5em;
} }
} }
.excerpt { .bookmark-menu {
color: @color; padding: 8px 16px 16px;
margin-top: 8px; display: flex;
padding: 0 16px; flex-flow: row nowrap;
text-overflow: ellipsis; min-width: 0;
word-wrap: break-word; min-height: 0;
overflow: hidden; align-items: center;
font-size: 0.9em; a {
line-height: 1.5em; color: @colorLink;
max-height: 10.5em; flex-shrink: 0;
opacity: 0.8;
display: none;
font-size: 0.9em;
&:not(:last-child) {
margin-right: 12px;
}
&:hover,
&:focus {
color: @main;
opacity: 1;
}
}
.url {
flex: 1 0;
opacity: 1;
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
line-height: 21px;
}
} }
} }
.bookmark-menu { .pagination-box {
padding: 8px 16px 16px; grid-column-start: 1;
grid-column-end: -1;
display: flex; display: flex;
flex-flow: row nowrap; flex-flow: row nowrap;
min-width: 0;
min-height: 0;
align-items: center;
a { a {
padding: 8px;
color: @colorLink; color: @colorLink;
flex-shrink: 0;
opacity: 0.8;
display: none;
font-size: 0.9em;
&:not(:last-child) {
margin-right: 12px;
}
&:hover, &:hover,
&:focus { &:focus {
color: @main; color: @main;
opacity: 1;
} }
} }
.url { input {
flex: 1 0; width: 40px;
opacity: 1; padding: 8px;
display: block; text-align: center;
white-space: nowrap; font-size: 0.9em;
overflow: hidden; border: 1px solid @border;
text-overflow: ellipsis; margin: 0 8px;
line-height: 21px; }
p {
font-size: 0.9em;
color: @colorLink;
line-height: 37px;
font-weight: 600;
&:last-of-type::before {
content: "/";
margin-right: 8px;
}
} }
} }
} #grid-padding {
.pagination-box { grid-column-start: 1;
grid-column-start: 1; grid-column-end: -1;
grid-column-end: -1; min-height: 1px;
display: flex; }
flex-flow: row nowrap; &.list {
a { grid-gap: 0;
padding: 8px; grid-template-columns: 1fr;
color: @colorLink; max-width: 1280px;
&:hover, .pagination-box {
&:focus { margin-top: 16px;
color: @main; &:first-of-type {
margin-top: 0;
margin-bottom: 16px;
}
}
.bookmark {
border-top-width: 0;
border-bottom-width: 1px;
position: relative;
padding-left: 100px;
background-color: #FAFAFA;
&:first-of-type {
border-top-width: 1px;
}
.bookmark-content {
display: flex;
flex-flow: column nowrap;
background-color: @contentBg;
border-left: 1px solid @border;
padding: 16px 24px 8px;
img {
position: absolute;
top: 0;
left: 0;
width: 100px;
height: 100%;
margin-bottom: 0;
}
.title {
padding: 0;
margin-top: 0;
white-space: nowrap;
}
.excerpt {
display: none;
}
}
.bookmark-menu {
background-color: @contentBg;
border-left: 1px solid @border;
padding: 0 24px 16px;
}
}
#grid-padding {
min-height: 16px
} }
} }
input {
width: 40px;
padding: 8px;
text-align: center;
font-size: 0.9em;
border: 1px solid @border;
margin: 0 8px;
}
p {
font-size: 0.9em;
color: @colorLink;
line-height: 37px;
font-weight: 600;
&:last-of-type::before {
content: "/";
margin-right: 8px;
}
}
}
#grid-padding {
grid-column-start: 1;
grid-column-end: -1;
min-height: 1px;
} }
} }
} }