- change: document element scroll behavior smooth.
- new: hotkey supported:
  - browser navigation back and forward.
  - post grid navigation.
  - post favorites.
This commit is contained in:
defaultkavy 2024-10-17 11:43:21 +08:00
parent 84f52ed030
commit d6be4c0c62
Signed by: defaultkavy
GPG Key ID: DFBB22C4E69D7826
13 changed files with 74 additions and 11 deletions

1
dist/assets/index-BMZsSbMp.js vendored Normal file

File diff suppressed because one or more lines are too long

1
dist/assets/index-ByWqsQtl.css vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4
dist/index.html vendored
View File

@ -16,8 +16,8 @@
gtag('config', 'G-59HBGP98WR'); gtag('config', 'G-59HBGP98WR');
</script> </script>
<script type="module" crossorigin src="/assets/index-D8tVsP8g.js"></script> <script type="module" crossorigin src="/assets/index-BMZsSbMp.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-D689878Y.css"> <link rel="stylesheet" crossorigin href="/assets/index-ByWqsQtl.css">
</head> </head>
<body> <body>
</body> </body>

View File

@ -33,7 +33,7 @@
html { html {
overflow-x: hidden; overflow-x: hidden;
font-size: 14px; font-size: 14px;
scroll-behavior: smooth;
::-webkit-scrollbar { ::-webkit-scrollbar {
background-color: var(--secondary-color-1); background-color: var(--secondary-color-1);
width: 8px; width: 8px;

View File

@ -2,7 +2,7 @@
"name": "danbooru-viewer", "name": "danbooru-viewer",
"module": "index.ts", "module": "index.ts",
"type": "module", "type": "module",
"version": "0.8.2", "version": "0.9.0",
"scripts": { "scripts": {
"dev": "bun x vite", "dev": "bun x vite",
"build": "bun x vite build", "build": "bun x vite build",

View File

@ -45,10 +45,11 @@ export class $Drawer extends $Container {
$('icon-button').icon('log-in-outline').content('Logout').on('dblclick', () => Booru.used.logout()).hide(true) $('icon-button').icon('log-in-outline').content('Logout').on('dblclick', () => Booru.used.logout()).hide(true)
.self(($div => Booru.events.on('login', () => $div.hide(false)).on('logout', () => $div.hide(true)))), .self(($div => Booru.events.on('login', () => $div.hide(false)).on('logout', () => $div.hide(true)))),
$('icon-button').icon('swap-horizontal').content('Logout').class('switch').content('Switch Booru') $('icon-button').icon('swap-horizontal').class('switch').content('Switch Booru')
.on('click', () => { .on('click', () => {
if (Booru.used === danbooru) Booru.set(safebooru); if (Booru.used === danbooru) Booru.set(safebooru);
else Booru.set(danbooru); else Booru.set(danbooru);
this.close();
}), }),
]) ])
]), ]),

View File

