diff --git a/package.json b/package.json index fd1d73ce..37566562 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "impro", - "version": "0.18.33", + "version": "0.18.34", "type": "module", "scripts": { "start": "rm -rf \"${BUILD_DIR:-build}\" && NODE_ENV=development eleventy --serve", @@ -10,6 +10,7 @@ "ios": "npm run build && cap sync && NODE_ENV=development cap run ios", "bundle:lit-html": "esbuild ./clientLibs/litHtml.js --bundle --format=esm --outfile=src/js/lib/lit-html.js", "format": "pretty-quick --staged", + "format:all": "prettier --write .", "test": "npm run test:unit", "test:unit": "node --import ./tests/unit/env.js --test-isolation=none --test-force-exit --test 'tests/unit/specs/**/*.test.js'", "test:unit:coverage": "node --import ./tests/unit/env.js --test-isolation=none --test-force-exit --experimental-test-coverage --test-coverage-include='src/js/**' --test 'tests/unit/specs/**/*.test.js'", diff --git a/src/css/style.css b/src/css/style.css index 1b183304..3201d625 100644 --- a/src/css/style.css +++ b/src/css/style.css @@ -1044,6 +1044,8 @@ tab-bar:not([full-width]) { .sidebar::backdrop { background: var(--overlay-color); + opacity: 0; + transition: opacity 0.3s ease; } .sidebar { @@ -1131,6 +1133,19 @@ tab-bar:not([full-width]) { transform: translateX(0); } +.sidebar[open]::backdrop { + opacity: 1; +} + +.sidebar[open][data-closing] { + pointer-events: none; + transform: translateX(-100%); +} + +.sidebar[open][data-closing]::backdrop { + opacity: 0; +} + .sidebar-profile { padding: 20px 20px 16px 20px; } @@ -5974,6 +5989,26 @@ emoji-picker-dialog emoji-picker, } } +@keyframes fade-out { + from { + opacity: 1; + } + to { + opacity: 0; + } +} + +@keyframes slide-down { + from { + transform: translateY(0); + opacity: 1; + } + to { + transform: translateY(100%); + opacity: 0; + } +} + .bottom-sheet { border: var(--hair) solid var(--post-border-color); border-bottom: none; @@ -5997,6 +6032,26 @@ emoji-picker-dialog emoji-picker, padding-bottom: var(--safe-area-inset-bottom); } +/* Shared scroll region for a bottom sheet's flex-fill body: fills the space + between a fixed header/footer and scrolls its own overflow. Requires a + flex-column ancestor (the dialog or content wrapper) to give it height. */ +.sheet-scroll-region { + flex: 1 1 auto; + min-height: 0; + overflow-y: auto; +} + +.bottom-sheet[data-closing] { + pointer-events: none; +} + +/* Only mobile bottom sheets animate out; on desktop they close instantly. */ +@media (max-width: 799px) { + .bottom-sheet[data-closing] { + animation: slide-down 0.15s ease-in forwards; + } +} + @media (min-width: 800px) { .bottom-sheet { padding-bottom: 0; @@ -6042,6 +6097,10 @@ emoji-picker-dialog emoji-picker, animation: fade-in 0.2s ease-out; } +.bottom-sheet[data-closing]::backdrop { + animation: fade-out 0.15s ease-in forwards; +} + @media (max-width: 799px) { .bottom-sheet-fullscreen { top: var(--sheet-top); @@ -6668,6 +6727,10 @@ context-menu-item-group:not(:last-child) .context-menu-item-group { animation: fade-in 0.2s ease-out; } +.modal-dialog[data-closing] { + pointer-events: none; +} + .modal-dialog-title { font-size: 24px; font-weight: 600; @@ -6787,6 +6850,11 @@ context-menu-item-group:not(:last-child) .context-menu-item-group { } } +.image-alt-text-dialog { + display: flex; + flex-direction: column; +} + .image-alt-text-dialog-content { padding: 20px; display: flex; @@ -6796,6 +6864,12 @@ context-menu-item-group:not(:last-child) .context-menu-item-group { min-height: 0; } +@media (min-width: 800px) { + .image-alt-text-dialog-content { + max-height: 80vh; + } +} + .image-alt-text-dialog-header { display: flex; justify-content: space-between; @@ -7657,6 +7731,7 @@ context-menu-item-group:not(:last-child) .context-menu-item-group { word-break: break-word; display: -webkit-box; -webkit-line-clamp: 8; + line-clamp: 8; -webkit-box-orient: vertical; overflow: hidden; } @@ -9221,9 +9296,6 @@ tab-bar[full-width] .tab-bar-button { display: flex; flex-direction: column; gap: 16px; - flex: 1 1 auto; - min-height: 0; - overflow-y: auto; } @media (min-width: 800px) { diff --git a/src/index.html b/src/index.html index 4fc8285d..9be5a1b1 100644 --- a/src/index.html +++ b/src/index.html @@ -157,6 +157,7 @@ import { IdentityResolver } from "/js/atproto.js"; import { Router } from "/js/router.js"; import { scrollLocks } from "/js/scrollLocks.js"; + import { closeWithAnimation } from "/js/dialogHelpers.js"; import { Api } from "/js/api.js"; import { auth } from "/js/auth.js"; import { NotificationService } from "/js/notificationService.js"; @@ -514,7 +515,7 @@ if (wrapper && typeof wrapper.close === "function") { wrapper.close(); } else { - dialog.close(); + closeWithAnimation(dialog); } }); }); diff --git a/src/js/components/account-switcher-dialog.js b/src/js/components/account-switcher-dialog.js index e31d50e9..d7642e6e 100644 --- a/src/js/components/account-switcher-dialog.js +++ b/src/js/components/account-switcher-dialog.js @@ -1,7 +1,7 @@ import { html, render } from "/js/lib/lit-html.js"; import { Component } from "/js/components/component.js"; import { scrollLocks } from "/js/scrollLocks.js"; -import { enableDragToDismiss } from "/js/utils.js"; +import { closeWithAnimation, enableDragToDismiss } from "/js/dialogHelpers.js"; import { auth, getLoginErrorMessage } from "/js/auth.js"; import { Signal, ReactiveStore, effect } from "/js/signals.js"; import { showToast } from "/js/toasts.js"; @@ -101,6 +101,11 @@ class AccountSwitcherDialog extends Component { this.close(); } }} + @close=${() => { + this.scrollLock?.release(); + this.scrollLock = null; + this.dispatchEvent(new CustomEvent("dialog-closed")); + }} >