aboutsummaryrefslogtreecommitdiff
path: root/public
diff options
context:
space:
mode:
Diffstat (limited to 'public')
-rw-r--r--public/gallery.js81
-rw-r--r--public/style.css30
2 files changed, 79 insertions, 32 deletions
diff --git a/public/gallery.js b/public/gallery.js
index 96f0216..dbcadf6 100644
--- a/public/gallery.js
+++ b/public/gallery.js
@@ -14,6 +14,56 @@ function call(method, params) {
const loading = (window.location.hostname !== 'localhost') ? 'lazy' : undefined
+let Header = {
+ global: [
+ {name: "Browse", route: '/browse'},
+ {name: "Tags", route: '/tags'},
+ {name: "Duplicates", route: '/duplicates'},
+ {name: "Orphans", route: '/orphans'},
+ ],
+
+ image: [
+ {
+ route: '/view',
+ render: () => m(m.route.Link, {
+ href: `/view/:key`,
+ params: {key: m.route.param('key')},
+ class: m.route.get().startsWith('/view')
+ ? 'active' : undefined,
+ }, "View"),
+ },
+ {
+ route: '/similar',
+ render: () => m(m.route.Link, {
+ href: `/similar/:key`,
+ params: {key: m.route.param('key')},
+ class: m.route.get().startsWith('/similar')
+ ? 'active' : undefined,
+ }, "Similar"),
+ },
+ ],
+
+ view(vnode) {
+ const route = m.route.get()
+ const main = this.global.map(x =>
+ m(m.route.Link, {
+ href: x.route,
+ class: route.startsWith(x.route) ? 'active' : undefined,
+ }, x.name))
+
+ let context
+ if (this.image.some(x => route.startsWith(x.route)))
+ context = this.image.map(x => x.render())
+
+ return m('.header', {}, [
+ m('nav', main),
+ m('nav', context),
+ // TODO: End it with an activity indicator.
+ m('.activity', '☺'),
+ ])
+ },
+}
+
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
let BrowseModel = {
@@ -113,7 +163,7 @@ let Browse = {
view(vnode) {
return m('.container', {}, [
- m('.header', {}, "Browser"),
+ m(Header),
m('.body', {}, [
m('ul.sidebar', BrowseModel.getBrowseLinks().map(link =>
m(BrowseBarLink, {link}))),
@@ -209,13 +259,7 @@ let View = {
: "No image.",
])
return m('.container', {}, [
- m('.header', {}, [
- "View",
- m(m.route.Link, {
- href: `/similar/:key`,
- params: {key: ViewModel.sha1},
- }, "Similar"),
- ]),
+ m(Header),
m('.body', {}, [view, m(ViewBar)]),
])
},
@@ -298,13 +342,7 @@ let Similar = {
view(vnode) {
return m('.container', {}, [
- m('.header', {}, [
- "Similarity search",
- m(m.route.Link, {
- href: `/view/:key`,
- params: {key: SimilarModel.sha1},
- }, "View"),
- ]),
+ m(Header),
m('.body', {}, m(SimilarList)),
])
},
@@ -353,9 +391,7 @@ let Duplicates = {
view(vnode) {
return m('.container', {}, [
- m('.header', {}, [
- "Duplicates",
- ]),
+ m(Header),
m('.body', {}, m(DuplicatesList)),
])
},
@@ -421,9 +457,7 @@ let Orphans = {
view(vnode) {
return m('.container', {}, [
- m('.header', {}, [
- "Orphans",
- ]),
+ m(Header),
m('.body', {}, m(OrphansList)),
])
},
@@ -436,11 +470,12 @@ window.addEventListener('load', () => {
// The path doesn't need to be escaped, perhaps change that (":key...").
"/browse/": Browse,
"/browse/:key": Browse,
- "/view/:key": View,
- "/similar/:key": Similar,
"/duplicates": Duplicates,
"/orphans": Orphans,
+ "/view/:key": View,
+ "/similar/:key": Similar,
+
"/tags": undefined,
"/tags/:space": undefined,
"/tags/:space/:tag": undefined,
diff --git a/public/style.css b/public/style.css
index 0276e43..0df44d9 100644
--- a/public/style.css
+++ b/public/style.css
@@ -1,22 +1,33 @@
body { margin: 0; padding: 0; font-family: sans-serif; }
-
a { color: inherit; }
.container { display: flex; flex-direction: column;
height: 100vh; width: 100vw; overflow: hidden; }
-.body { display: flex; flex-grow: 1; overflow: hidden; }
-.header { padding: .25rem .5rem; background: #aaa; color: white;
- border-bottom: 1px solid #444;
- display: flex; justify-content: space-between; align-items: baseline;
- column-gap: .5rem; }
+.body { display: flex; flex-grow: 1; overflow: hidden; position: relative; }
+.body::after { content: ''; position: absolute; pointer-events: none;
+ top: 0; left: 0; right: 0; height: .75rem;
+ background: linear-gradient(#fff, rgb(255 255 255 / 0%)); }
+
+.header { color: #000; background: #aaa linear-gradient(#888, #999);
+ display: flex; justify-content: space-between; column-gap: .5rem; }
+.header nav { display: flex; margin: 0 .5rem; align-items: end; }
+.header nav a { display: block; text-decoration: none;
+ background: #bbb linear-gradient(#bbb, #ccc);
+ margin: .25rem 0 0 -1px; padding: .25rem .75rem;
+ border: 1px solid #888; border-radius: .5rem .5rem 0 0; }
+.header nav a.active { font-weight: bold; border-bottom: 1px solid #fff;
+ background: #fff linear-gradient(#ddd, #fff); }
+.header nav a.active, .header nav a:hover { padding-bottom: .4rem; }
+.header .activity { padding: .25rem .5rem; align-self: center; color: #fff; }
+
+ul.sidebar { margin: 0; padding: .5rem 0; background: #eee;
+ min-width: 10rem; overflow: auto; border-right: 1px solid #ccc; }
-ul.sidebar { margin: 0; padding: 0; background: #eee;
- border-right: 1px solid #ccc; min-width: 10rem; overflow: auto; }
ul.sidebar li { margin: 0; padding: 0; }
ul.sidebar li a { padding: .25rem .5rem; padding-left: 30px;
display: block; text-decoration: none; white-space: nowrap; }
-ul.sidebar li a:hover { background: #ddd; }
+ul.sidebar li a:hover { background-color: rgb(0 0 0 / 10%); }
ul.sidebar li.parent a {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20'%3E%3Cpath d='M 4 14 10 8 16 14' stroke='%23888' stroke-width='4' fill='none' /%3E%3C/svg%3E%0A");
@@ -43,6 +54,7 @@ img.thumbnail { display: block; box-shadow: 0 0 3px rgba(0, 0, 0, 0.75);
.view { display: flex; flex-grow: 1; overflow: hidden;
justify-content: center; align-items: center; }
.view img { max-width: 100%; max-height: 100%; object-fit: contain; }
+.view img { z-index: 1; }
.viewbar { padding: .25rem .5rem; background: #eee;
border-left: 1px solid #ccc; min-width: 20rem; overflow: auto; }