feat(web): expand/collapse sidebar (#16768)

* feat: expand/collapse sidebar

* fix: general PR cleanup

- add skip link unit test
- remove unused tailwind styles
- adjust asset grid spacing
- fix event propogation

* fix: cleaning up event listeners

* fix: purchase modal and button on small screens

* fix: explicit tailwind classes

* fix: no animation on initial page load

* fix: sidebar spacing and reactivity

* chore: reverting changes to icons in nav and account info panel

* fix: remove left margin from the asset grid after merging in new timeline

* chore: extract search-bar changes for a separate PR

* fix: add margin to memories
This commit is contained in:
Ben
2025-04-01 22:12:04 -04:00
committed by GitHub
parent 00d3b8d83a
commit 6e62c09d84
23 changed files with 193 additions and 66 deletions
+32 -8
View File
@@ -1,16 +1,34 @@
import { shortcuts } from '$lib/actions/shortcut';
import { tick } from 'svelte';
const selectors =
'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
interface Options {
/**
* Set whether the trap is active or not.
*/
active?: boolean;
}
export function focusTrap(container: HTMLElement) {
const selectors =
'button:not([disabled], .hidden), [href]:not(.hidden), input:not([disabled], .hidden), select:not([disabled], .hidden), textarea:not([disabled], .hidden), [tabindex]:not([tabindex="-1"], .hidden)';
export function focusTrap(container: HTMLElement, options?: Options) {
const triggerElement = document.activeElement;
const focusableElement = container.querySelector<HTMLElement>(selectors);
const withDefaults = (options?: Options) => {
return {
active: options?.active ?? true,
};
};
// Use tick() to ensure focus trap works correctly inside <Portal />
void tick().then(() => focusableElement?.focus());
const setInitialFocus = () => {
const focusableElement = container.querySelector<HTMLElement>(selectors);
// Use tick() to ensure focus trap works correctly inside <Portal />
void tick().then(() => focusableElement?.focus());
};
if (withDefaults(options).active) {
setInitialFocus();
}
const getFocusableElements = (): [HTMLElement | null, HTMLElement | null] => {
const focusableElements = container.querySelectorAll<HTMLElement>(selectors);
@@ -27,7 +45,7 @@ export function focusTrap(container: HTMLElement) {
shortcut: { key: 'Tab' },
onShortcut: (event) => {
const [firstElement, lastElement] = getFocusableElements();
if (document.activeElement === lastElement) {
if (document.activeElement === lastElement && withDefaults(options).active) {
event.preventDefault();
firstElement?.focus();
}
@@ -39,7 +57,7 @@ export function focusTrap(container: HTMLElement) {
shortcut: { key: 'Tab', shift: true },
onShortcut: (event) => {
const [firstElement, lastElement] = getFocusableElements();
if (document.activeElement === firstElement) {
if (document.activeElement === firstElement && withDefaults(options).active) {
event.preventDefault();
lastElement?.focus();
}
@@ -48,6 +66,12 @@ export function focusTrap(container: HTMLElement) {
]);
return {
update(newOptions?: Options) {
options = newOptions;
if (withDefaults(options).active) {
setInitialFocus();
}
},
destroy() {
destroyShortcuts?.();
if (triggerElement instanceof HTMLElement) {