@ -2,6 +2,7 @@ import { $Layout, type $LayoutEventMap } from "@elexis/layout";
import { Booru } from "../../structure/Booru"; import { Booru } from "../../structure/Booru";
import { Post } from "../../structure/Post"; import { Post } from "../../structure/Post";
import { $PostTile } from "../PostTile/$PostTile"; import { $PostTile } from "../PostTile/$PostTile";
import { $Input } from "elexis/lib/node/$Input";
interface $PostGridOptions { interface $PostGridOptions {
tags?: string tags?: string
@ -13,6 +14,7 @@ export class $PostGrid extends $Layout<$PostGridEventMap> {
tags?: string; tags?: string;
finished = false; finished = false;
limit = 100; limit = 100;
$focus = $.focus();
constructor(options?: $PostGridOptions) { constructor(options?: $PostGridOptions) {
super(); super();
this.tags = options?.tags; this.tags = options?.tags;
@ -34,6 +36,29 @@ export class $PostGrid extends $Layout<$PostGridEventMap> {
this.on('resize', () => this.resize()) this.on('resize', () => this.resize())
this.events.fire('startLoad'); this.events.fire('startLoad');
this.loader(); this.loader();
this.$focus.layer(100).loop(false).scrollThreshold($.rem(2) + 60);
$.keys($(window))
.if(e => {
if ($(e.target) instanceof $Input) return;
if (!this.inDOM()) return;
return true;
})
.keydown('Tab', e => {
e.preventDefault();
if (e.shiftKey) this.$focus.prev();
else this.$focus.next();
})
.keydown(['w', 'W'], e => { e.preventDefault(); this.$focus.up(); })
.keydown(['s', 'S'], e => { e.preventDefault(); this.$focus.down(); })
.keydown(['d', 'D'], e => { e.preventDefault(); this.$focus.right(); })
.keydown(['a', 'A'], e => { e.preventDefault(); this.$focus.left(); })
.keydown([' ', 'Enter'], e => {
e.preventDefault();
const focused = this.$focus.currentLayer?.currentFocus;
if (focused instanceof $PostTile) $.open(`/posts/${focused.post.id}`);
})
.keydown(['Escape'], e => { e.preventDefault(); this.$focus.blur(); })
} }
protected async loader() { protected async loader() {
@ -63,7 +88,8 @@ export class $PostGrid extends $Layout<$PostGridEventMap> {
this.$posts.set(post, $post); this.$posts.set(post, $post);
this.posts.add(post); this.posts.add(post);
} }
const $posts = [...this.orderMap.values()].map(post => this.$posts.get(post)); this.$focus.layer(100).removeAll();
const $posts = [...this.orderMap.values()].map(post => this.$posts.get(post)?.self(this.$focus.layer(100).add));
this.content($posts).render(); this.content($posts).render();
return this; return this;
} }

View File

@ -1,3 +1,12 @@
layout.post-grid { layout.post-grid {
margin-top: 0.4rem; margin-top: 0.4rem;
&:has(post-tile[focus]) {
post-tile:not([focus]) {
opacity: 0.5;
}
post-tile:hover {
opacity: 1;
}
}
} }

View File

@ -7,7 +7,12 @@ post-tile {
overflow: hidden; overflow: hidden;
-webkit-tap-highlight-color: transparent; -webkit-tap-highlight-color: transparent;
user-select: none; user-select: none;
outline: transparent solid 2px;
&[focus] {
outline: var(--secondary-color-9) solid 2px;
transform: scale(1.02);
}
@media (hover: hover) { @media (hover: hover) {
&:hover { &:hover {
transform: scale(1.02); transform: scale(1.02);

View File

@ -1,7 +1,7 @@
import 'elexis'; import 'elexis';
import '@elexis/layout'; import '@elexis/layout';
import '@elexis/router'; import '@elexis/router';
import { Booru, type BooruOptions } from './structure/Booru'; import { Booru } from './structure/Booru';
import { post_route } from './route/post/$post_route'; import { post_route } from './route/post/$post_route';
import { $PostGrid } from './component/PostGrid/$PostGrid'; import { $PostGrid } from './component/PostGrid/$PostGrid';
import { $Router, $RouterNavigationDirection } from '@elexis/router'; import { $Router, $RouterNavigationDirection } from '@elexis/router';
@ -10,6 +10,7 @@ import { $IonIcon } from './component/IonIcon/$IonIcon';
import { $IconButton } from './component/IconButton/$IconButton'; import { $IconButton } from './component/IconButton/$IconButton';
import { $login_route } from './route/login/$login_route'; import { $login_route } from './route/login/$login_route';
import { $Drawer } from './component/Drawer/$Drawer'; import { $Drawer } from './component/Drawer/$Drawer';
import { $Input } from 'elexis/lib/node/$Input';
// declare elexis module // declare elexis module
declare module 'elexis' { declare module 'elexis' {
export namespace $ { export namespace $ {
@ -117,6 +118,7 @@ $(document.body).content([
const TX = 2; const TX = 2;
e.preventDefault(); e.preventDefault();
function intro() { function intro() {
$(document.documentElement).css({scrollBehavior: 'auto'});
const transform = $.call(() => { const transform = $.call(() => {
switch ($Router.navigationDirection) { switch ($Router.navigationDirection) {
case $RouterNavigationDirection.Forward: return [`translateX(${TX}%)`, `translateX(0%)`]; case $RouterNavigationDirection.Forward: return [`translateX(${TX}%)`, `translateX(0%)`];
@ -134,10 +136,12 @@ $(document.body).content([
easing: 'ease' easing: 'ease'
}, () => { }, () => {
e.switched(); e.switched();
$(document.documentElement).css({scrollBehavior: ''});
e.nextContent.element?.removeClass('animated') e.nextContent.element?.removeClass('animated')
}) })
} }
function outro() { function outro() {
$(document.documentElement).css({scrollBehavior: 'auto'});
const transform = $.call(() => { const transform = $.call(() => {
switch ($Router.navigationDirection) { switch ($Router.navigationDirection) {
case $RouterNavigationDirection.Forward: return [`translateX(0%)`, `translateX(-${TX}%)`]; case $RouterNavigationDirection.Forward: return [`translateX(0%)`, `translateX(-${TX}%)`];
@ -169,3 +173,10 @@ componentState(undefined, new URL(location.href))
function componentState(beforeURL: URL | undefined, afterURL: URL) { function componentState(beforeURL: URL | undefined, afterURL: URL) {
$searchbar.checkURL(beforeURL, afterURL); $drawer.checkURL(beforeURL, afterURL) $searchbar.checkURL(beforeURL, afterURL); $drawer.checkURL(beforeURL, afterURL)
} }
$.keys($(window))
.if(e => {
if ($(e.target) instanceof $Input) return;
return true;
})
.keydown(['q', 'Q'], e => { e.preventDefault(); if ($Router.index !== 0) $.back(); })
.keydown(['e', 'E'], e => { e.preventDefault(); if ($Router.forwardIndex !== 0) $.forward(); })

View File

@ -7,6 +7,7 @@ import type { $IonIcon } from "../../component/IonIcon/$IonIcon";
import { numberFormat } from "../../structure/Util"; import { numberFormat } from "../../structure/Util";
import { ClientUser } from "../../structure/ClientUser"; import { ClientUser } from "../../structure/ClientUser";
import { $VideoController } from "../../component/VideoController/$VideoController"; import { $VideoController } from "../../component/VideoController/$VideoController";
import { $Input } from "elexis/lib/node/$Input";
export const post_route = $('route').path('/posts/:id').id('post').builder(({$route, params}) => { export const post_route = $('route').path('/posts/:id').id('post').builder(({$route, params}) => {
if (!Number(params.id)) return $('h1').content('404: POST NOT FOUND'); if (!Number(params.id)) return $('h1').content('404: POST NOT FOUND');
@ -18,6 +19,16 @@ export const post_route = $('route').path('/posts/:id').id('post').builder(({$ro
original_size: [], original_size: [],
video_play_pause: [] video_play_pause: []
}>(); }>();
$.keys($(window))
.if(e => {
if ($(e.target) instanceof $Input) return;
if (!$route.inDOM()) return;
return true;
})
.keydown(['f', 'F'], e => {
if (Booru.used.user?.favorites.has(post.id)) post.deleteFavorite();
else post.createFavorite();
})
return [ return [
$('div').class('viewer').content(async ($viewer) => { $('div').class('viewer').content(async ($viewer) => {
const $video = $('video'); const $video = $('video');