This commit is contained in:
2026-01-03 22:15:15 +01:00
parent fba501f697
commit 1bcb9e38b4
13 changed files with 1554 additions and 52 deletions
+121
View File
@@ -1,4 +1,5 @@
import { api } from '../api.js';
import { GestureHandler, PullToRefreshIndicator, ContextMenu, isMobile } from '../gesture-handler.js';
export class FileList extends HTMLElement {
constructor() {
@@ -12,6 +13,9 @@ export class FileList extends HTMLElement {
this.boundHandleClick = this.handleClick.bind(this);
this.boundHandleDblClick = this.handleDblClick.bind(this);
this.boundHandleChange = this.handleChange.bind(this);
this.gestureHandler = null;
this.pullIndicator = null;
this.contextMenu = new ContextMenu();
}
async connectedCallback() {
@@ -27,6 +31,12 @@ export class FileList extends HTMLElement {
this.removeEventListener('click', this.boundHandleClick);
this.removeEventListener('dblclick', this.boundHandleDblClick);
this.removeEventListener('change', this.boundHandleChange);
if (this.gestureHandler) {
this.gestureHandler.destroy();
}
if (this.pullIndicator) {
this.pullIndicator.destroy();
}
}
async loadContents(folderId) {
@@ -305,6 +315,117 @@ export class FileList extends HTMLElement {
attachListeners() {
this.updateBatchActionVisibility();
this.initGestures();
}
initGestures() {
if (this.gestureHandler) {
this.gestureHandler.destroy();
}
if (this.pullIndicator) {
this.pullIndicator.destroy();
}
const container = this.querySelector('.file-list-container');
if (!container) return;
this.pullIndicator = new PullToRefreshIndicator(container);
this.gestureHandler = new GestureHandler(container);
this.gestureHandler.on('pullToRefresh', async () => {
this.pullIndicator.showRefreshing();
await this.loadContents(this.currentFolderId);
this.pullIndicator.hide();
});
this.gestureHandler.on('longPress', (data) => {
if (!isMobile()) return;
const fileItem = data.target?.closest('.file-item');
if (!fileItem) return;
const folderId = fileItem.dataset.folderId;
const fileId = fileItem.dataset.fileId;
if (folderId) {
const folder = this.folders.find(f => f.id === parseInt(folderId));
if (folder) {
this.showFolderContextMenu(data.x, data.y, folder);
}
} else if (fileId) {
const file = this.files.find(f => f.id === parseInt(fileId));
if (file) {
this.showFileContextMenu(data.x, data.y, file);
}
}
});
container.addEventListener('pull-progress', (e) => {
this.pullIndicator.setProgress(e.detail.progress);
});
container.addEventListener('pull-end', () => {
if (this.pullIndicator) {
this.pullIndicator.hide();
}
});
}
showFileContextMenu(x, y, file) {
const items = [
{
label: 'Download',
icon: '⬇',
action: () => this.handleAction('download', file.id)
},
{
label: 'Rename',
icon: '✏',
action: () => this.handleAction('rename', file.id)
},
{
label: 'Share',
icon: '🔗',
action: () => this.handleAction('share', file.id)
},
{ separator: true },
{
label: file.is_starred ? 'Unstar' : 'Star',
icon: file.is_starred ? '★' : '☆',
action: () => this.handleAction(file.is_starred ? 'unstar-file' : 'star-file', file.id)
},
{ separator: true },
{
label: 'Delete',
icon: '🗑',
destructive: true,
action: () => this.handleAction('delete', file.id)
}
];
this.contextMenu.show(x, y, items);
}
showFolderContextMenu(x, y, folder) {
const items = [
{
label: 'Open',
icon: '📂',
action: () => this.loadContents(folder.id)
},
{
label: folder.is_starred ? 'Unstar' : 'Star',
icon: folder.is_starred ? '★' : '☆',
action: () => this.handleAction(folder.is_starred ? 'unstar-folder' : 'star-folder', folder.id)
},
{ separator: true },
{
label: 'Delete',
icon: '🗑',
destructive: true,
action: () => this.handleAction('delete-folder', folder.id)
}
];
this.contextMenu.show(x, y, items);
}
toggleSelectItem(type, id, checked) {
+21
View File
@@ -1,15 +1,36 @@
import { api } from '../api.js';
import { GestureHandler, isMobile } from '../gesture-handler.js';
class FilePreview extends HTMLElement {
constructor() {
super();
this.file = null;
this.handleEscape = this.handleEscape.bind(this);
this.gestureHandler = null;
}
connectedCallback() {
this.render();
this.setupEventListeners();
this.initGestures();
}
disconnectedCallback() {
if (this.gestureHandler) {
this.gestureHandler.destroy();
}
}
initGestures() {
const overlay = this.querySelector('.file-preview-overlay');
if (!overlay) return;
this.gestureHandler = new GestureHandler(overlay);
this.gestureHandler.on('swipeDown', () => {
if (isMobile()) {
this.close();
}
});
}
setupEventListeners() {
+19
View File
@@ -1,4 +1,5 @@
import { api } from '../api.js';
import { GestureHandler, isMobile } from '../gesture-handler.js';
export class FileUploadView extends HTMLElement {
constructor() {
@@ -6,6 +7,7 @@ export class FileUploadView extends HTMLElement {
this.folderId = null;
this.handleEscape = this.handleEscape.bind(this);
this.uploadItems = new Map();
this.gestureHandler = null;
}
connectedCallback() {
@@ -14,6 +16,21 @@ export class FileUploadView extends HTMLElement {
disconnectedCallback() {
document.removeEventListener('keydown', this.handleEscape);
if (this.gestureHandler) {
this.gestureHandler.destroy();
}
}
initGestures() {
const view = this.querySelector('.file-upload-view');
if (!view) return;
this.gestureHandler = new GestureHandler(view);
this.gestureHandler.on('swipeDown', () => {
if (isMobile()) {
this.close();
}
});
}
setFolder(folderId) {
@@ -57,6 +74,8 @@ export class FileUploadView extends HTMLElement {
backBtn.addEventListener('click', () => this.close());
}
this.initGestures();
if (fileInput) {
fileInput.addEventListener('change', (e) => {
if (e.target.files.length > 0) {
+73 -4
View File
@@ -15,8 +15,9 @@ import './billing-dashboard.js';
import './admin-billing.js';
import './code-editor-view.js';
import './cookie-consent.js';
import './user-settings.js'; // Import the new user settings component
import './user-settings.js';
import { shortcuts } from '../shortcuts.js';
import { GestureHandler, isMobile } from '../gesture-handler.js';
const api = app.getAPI();
const logger = app.getLogger();
@@ -31,6 +32,8 @@ export class MyWebdavApp extends HTMLElement {
this.boundHandlePopState = this.handlePopState.bind(this);
this.popstateAttached = false;
this.currentSearchId = 0;
this.gestureHandler = null;
this.sidebarOpen = false;
}
async connectedCallback() {
@@ -127,10 +130,15 @@ export class MyWebdavApp extends HTMLElement {
<div class="app-container">
<header class="app-header">
<div class="header-left">
<button class="hamburger-btn" id="hamburger-btn" aria-label="Toggle navigation">
<span></span>
<span></span>
<span></span>
</button>
<h1 class="app-title">MyWebdav</h1>
</div>
<div class="header-center">
<input type="search" placeholder="Search..." class="search-input" id="search-input">
<input type="search" placeholder="Search..." class="search-input" id="search-input" inputmode="search">
</div>
<div class="header-right">
<span class="user-info">${this.user.username}</span>
@@ -139,7 +147,8 @@ export class MyWebdavApp extends HTMLElement {
</header>
<div class="app-body">
<aside class="app-sidebar">
<div class="sidebar-overlay" id="sidebar-overlay"></div>
<aside class="app-sidebar" id="app-sidebar">
<nav class="sidebar-nav">
<h3 class="nav-title">Navigation</h3>
<ul class="nav-list">
@@ -160,7 +169,7 @@ export class MyWebdavApp extends HTMLElement {
</nav>
</aside>
<main class="app-main">
<main class="app-main" id="app-main">
<div id="main-content">
<file-list></file-list>
</div>
@@ -191,6 +200,55 @@ export class MyWebdavApp extends HTMLElement {
this.initializeNavigation();
this.attachListeners();
this.registerShortcuts();
this.initGestures();
}
initGestures() {
const appMain = this.querySelector('#app-main');
if (!appMain) return;
this.gestureHandler = new GestureHandler(appMain);
this.gestureHandler.on('edgeSwipeRight', () => {
if (isMobile()) {
this.openSidebar();
}
});
}
toggleSidebar() {
if (this.sidebarOpen) {
this.closeSidebar();
} else {
this.openSidebar();
}
}
openSidebar() {
const sidebar = this.querySelector('#app-sidebar');
const overlay = this.querySelector('#sidebar-overlay');
const hamburger = this.querySelector('#hamburger-btn');
if (sidebar && overlay) {
sidebar.classList.add('open');
overlay.classList.add('visible');
hamburger?.classList.add('active');
this.sidebarOpen = true;
document.body.style.overflow = 'hidden';
}
}
closeSidebar() {
const sidebar = this.querySelector('#app-sidebar');
const overlay = this.querySelector('#sidebar-overlay');
const hamburger = this.querySelector('#hamburger-btn');
if (sidebar && overlay) {
sidebar.classList.remove('open');
overlay.classList.remove('visible');
hamburger?.classList.remove('active');
this.sidebarOpen = false;
document.body.style.overflow = '';
}
}
initializeNavigation() {
@@ -402,11 +460,22 @@ export class MyWebdavApp extends HTMLElement {
api.logout();
});
this.querySelector('#hamburger-btn')?.addEventListener('click', () => {
this.toggleSidebar();
});
this.querySelector('#sidebar-overlay')?.addEventListener('click', () => {
this.closeSidebar();
});
this.querySelectorAll('.nav-link').forEach(link => {
link.addEventListener('click', (e) => {
e.preventDefault();
const view = link.dataset.view;
this.switchView(view);
if (isMobile()) {
this.closeSidebar();
}
});
});
+34
View File
@@ -1,20 +1,54 @@
import { api } from '../api.js';
import { GestureHandler, PullToRefreshIndicator } from '../gesture-handler.js';
class PhotoGallery extends HTMLElement {
constructor() {
super();
this.photos = [];
this.boundHandleClick = this.handleClick.bind(this);
this.gestureHandler = null;
this.pullIndicator = null;
}
connectedCallback() {
this.addEventListener('click', this.boundHandleClick);
this.render();
this.loadPhotos();
this.initGestures();
}
disconnectedCallback() {
this.removeEventListener('click', this.boundHandleClick);
if (this.gestureHandler) {
this.gestureHandler.destroy();
}
if (this.pullIndicator) {
this.pullIndicator.destroy();
}
}
initGestures() {
const container = this.querySelector('.photo-gallery');
if (!container) return;
this.pullIndicator = new PullToRefreshIndicator(container);
this.gestureHandler = new GestureHandler(container);
this.gestureHandler.on('pullToRefresh', async () => {
this.pullIndicator.showRefreshing();
await this.loadPhotos();
this.pullIndicator.hide();
});
container.addEventListener('pull-progress', (e) => {
this.pullIndicator.setProgress(e.detail.progress);
});
container.addEventListener('pull-end', () => {
if (this.pullIndicator) {
this.pullIndicator.hide();
}
});
}
async loadPhotos() {
+21
View File
@@ -1,4 +1,5 @@
import { api } from '../api.js';
import { GestureHandler, isMobile } from '../gesture-handler.js';
export class ShareModal extends HTMLElement {
constructor() {
@@ -6,10 +7,29 @@ export class ShareModal extends HTMLElement {
this.fileId = null;
this.folderId = null;
this.handleEscape = this.handleEscape.bind(this);
this.gestureHandler = null;
this.render();
this.attachListeners();
}
disconnectedCallback() {
if (this.gestureHandler) {
this.gestureHandler.destroy();
}
}
initGestures() {
const modal = this.querySelector('.share-modal-content');
if (!modal || this.gestureHandler) return;
this.gestureHandler = new GestureHandler(modal);
this.gestureHandler.on('swipeDown', () => {
if (isMobile()) {
this.hide();
}
});
}
render() {
this.innerHTML = `
<div class="share-modal" id="share-modal" style="display: none;">
@@ -90,6 +110,7 @@ export class ShareModal extends HTMLElement {
this.querySelector('#share-result').style.display = 'none';
this.querySelector('#share-form').reset();
document.addEventListener('keydown', this.handleEscape);
this.initGestures();
}
hide() {