Profile: filter recent toots

- material: new Menu component
- material/theme: animation curves
- TootFilterButton
This commit is contained in:
thislight 2024-10-24 23:47:44 +08:00
parent dff6abd379
commit b22ac67c3a
No known key found for this signature in database
GPG key ID: A50F9451AC56A63E
5 changed files with 284 additions and 5 deletions

136
src/material/Menu.tsx Normal file
View file

@ -0,0 +1,136 @@
import { useWindowSize } from "@solid-primitives/resize-observer";
import { MenuList } from "@suid/material";
import {
createEffect,
createSignal,
type JSX,
type ParentComponent,
} from "solid-js";
import { ANIM_CURVE_STD } from "./theme";
type Props = {
open?: boolean;
onClose?: JSX.EventHandlerUnion<HTMLDialogElement, Event>;
anchor: () => DOMRect;
};
function adjustMenuPosition(
rect: DOMRect,
[left, top]: [number, number],
{ width, height }: { width: number; height: number },
) {
const ntop = rect.bottom > height ? top - (rect.bottom - height) : top;
const nleft = rect.right > width ? left - (rect.right - width) : left;
return [nleft, ntop] as [number, number];
}
const Menu: ParentComponent<Props> = (props) => {
let root: HTMLDialogElement;
const [pos, setPos] = createSignal<[number, number]>([0, 0]);
const windowSize = useWindowSize();
createEffect(() => {
if (props.open) {
const a = props.anchor();
if (!root.open) {
root.showModal();
const rend = root.getBoundingClientRect();
setPos(adjustMenuPosition(rend, [a.left, a.top], windowSize));
const overflow = root.style.overflow;
root.style.overflow = "hidden";
const duration = (rend.height / 1600) * 1000;
const easing = ANIM_CURVE_STD;
const animation = root.animate(
{
height: [`${rend.height / 2}px`, `${rend.height}px`],
width: [`${rend.width / 4 * 3}px`, `${rend.width}px`],
},
{
duration,
easing,
},
);
animation.addEventListener(
"finish",
() => (root.style.overflow = overflow),
);
} else {
setPos(
adjustMenuPosition(
root.getBoundingClientRect(),
[a.left, a.top],
windowSize,
),
);
}
} else {
animateClose();
}
});
const animateClose = () => {
const rend = root.getBoundingClientRect();
const overflow = root.style.overflow;
root.style.overflow = "hidden";
const animation = root.animate(
{
height: [`${rend.height}px`, `${rend.height / 2}px`],
width: [`${rend.width}px`, `${rend.width / 4 * 3}px`],
},
{
duration: (rend.height / 2 / 1600) * 1000,
easing: ANIM_CURVE_STD,
},
);
animation.addEventListener("finish", () => {
root.style.overflow = overflow;
root.close();
});
};
return (
<dialog
ref={root!}
onClose={props.onClose}
onClick={(e) => {
if (e.target === root) {
if (props.onClose) {
if (Array.isArray(props.onClose)) {
props.onClose[0](props.onClose[1], e);
} else {
(
props.onClose as (
event: Event & { currentTarget: HTMLDialogElement },
) => void
)(e);
}
}
e.stopPropagation();
}
}}
style={{
position: "absolute",
left: `${pos()[0]}px`,
top: `${pos()[1]}px`,
border: "none",
padding: 0,
"max-width": "560px",
width: "max-content",
/*"min-width": "20vw", */
"box-shadow": "var(--tutu-shadow-e8)",
}}
>
<div
style={{
background: "var(--tutu-color-surface)",
}}
>
<MenuList>{props.children}</MenuList>
</div>
</dialog>
);
};
export default Menu;