feat(web): open spaces as a plain chat on phones with a side drawer
Spaces, channels, files, members and the account move into a left drawer, the top bar keeps only the essentials, and touch devices get a tap menu instead of always-visible delete buttons. Also fixes the history checkboxes in the channel settings sheet.
This commit is contained in:
1 parent
06fe077ef1
commit
1db3795d6a
16 files changed
+383
-56
No files matched your search
@@ -0,0 +1,40 @@
|
||||
.scrim {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 50;
|
||||
background: var(--scrim);
|
||||
animation: drawer-scrim-in 0.15s ease-out;
|
||||
}
|
||||
|
||||
.drawer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: min(320px, 86vw);
|
||||
height: 100%;
|
||||
padding-top: env(safe-area-inset-top);
|
||||
border-right: 1px solid var(--line);
|
||||
background: var(--bg);
|
||||
box-shadow: var(--shadow-sheet);
|
||||
outline: none;
|
||||
animation: drawer-in 0.2s ease-out;
|
||||
}
|
||||
|
||||
@keyframes drawer-scrim-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes drawer-in {
|
||||
from {
|
||||
translate: -24px 0;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.scrim,
|
||||
.drawer {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { useEffect, useRef, type ReactNode } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { useBackToClose } from '@/lib/useBackToClose';
|
||||
import styles from './Drawer.module.css';
|
||||
|
||||
interface Props { label: string; onClose: () => void; children: ReactNode }
|
||||
|
||||
/** 手机的左侧抽屉:遮罩是纯色,返回键关闭(与 Sheet 一致) */
|
||||
export function Drawer({ label, onClose, children }: Props) {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const close = useRef(onClose);
|
||||
useEffect(() => { close.current = onClose; }, [onClose]);
|
||||
useBackToClose(onClose);
|
||||
useEffect(() => {
|
||||
const prev = document.activeElement as HTMLElement | null;
|
||||
ref.current?.focus();
|
||||
const onKey = (event: KeyboardEvent) => { if (event.key === 'Escape') close.current(); };
|
||||
window.addEventListener('keydown', onKey);
|
||||
return () => { window.removeEventListener('keydown', onKey); prev?.focus(); };
|
||||
}, []);
|
||||
return createPortal(
|
||||
<div className={styles.scrim} onClick={onClose}>
|
||||
<div ref={ref} tabIndex={-1} role="dialog" aria-modal="true" aria-label={label} className={styles.drawer} onClick={(event) => event.stopPropagation()}>
|
||||
{children}
|
||||
</div>
|
||||
</div>,
|
||||
document.body,
|
||||
);
|
||||
}
|
||||
@@ -21,6 +21,8 @@ const PATHS = {
|
||||
phone: 'M8 3h8v18H8z M11 18h2',
|
||||
tablet: 'M5 4h14v16H5z M11 17h2',
|
||||
more: 'M5 12h.01 M12 12h.01 M19 12h.01',
|
||||
menu: 'M4 7h16 M4 12h16 M4 17h16',
|
||||
users: 'M9 11a3 3 0 100-6 3 3 0 000 6z M3 19a6 6 0 0112 0 M16 6a3 3 0 010 6 M18 19a5 5 0 00-3-4.6',
|
||||
undo: 'M9 14L4 9l5-5 M4 9h10a6 6 0 010 12h-3',
|
||||
warn: 'M12 4l9 16H3z M12 10v4 M12 17h.01',
|
||||
sun: 'M12 8a4 4 0 100 8 4 4 0 000-8z M12 2v2 M12 20v2 M2 12h2 M20 12h2 M5 5l1.5 1.5 M17.5 17.5L19 19 M5 19l1.5-1.5 M17.5 6.5L19 5',
|
||||
|
||||
Reference in new issue
Block a user