mirror of
https://github.com/go-shiori/shiori.git
synced 2025-02-21 22:43:22 +08:00
Implement initial setting page
This commit is contained in:
parent
1b74d555ad
commit
fcc77e2db8
11 changed files with 279 additions and 51 deletions
|
@ -1 +1 @@
|
|||
:root{--dialogHeaderBg:#292929;--colorDialogHeader:#FFF}.custom-dialog-overlay{display:-webkit-box;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;flex-flow:column nowrap;-webkit-box-align:center;align-items:center;-webkit-box-pack:center;justify-content:center;min-width:0;min-height:0;overflow:hidden;position:fixed;top:0;left:0;width:100vw;height:100vh;z-index:10001;background-color:rgba(0,0,0,0.6);padding:32px}.custom-dialog-overlay .custom-dialog{display:-webkit-box;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;flex-flow:column nowrap;width:100%;max-width:400px;min-height:0;max-height:100%;overflow:auto;background-color:var(--contentBg);font-size:16px}.custom-dialog-overlay .custom-dialog .custom-dialog-header{padding:16px;color:var(--colorDialogHeader);background-color:var(--dialogHeaderBg);font-weight:600;font-size:1em;text-transform:uppercase;border-bottom:1px solid var(--border)}.custom-dialog-overlay .custom-dialog .custom-dialog-body{padding:16px;display:grid;max-height:100%;min-height:80px;min-width:0;overflow:auto;font-size:1em;grid-template-columns:max-content 1fr;-webkit-box-align:baseline;align-items:baseline;grid-gap:16px}.custom-dialog-overlay .custom-dialog .custom-dialog-body::after{content:"";display:block;min-height:1px;grid-column-end:-1;grid-column-start:1}.custom-dialog-overlay .custom-dialog .custom-dialog-body .custom-dialog-content{grid-column-end:-1;grid-column-start:1;color:var(--color);align-self:baseline}.custom-dialog-overlay .custom-dialog .custom-dialog-body>input,.custom-dialog-overlay .custom-dialog .custom-dialog-body>textarea{color:var(--color);padding:8px;font-size:1em;border:1px solid var(--border);background-color:var(--contentBg);min-width:0}.custom-dialog-overlay .custom-dialog .custom-dialog-body>textarea{height:6em;min-height:37px;resize:vertical}.custom-dialog-overlay .custom-dialog .custom-dialog-body>.suggestion{position:absolute;display:block;padding:8px;background-color:var(--contentBg);border:1px solid var(--border);color:var(--color);font-size:.9em}.custom-dialog-overlay .custom-dialog .custom-dialog-footer{padding:16px;display:-webkit-box;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;flex-flow:row wrap;-webkit-box-pack:end;justify-content:flex-end;border-top:1px solid var(--border)}.custom-dialog-overlay .custom-dialog .custom-dialog-footer>a{padding:0 8px;font-size:.9em;font-weight:600;color:var(--color);text-transform:uppercase}.custom-dialog-overlay .custom-dialog .custom-dialog-footer>a:hover,.custom-dialog-overlay .custom-dialog .custom-dialog-footer>a:focus{outline:none;color:var(--main)}.custom-dialog-overlay .custom-dialog .custom-dialog-footer>i.fa-spinner.fa-spin{width:19px;line-height:19px;text-align:center}
|
||||
:root{--dialogHeaderBg:#292929;--colorDialogHeader:#FFF}.custom-dialog-overlay{display:-webkit-box;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;flex-flow:column nowrap;-webkit-box-align:center;align-items:center;-webkit-box-pack:center;justify-content:center;min-width:0;min-height:0;overflow:hidden;position:fixed;top:0;left:0;width:100vw;height:100vh;z-index:10001;background-color:rgba(0,0,0,0.6);padding:32px}.custom-dialog-overlay .custom-dialog{display:-webkit-box;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;flex-flow:column nowrap;width:100%;max-width:400px;min-height:0;max-height:100%;overflow:auto;background-color:var(--contentBg);font-size:16px}.custom-dialog-overlay .custom-dialog .custom-dialog-header{padding:16px;color:var(--colorDialogHeader);background-color:var(--dialogHeaderBg);font-weight:600;font-size:1em;text-transform:uppercase;border-bottom:1px solid var(--border)}.custom-dialog-overlay .custom-dialog .custom-dialog-body{padding:16px;display:grid;max-height:100%;min-height:80px;min-width:0;overflow:auto;font-size:1em;grid-template-columns:max-content 1fr;-webkit-box-align:baseline;align-items:baseline;grid-gap:16px}.custom-dialog-overlay .custom-dialog .custom-dialog-body::after{content:"";display:block;min-height:1px;grid-column-end:-1;grid-column-start:1}.custom-dialog-overlay .custom-dialog .custom-dialog-body .custom-dialog-content{grid-column-end:-1;grid-column-start:1;color:var(--color);align-self:baseline}.custom-dialog-overlay .custom-dialog .custom-dialog-body>input,.custom-dialog-overlay .custom-dialog .custom-dialog-body>textarea{color:var(--color);padding:8px;font-size:1em;border:1px solid var(--border);background-color:var(--contentBg);min-width:0}.custom-dialog-overlay .custom-dialog .custom-dialog-body>textarea{height:6em;min-height:37px;resize:vertical}.custom-dialog-overlay .custom-dialog .custom-dialog-body>.suggestion{position:absolute;display:block;padding:8px;background-color:var(--contentBg);border:1px solid var(--border);color:var(--color);font-size:.9em}.custom-dialog-overlay .custom-dialog .custom-dialog-footer{padding:16px;display:-webkit-box;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;flex-flow:row wrap;-webkit-box-pack:end;justify-content:flex-end;border-top:1px solid var(--border)}.custom-dialog-overlay .custom-dialog .custom-dialog-footer>a{padding:0 8px;font-size:.9em;font-weight:600;color:var(--color);text-transform:uppercase}.custom-dialog-overlay .custom-dialog .custom-dialog-footer>a:hover,.custom-dialog-overlay .custom-dialog .custom-dialog-footer>a:focus{outline:none;color:var(--main)}.custom-dialog-overlay .custom-dialog .custom-dialog-footer>i.fa-spinner.fa-spin{width:19px;line-height:19px;text-align:center;color:var(--color)}
|
File diff suppressed because one or more lines are too long
|
@ -22,8 +22,8 @@
|
|||
</head>
|
||||
|
||||
<body>
|
||||
<div class="home" id="app">
|
||||
<div class="home-sidebar">
|
||||
<div id="main-scene" :class="{night: displayOptions.nightMode}">
|
||||
<div id="main-sidebar">
|
||||
<a v-for="item in sidebarItems" :title="item.title" :class="{active: activePage === item.page}" @click="switchPage(item.page)">
|
||||
<i class="fas fa-fw" :class="item.icon"></i>
|
||||
</a>
|
||||
|
@ -33,21 +33,23 @@
|
|||
</a>
|
||||
</div>
|
||||
<keep-alive>
|
||||
<component :is="activePage"></component>
|
||||
<component :is="activePage" :display-options="displayOptions" @setting-changed="saveSetting"></component>
|
||||
</keep-alive>
|
||||
<custom-dialog v-bind="dialog" />
|
||||
</div>
|
||||
|
||||
<script type="module">
|
||||
import pageHome from "./js/page/home.js";
|
||||
import basePage from "./js/page/base.js";
|
||||
import pageHome from "./js/page/home.js";
|
||||
import pageSetting from "./js/page/setting.js";
|
||||
import customDialog from "./js/component/dialog.js";
|
||||
|
||||
var app = new Vue({
|
||||
el: '#app',
|
||||
el: '#main-scene',
|
||||
mixins: [basePage],
|
||||
components: {
|
||||
pageHome,
|
||||
pageSetting,
|
||||
customDialog
|
||||
},
|
||||
data: {
|
||||
|
@ -96,9 +98,28 @@
|
|||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
saveSetting(opts) {
|
||||
localStorage.setItem("shiori-setting", JSON.stringify(opts));
|
||||
this.displayOptions = opts;
|
||||
},
|
||||
loadSetting() {
|
||||
var opts = JSON.parse(localStorage.getItem("shiori-setting")) || {},
|
||||
showId = (typeof opts.showId === "boolean") ? opts.showId : false,
|
||||
listMode = (typeof opts.listMode === "boolean") ? opts.listMode : false,
|
||||
nightMode = (typeof opts.nightMode === "boolean") ? opts.nightMode : false;
|
||||
|
||||
this.displayOptions = {
|
||||
showId: showId,
|
||||
listMode: listMode,
|
||||
nightMode: nightMode,
|
||||
};
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// Load setting
|
||||
this.loadSetting();
|
||||
|
||||
// Prepare history state watcher
|
||||
var stateWatcher = (e) => {
|
||||
var state = e.state || {};
|
||||
|
|
|
@ -71,7 +71,7 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
tagClicked(name) {
|
||||
this.$emit("tagClicked", name);
|
||||
this.$emit("tag-clicked", name);
|
||||
},
|
||||
selectBookmark() {
|
||||
this.$emit("select", this.eventItem);
|
||||
|
|
|
@ -1,11 +1,19 @@
|
|||
export default {
|
||||
props: {
|
||||
displayOptions: {
|
||||
type: Object,
|
||||
default () {
|
||||
return {
|
||||
showId: false,
|
||||
listMode: false,
|
||||
nightMode: false,
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dialog: {},
|
||||
displayOptions: {
|
||||
showId: false,
|
||||
listMode: false,
|
||||
}
|
||||
dialog: {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
|
|
@ -46,7 +46,7 @@ var template = `
|
|||
:listMode="displayOptions.listMode"
|
||||
:selected="isSelected(book.id)"
|
||||
@select="toggleSelection"
|
||||
@tagClicked="filterTag"
|
||||
@tag-clicked="filterTag"
|
||||
@edit="showDialogEdit"
|
||||
@delete="showDialogDelete"
|
||||
@update="showDialogUpdateArchive">
|
||||
|
|
53
internal/view/js/page/setting.js
Normal file
53
internal/view/js/page/setting.js
Normal file
|
@ -0,0 +1,53 @@
|
|||
var template = `
|
||||
<div id="page-setting">
|
||||
<div class="page-header">
|
||||
<p>Settings</p>
|
||||
<a href="#" title="Refresh setting">
|
||||
<i class="fas fa-fw fa-sync-alt"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="setting-container">
|
||||
<details open class="setting-group" id="setting-display">
|
||||
<summary>Display</summary>
|
||||
<label>
|
||||
<input type="checkbox" v-model="displayOptions.showId" @change="saveSetting">
|
||||
Show bookmark's ID
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" v-model="displayOptions.listMode" @change="saveSetting">
|
||||
Display bookmarks as list
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" v-model="displayOptions.nightMode" @change="saveSetting">
|
||||
Use dark theme
|
||||
</label>
|
||||
</details>
|
||||
</div>
|
||||
<div class="loading-overlay" v-if="loading"><i class="fas fa-fw fa-spin fa-spinner"></i></div>
|
||||
<custom-dialog v-bind="dialog"/>
|
||||
</div>`;
|
||||
|
||||
import customDialog from "../component/dialog.js";
|
||||
import basePage from "./base.js";
|
||||
|
||||
export default {
|
||||
template: template,
|
||||
mixins: [basePage],
|
||||
components: {
|
||||
customDialog
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
saveSetting() {
|
||||
this.$emit("setting-changed", {
|
||||
showId: this.displayOptions.showId,
|
||||
listMode: this.displayOptions.listMode,
|
||||
nightMode: this.displayOptions.nightMode,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -120,6 +120,7 @@
|
|||
width: 19px;
|
||||
line-height: 19px;
|
||||
text-align: center;
|
||||
color: var(--color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ body {
|
|||
overflow: hidden;
|
||||
}
|
||||
|
||||
.login {
|
||||
#login-scene {
|
||||
height: 100vh;
|
||||
padding: 16px;
|
||||
overflow: auto;
|
||||
|
@ -117,7 +117,6 @@ body {
|
|||
|
||||
>label {
|
||||
color: var(--color);
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
>input {
|
||||
|
@ -125,8 +124,8 @@ body {
|
|||
padding: 8px;
|
||||
background-color: var(--contentBg);
|
||||
border: 1px solid var(--border);
|
||||
font-size: 0.9em;
|
||||
min-width: 0;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.checkbox-field {
|
||||
|
@ -135,9 +134,14 @@ body {
|
|||
flex-flow: row nowrap;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 0.9em;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
text-decoration: underline;
|
||||
text-decoration-color: var(--main);
|
||||
}
|
||||
|
||||
>input[type="checkbox"] {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
@ -154,7 +158,6 @@ body {
|
|||
color: var(--color);
|
||||
text-transform: uppercase;
|
||||
text-align: center;
|
||||
font-size: 0.9em;
|
||||
font-weight: 600;
|
||||
cursor: default;
|
||||
|
||||
|
@ -171,7 +174,7 @@ body {
|
|||
}
|
||||
}
|
||||
|
||||
.home {
|
||||
#main-scene {
|
||||
display: grid;
|
||||
grid-template-rows: minmax(0, 1fr);
|
||||
grid-template-columns: 60px minmax(0, 1fr);
|
||||
|
@ -179,7 +182,7 @@ body {
|
|||
width: 100vw;
|
||||
height: 100vh;
|
||||
|
||||
.home-sidebar {
|
||||
#main-sidebar {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
background-color: var(--sidebarBg);
|
||||
|
@ -423,6 +426,10 @@ body {
|
|||
|
||||
.pagination-box {
|
||||
padding: 16px 0;
|
||||
|
||||
&:first-child {
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
|
@ -471,4 +478,124 @@ body {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#page-setting {
|
||||
min-height: 0;
|
||||
max-height: 100%;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
|
||||
.setting-container {
|
||||
padding: 8px;
|
||||
display: flex;
|
||||
overflow: auto;
|
||||
flex-flow: column nowrap;
|
||||
flex: 1 0;
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
display: block;
|
||||
min-height: 1px;
|
||||
}
|
||||
|
||||
details.setting-group {
|
||||
margin: 8px;
|
||||
display: block;
|
||||
max-width: 350px;
|
||||
color: var(--color);
|
||||
background-color: var(--contentBg);
|
||||
border: 1px solid var(--border);
|
||||
|
||||
@media (max-width: 600px) {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
summary {
|
||||
list-style: none;
|
||||
font-weight: 600;
|
||||
width: 100%;
|
||||
padding: 12px 8px;
|
||||
font-size: 1.1em;
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: var(--main)
|
||||
}
|
||||
|
||||
&::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: "+";
|
||||
margin-left: 8px;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
&[open] summary {
|
||||
border-bottom: 1px solid var(--border);
|
||||
|
||||
::after {
|
||||
content: "-";
|
||||
}
|
||||
}
|
||||
|
||||
div.setting-group-footer {
|
||||
padding: 4px 8px;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: flex-end;
|
||||
border-top: 1px solid var(--border);
|
||||
|
||||
>a {
|
||||
text-transform: uppercase;
|
||||
padding: 8px 4px;
|
||||
font-size: 0.9em;
|
||||
font-weight: 600;
|
||||
|
||||
&:hover {
|
||||
color: var(--mainDark);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
color: var(--mainDark);
|
||||
border-bottom: 1px dashed var(--mainDark);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#setting-display {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
padding-bottom: 8px;
|
||||
|
||||
summary {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
label {
|
||||
padding: 4px 8px;
|
||||
color: var(--color);
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
text-decoration: underline;
|
||||
text-decoration-color: var(--main);
|
||||
}
|
||||
|
||||
>input[type="checkbox"] {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,7 +20,7 @@
|
|||
</head>
|
||||
|
||||
<body>
|
||||
<div class="login" id="app">
|
||||
<div id="login-scene" :class="{night: nightMode}">
|
||||
<p class="error-message" v-if="error !== ''">{{error}}</p>
|
||||
<div id="login-box">
|
||||
<div id="logo-area">
|
||||
|
@ -47,13 +47,14 @@
|
|||
|
||||
<script type="module">
|
||||
var app = new Vue({
|
||||
el: "#app",
|
||||
el: "#login-scene",
|
||||
data: {
|
||||
error: "",
|
||||
loading: false,
|
||||
username: "",
|
||||
password: "",
|
||||
remember: false,
|
||||
nightMode: false,
|
||||
},
|
||||
methods: {
|
||||
login() {
|
||||
|
@ -92,9 +93,18 @@
|
|||
this.error = `${msg} (${err.status})`;
|
||||
})
|
||||
});
|
||||
},
|
||||
loadSetting() {
|
||||
var opts = JSON.parse(localStorage.getItem("shiori-setting")) || {},
|
||||
nightMode = (typeof opts.nightMode === "boolean") ? opts.nightMode : false;
|
||||
|
||||
this.nightMode = nightMode;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// Load setting
|
||||
this.loadSetting();
|
||||
|
||||
// Set initial URL
|
||||
history.replaceState(null, "login", "login");
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue