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>
</yla-tooltip>
<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">
<a>
<i class="fas fa-moon fa-fw"></i>
@ -61,65 +66,67 @@
<i class="fas fa-search fa-fw"></i>
</a>
</div>
<div id="grid">
<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 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)}}
<div id="content" ref="content">
<div id="grid" :class="{list: listView}">
<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 title="Edit bookmark" @click="showDialogEdit(idx)">
<i class="fas fa-pencil-alt"></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 title="Delete bookmark" @click="showDialogDelete([idx])">
<i class="fas fa-trash-alt"></i>
<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 title="Update cache" @click="showDialogUpdateCache([idx])">
<i class="fas fa-cloud-download-alt"></i>
<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 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 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>
<yla-dialog v-bind="dialog"></yla-dialog>
@ -148,6 +155,8 @@
search: '',
page: 0,
maxPage: 0,
listView: false,
nightMode: false
},
computed: {
visibleBookmarks() {
@ -183,7 +192,7 @@
this.bookmarks = response.data;
this.page = 0;
this.maxPage = Math.ceil(this.bookmarks.length / pageSize) - 1;
document.getElementById('grid').scrollTop = 0;
this.$refs.content.scrollTop = 0;
})
.catch((error) => {
var errorMsg = (error.response ? error.response.data : error.message).trim();
@ -191,7 +200,11 @@
this.showErrorDialog(errorMsg);
});
},
reloadData() {},
reloadData() {
if (this.loading) return;
this.search = '';
this.loadData();
},
changePage(target) {
target = parseInt(target, 10) || 0;
@ -199,7 +212,12 @@
else if (target <= 0) this.page = 0;
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() {
this.showDialog({
@ -403,6 +421,14 @@
}
},
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();
}
});

View file

@ -198,141 +198,195 @@ a {
}
}
}
#grid {
#content {
overflow-y: auto;
display: grid;
grid-template-rows: auto;
grid-template-columns: repeat(4, 1fr);
grid-gap: 16px;
padding: 16px 16px 0;
.bookmark {
display: flex;
flex-flow: column nowrap;
min-width: 0;
border: 1px solid @border;
background-color: @contentBg;
height: 100%;
&:hover,
&:focus {
.bookmark-menu>a {
display: block;
#grid {
display: grid;
grid-template-rows: auto;
grid-template-columns: repeat(4, 1fr);
grid-gap: 16px;
padding: 16px 16px 0;
.bookmark {
display: flex;
flex-flow: column nowrap;
min-width: 0;
border: 1px solid @border;
background-color: @contentBg;
height: 100%;
&:hover,
&:focus {
.bookmark-menu>a {
display: block;
}
}
}
.bookmark-content {
display: block;
position: relative;
flex: 1;
cursor: default;
&[href] {
cursor: pointer;
&:hover,
&:focus {
.title {
color: @main;
.bookmark-content {
display: block;
flex: 1;
cursor: default;
&[href] {
cursor: pointer;
&:hover,
&:focus {
.title {
color: @main;
}
}
}
}
img {
width: 100%;
max-height: 13em;
object-fit: cover;
margin-bottom: 8px;
}
.title {
text-overflow: ellipsis;
word-wrap: break-word;
overflow: hidden;
font-size: 1.2em;
line-height: 1.3em;
max-height: 5.2em;
font-weight: 600;
padding: 0 16px;
color: @color;
&:first-child {
margin-top: 16px;
img {
width: 100%;
max-height: 13em;
object-fit: cover;
margin-bottom: 8px;
}
.title {
text-overflow: ellipsis;
word-wrap: break-word;
overflow: hidden;
font-size: 1.2em;
line-height: 1.3em;
max-height: 5.2em;
font-weight: 600;
padding: 0 16px;
color: @color;
&:first-child {
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 {
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;
.bookmark-menu {
padding: 8px 16px 16px;
display: flex;
flex-flow: row nowrap;
min-width: 0;
min-height: 0;
align-items: center;
a {
color: @colorLink;
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 {
padding: 8px 16px 16px;
.pagination-box {
grid-column-start: 1;
grid-column-end: -1;
display: flex;
flex-flow: row nowrap;
min-width: 0;
min-height: 0;
align-items: center;
a {
padding: 8px;
color: @colorLink;
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;
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;
}
}
}
}
.pagination-box {
grid-column-start: 1;
grid-column-end: -1;
display: flex;
flex-flow: row nowrap;
a {
padding: 8px;
color: @colorLink;
&:hover,
&:focus {
color: @main;
#grid-padding {
grid-column-start: 1;
grid-column-end: -1;
min-height: 1px;
}
&.list {
grid-gap: 0;
grid-template-columns: 1fr;
max-width: 1280px;
.pagination-box {
margin-top: 16px;
&: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;
}
}
}