From 772cb7837a7f4aad2c191640510ae1585e155be5 Mon Sep 17 00:00:00 2001 From: undefined Date: Fri, 17 Apr 2026 05:01:39 +0800 Subject: [PATCH] ui: use inline slideout impl --- .../components/navigation/navigation.page.js | 84 +++++++++++++++---- packages/ui-default/misc/slideout.styl | 1 + packages/ui-default/package.json | 27 +++--- 3 files changed, 82 insertions(+), 30 deletions(-) diff --git a/packages/ui-default/components/navigation/navigation.page.js b/packages/ui-default/components/navigation/navigation.page.js index 8833003ce..3e4f57d71 100644 --- a/packages/ui-default/components/navigation/navigation.page.js +++ b/packages/ui-default/components/navigation/navigation.page.js @@ -1,5 +1,4 @@ import $ from 'jquery'; -import Slideout from 'slideout'; import Notification from 'vj/components/notification'; import selectUser from 'vj/components/selectUser'; import { AutoloadPage } from 'vj/misc/Page'; @@ -88,23 +87,76 @@ const navigationPage = new AutoloadPage('navigationPage', () => { $(document).on('click', '[name="nav_logout"]', handleNavLogoutClick); $(document).on('click', '[name="nav_switch_account"]', handlerSwitchAccount); - const slideout = new Slideout({ - panel: document.getElementById('panel'), - menu: document.getElementById('menu'), - padding: 200, - tolerance: 70, - side: 'right', - }); - [['beforeopen', 'add'], ['beforeclose', 'remove']].forEach(([event, action]) => { - slideout.on(event, () => $('.header__hamburger .hamburger')[`${action}Class`]('is-active')); - }); - + let isOpen = false; + const panel = document.getElementById('panel'); const $slideoutOverlay = $('.slideout-overlay'); - $slideoutOverlay.on('click', () => slideout.close()); - slideout.on('beforeopen', () => $slideoutOverlay.show()); - slideout.on('beforeclose', () => $slideoutOverlay.hide()); + const $hamburger = $('.header__hamburger .hamburger'); + const PADDING = 200; + const TOLERANCE = 70; - $('.header__hamburger').on('click', () => slideout.toggle()); + function setTranslateX(x) { + panel.style.transform = x ? `translateX(${x}px)` : ''; + } + + function open() { + isOpen = true; + $('html').addClass('slideout-open'); + setTranslateX(-PADDING); + $hamburger.addClass('is-active'); + $slideoutOverlay.show(); + } + + function close() { + isOpen = false; + setTranslateX(0); + $hamburger.removeClass('is-active'); + $slideoutOverlay.hide(); + $('html').removeClass('slideout-open'); + } + + // Touch swipe support + let touchStartX = 0; + let touchCurrentX = 0; + let isSwiping = false; + + panel.addEventListener('touchstart', (e) => { + if (e.target.closest('[data-slideout-ignore]')) return; + touchStartX = e.touches[0].clientX; + touchCurrentX = touchStartX; + isSwiping = false; + }, { passive: true }); + + panel.addEventListener('touchmove', (e) => { + touchCurrentX = e.touches[0].clientX; + const dx = touchStartX - touchCurrentX; + if (!isSwiping) { + if (Math.abs(dx) < 10) return; + isSwiping = true; + if (!isOpen) $slideoutOverlay.show(); + $('html').addClass('slideout-open'); + panel.style.transition = 'none'; + } + setTranslateX(isOpen + ? Math.max(-PADDING, Math.min(0, -PADDING - dx)) + : Math.max(-PADDING, Math.min(0, -dx))); + }, { passive: true }); + + panel.addEventListener('touchend', () => { + if (!isSwiping) return; + panel.style.transition = ''; + const dx = touchStartX - touchCurrentX; + if (isOpen) { + if (-dx > TOLERANCE) close(); + else open(); + } else { + if (dx > TOLERANCE) open(); + else close(); + } + isSwiping = false; + }); + + $slideoutOverlay.on('click', close); + $('.header__hamburger').on('click', () => (isOpen ? close() : open())); $(window).on('resize', handleNavbar); setInterval(handleNavbar, 3000); }, () => { diff --git a/packages/ui-default/misc/slideout.styl b/packages/ui-default/misc/slideout.styl index 6d0168727..1a26453b5 100644 --- a/packages/ui-default/misc/slideout.styl +++ b/packages/ui-default/misc/slideout.styl @@ -11,6 +11,7 @@ body, #panel .slideout-panel position: relative z-index: 1 + transition: transform .3s ease .slideout-overlay display: none diff --git a/packages/ui-default/package.json b/packages/ui-default/package.json index 2cc32996a..33df5e87b 100644 --- a/packages/ui-default/package.json +++ b/packages/ui-default/package.json @@ -20,11 +20,11 @@ "@fontsource/source-code-pro": "^5.2.7", "@fontsource/ubuntu-mono": "^5.2.8", "@hydrooj/utils": "workspace:^", - "@mantine/core": "^9.0.0", - "@mantine/hooks": "^9.0.0", - "@mantine/notifications": "^9.0.0", - "@sentry/browser": "^10.47.0", - "@sentry/webpack-plugin": "^5.1.1", + "@mantine/core": "^9.0.2", + "@mantine/hooks": "^9.0.2", + "@mantine/notifications": "^9.0.2", + "@sentry/browser": "^10.49.0", + "@sentry/webpack-plugin": "^5.2.0", "@simplewebauthn/browser": "13.2.2", "@svgr/webpack": "^8.1.0", "@types/html-to-text": "^9.0.4", @@ -45,14 +45,14 @@ "@vscode/codicons": "^0.0.45", "ajv": "^8.18.0", "allotment": "^1.20.5", - "autoprefixer": "^10.4.27", + "autoprefixer": "^10.5.0", "browser-update": "^3.3.63", "chalk": "^5.6.2", "classnames": "^2.5.1", "clean-webpack-plugin": "^4.0.0", "clipboard": "^2.0.11", "copy-webpack-plugin": "^14.0.0", - "cordis": "4.0.0-rc.0", + "cordis": "4.0.0-rc.2", "core-js": "^3.49.0", "core-js-compat": "^3.49.0", "css-loader": "^7.1.4", @@ -69,7 +69,7 @@ "jquery.transit": "^0.9.12", "mantine-contextmenu": "^8.3.13", "matchmedia-polyfill": "^0.3.2", - "md-editor-rt": "^6.4.1", + "md-editor-rt": "^6.4.2", "mini-css-extract-plugin": "^2.10.2", "moment": "^2.30.1", "monaco-editor": "0.52.2", @@ -77,10 +77,10 @@ "monaco-editor-webpack-plugin": "^7.1.1", "monaco-themes": "0.4.7", "monaco-yaml": "5.4.1", - "nanoid": "^5.1.7", + "nanoid": "^5.1.9", "normalize.css": "^8.0.1", "nprogress": "^0.2.0", - "p-queue": "^9.1.1", + "p-queue": "^9.1.2", "pickadate": "patch:pickadate@npm%3A3.6.4#~/.yarn/patches/pickadate-npm-3.6.4-5795acb19d.patch", "postcss-loader": "7.3.4", "postcss-preset-mantine": "^1.18.0", @@ -89,11 +89,11 @@ "prop-types": "^15.8.1", "qface": "^1.4.1", "qrcode": "^1.5.4", - "react": "^19.2.4", + "react": "^19.2.5", "react-colorful": "^5.6.1", "react-dnd": "^16.0.1", "react-dnd-html5-backend": "^16.0.1", - "react-dom": "^19.2.4", + "react-dom": "^19.2.5", "react-redux": "^9.2.0", "reconnecting-websocket": "^4.4.0", "redux": "^5.0.1", @@ -105,7 +105,6 @@ "sass-loader": "^16.0.7", "schemastery": "^3.18.0", "shorty.js": "^1.0.1", - "slideout": "^1.0.1", "sticky-kit": "^1.1.3", "style-loader": "^4.0.0", "stylus": "^0.64.0", @@ -117,7 +116,7 @@ "timeago.js": "^4.0.2", "ts-loader": "^9.5.7", "web-streams-polyfill": "^4.2.0", - "webpack": "^5.105.4", + "webpack": "^5.106.2", "webpack-bundle-analyzer": "^5.3.0", "webpack-dev-server": "^5.2.3", "webpack-manifest-plugin": "^6.0.1",