From f129574b62255e40e1dcfbbebc03948fd69a25d8 Mon Sep 17 00:00:00 2001 From: retoor Date: Sat, 3 Jan 2026 14:13:55 +0100 Subject: [PATCH] chore: update html, js files --- CHANGELOG.md | 8 ++ pyproject.toml | 2 +- src/snek/static/nav-menu.js | 232 ++++++++++++++++++++++++++++++++++++ src/snek/templates/app.html | 39 +++--- 4 files changed, 256 insertions(+), 25 deletions(-) create mode 100644 src/snek/static/nav-menu.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 326be43..4fc19a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,14 @@ + +## Version 1.18.0 - 2026-01-03 + +update html, js files + +**Changes:** 2 files, 271 lines +**Languages:** HTML (39 lines), JavaScript (232 lines) + ## Version 1.17.0 - 2026-01-03 update css, js, py files diff --git a/pyproject.toml b/pyproject.toml index b7a9d74..6fd6625 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "Snek" -version = "1.17.0" +version = "1.18.0" readme = "README.md" #license = { file = "LICENSE", content-type="text/markdown" } description = "Snek Chat Application by Molodetz" diff --git a/src/snek/static/nav-menu.js b/src/snek/static/nav-menu.js new file mode 100644 index 0000000..535d11d --- /dev/null +++ b/src/snek/static/nav-menu.js @@ -0,0 +1,232 @@ +// retoor + +class NavMenu extends HTMLElement { + constructor() { + super(); + this.attachShadow({ mode: 'open' }); + this._isOpen = false; + this._boundClickOutside = this._handleClickOutside.bind(this); + + const style = document.createElement('style'); + style.textContent = ` + :host { + display: inline-block; + position: relative; + } + + .nav-container { + position: relative; + } + + .menu-toggle { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 4px; + width: 32px; + height: 32px; + padding: 6px; + background: none; + border: none; + border-radius: 4px; + cursor: pointer; + transition: all 0.2s ease; + } + + .menu-toggle .bar { + width: 18px; + height: 2px; + background-color: #888; + border-radius: 1px; + transition: all 0.2s ease; + } + + .menu-toggle:hover .bar { + background-color: #fff; + } + + .menu-toggle:hover { + background-color: rgba(255, 255, 255, 0.05); + } + + :host([open]) .menu-toggle { + background-color: rgba(255, 255, 255, 0.05); + } + + :host([open]) .menu-toggle .bar { + background-color: #fff; + } + + :host([open]) .menu-toggle .bar:nth-child(1) { + transform: rotate(45deg) translate(4px, 4px); + } + + :host([open]) .menu-toggle .bar:nth-child(2) { + opacity: 0; + } + + :host([open]) .menu-toggle .bar:nth-child(3) { + transform: rotate(-45deg) translate(4px, -4px); + } + + .menu-panel { + position: absolute; + top: 100%; + right: 0; + margin-top: 8px; + background-color: #111; + border: 1px solid #333; + border-radius: 8px; + padding: 8px 0; + display: none; + flex-direction: column; + min-width: 180px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4); + z-index: 1000; + } + + :host([open]) .menu-panel { + display: flex; + } + + .menu-item { + display: flex; + align-items: center; + gap: 12px; + padding: 10px 16px; + color: #888; + text-decoration: none; + font-size: 0.95em; + transition: all 0.2s ease; + cursor: pointer; + border: none; + background: none; + width: 100%; + text-align: left; + } + + .menu-item:hover { + color: #fff; + background-color: rgba(255, 255, 255, 0.05); + } + + .menu-item .icon { + font-size: 1.1em; + } + + .menu-item.hidden { + display: none; + } + `; + + const container = document.createElement('div'); + container.className = 'nav-container'; + + this._menuPanel = document.createElement('div'); + this._menuPanel.className = 'menu-panel'; + + this._toggleButton = document.createElement('button'); + this._toggleButton.className = 'menu-toggle'; + this._toggleButton.setAttribute('aria-label', 'Toggle navigation menu'); + this._toggleButton.innerHTML = ''; + + this._toggleButton.addEventListener('click', (e) => { + e.stopPropagation(); + this._toggle(); + }); + + this._createMenuItems(); + + container.appendChild(this._menuPanel); + container.appendChild(this._toggleButton); + this.shadowRoot.append(style, container); + } + + _createMenuItems() { + const items = [ + { icon: '🏠', label: 'Home', href: '/web.html' }, + { icon: '📂', label: 'Files', href: '/drive.html' }, + { icon: '🔍', label: 'Search', href: '/search-user.html' }, + { icon: '📥', label: 'Install', href: '#', id: 'install-button', hidden: true }, + { icon: '💬', label: 'Forum', href: '/forum/index.html' }, + { icon: '⚙️', label: 'Settings', href: '/settings/index.html' }, + { icon: '✉️', label: 'Notifications', href: '#', action: 'notifications' }, + { icon: '🔒', label: 'Logout', href: '/logout.html' } + ]; + + items.forEach(item => { + const link = document.createElement('a'); + link.className = 'menu-item'; + if (item.hidden) { + link.classList.add('hidden'); + } + link.href = item.href; + if (item.id) { + link.id = item.id; + this['_' + item.id.replace('-', '')] = link; + } + if (item.action === 'notifications') { + link.addEventListener('click', (e) => { + e.preventDefault(); + this._close(); + if (typeof window.registerNotificationsServiceWorker === 'function') { + window.registerNotificationsServiceWorker(); + } + }); + } else if (item.href !== '#') { + link.addEventListener('click', () => { + this._close(); + }); + } + link.innerHTML = `${item.icon}${item.label}`; + this._menuPanel.appendChild(link); + }); + } + + _toggle() { + if (this._isOpen) { + this._close(); + } else { + this._open(); + } + } + + _open() { + this._isOpen = true; + this.setAttribute('open', ''); + setTimeout(() => { + document.addEventListener('click', this._boundClickOutside); + }, 0); + } + + _close() { + this._isOpen = false; + this.removeAttribute('open'); + document.removeEventListener('click', this._boundClickOutside); + } + + _handleClickOutside(event) { + const path = event.composedPath(); + if (!path.includes(this)) { + this._close(); + } + } + + showInstallButton() { + const installBtn = this.shadowRoot.getElementById('install-button'); + if (installBtn) { + installBtn.classList.remove('hidden'); + } + } + + getInstallButton() { + return this.shadowRoot.getElementById('install-button'); + } + + disconnectedCallback() { + document.removeEventListener('click', this._boundClickOutside); + } +} + +customElements.define('nav-menu', NavMenu); diff --git a/src/snek/templates/app.html b/src/snek/templates/app.html index 46b124a..5ceb4a2 100644 --- a/src/snek/templates/app.html +++ b/src/snek/templates/app.html @@ -13,6 +13,7 @@ + @@ -40,17 +41,7 @@
- - +
{% block sidebar %} @@ -152,21 +143,21 @@ app.starField.renderWord("H4x0r 1337") } }) -let installPrompt = null +let installPrompt = null window.addEventListener("beforeinstallprompt", (e) => { - //e.preventDefault(); installPrompt = e; - //document.addEventListener("DOMContentLoaded", () => { - const button = document.getElementById("install-button") - - button.style.display = 'inline-block' - - button.addEventListener("click", async ()=>{ - const result = await installPrompt.prompt() - }) - - - }) + const navMenu = document.querySelector('nav-menu'); + if (navMenu) { + navMenu.showInstallButton(); + const button = navMenu.getInstallButton(); + if (button) { + button.addEventListener("click", async (evt) => { + evt.preventDefault(); + const result = await installPrompt.prompt(); + }); + } + } + }) ;