This commit is contained in:
parent
556e0b2336
commit
4958fb1346
1 changed files with 102 additions and 55 deletions
|
@ -14,56 +14,84 @@ type Props = {
|
||||||
anchor: () => DOMRect;
|
anchor: () => DOMRect;
|
||||||
};
|
};
|
||||||
|
|
||||||
function adjustMenuPosition(
|
function px(n?: number) {
|
||||||
rect: DOMRect,
|
if (n) {
|
||||||
[left, top]: [number, number],
|
return `${n}px`;
|
||||||
{ width, height }: { width: number; height: number },
|
} else {
|
||||||
) {
|
return undefined;
|
||||||
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) => {
|
const Menu: ParentComponent<Props> = (props) => {
|
||||||
let root: HTMLDialogElement;
|
let root: HTMLDialogElement;
|
||||||
const [pos, setPos] = createSignal<[number, number]>([0, 0]);
|
|
||||||
const windowSize = useWindowSize();
|
const windowSize = useWindowSize();
|
||||||
|
|
||||||
|
const [anchorPos, setAnchorPos] = createSignal<{
|
||||||
|
left?: number;
|
||||||
|
top?: number;
|
||||||
|
}>({});
|
||||||
|
|
||||||
|
let openAnimationOrigin: "lt" | "rt" = "lt";
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
if (props.open) {
|
if (props.open) {
|
||||||
const a = props.anchor();
|
const a = props.anchor();
|
||||||
|
|
||||||
if (!root.open) {
|
if (!root.open) {
|
||||||
root.showModal();
|
root.showModal();
|
||||||
const rend = root.getBoundingClientRect();
|
const rend = root.getBoundingClientRect();
|
||||||
|
|
||||||
setPos(adjustMenuPosition(rend, [a.left, a.top], windowSize));
|
const { width } = windowSize;
|
||||||
|
const { left, top, right } = a;
|
||||||
|
if (left > width / 2) {
|
||||||
|
openAnimationOrigin = "rt";
|
||||||
|
setAnchorPos({
|
||||||
|
left: right - rend.width,
|
||||||
|
top,
|
||||||
|
});
|
||||||
|
|
||||||
const overflow = root.style.overflow;
|
const overflow = root.style.overflow;
|
||||||
root.style.overflow = "hidden";
|
root.style.overflow = "hidden";
|
||||||
const duration = (rend.height / 1600) * 1000;
|
const duration = (rend.height / 1600) * 1000;
|
||||||
const easing = ANIM_CURVE_STD;
|
const easing = ANIM_CURVE_STD;
|
||||||
const animation = root.animate(
|
const animation = root.animate(
|
||||||
{
|
{
|
||||||
height: [`${rend.height / 2}px`, `${rend.height}px`],
|
height: [`${rend.height / 2}px`, `${rend.height}px`],
|
||||||
width: [`${rend.width / 4 * 3}px`, `${rend.width}px`],
|
},
|
||||||
},
|
{
|
||||||
{
|
duration,
|
||||||
duration,
|
easing,
|
||||||
easing,
|
},
|
||||||
},
|
);
|
||||||
);
|
animation.addEventListener(
|
||||||
animation.addEventListener(
|
"finish",
|
||||||
"finish",
|
() => (root.style.overflow = overflow),
|
||||||
() => (root.style.overflow = overflow),
|
);
|
||||||
);
|
} else {
|
||||||
|
openAnimationOrigin = "lt";
|
||||||
|
setAnchorPos({ left, top });
|
||||||
|
|
||||||
|
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 {
|
} else {
|
||||||
setPos(
|
// TODO: update the pos
|
||||||
adjustMenuPosition(
|
|
||||||
root.getBoundingClientRect(),
|
|
||||||
[a.left, a.top],
|
|
||||||
windowSize,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
animateClose();
|
animateClose();
|
||||||
|
@ -72,22 +100,40 @@ const Menu: ParentComponent<Props> = (props) => {
|
||||||
|
|
||||||
const animateClose = () => {
|
const animateClose = () => {
|
||||||
const rend = root.getBoundingClientRect();
|
const rend = root.getBoundingClientRect();
|
||||||
const overflow = root.style.overflow;
|
if (openAnimationOrigin === "lt") {
|
||||||
root.style.overflow = "hidden";
|
const overflow = root.style.overflow;
|
||||||
const animation = root.animate(
|
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`],
|
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,
|
duration: (rend.height / 2 / 1600) * 1000,
|
||||||
},
|
easing: ANIM_CURVE_STD,
|
||||||
);
|
},
|
||||||
animation.addEventListener("finish", () => {
|
);
|
||||||
root.style.overflow = overflow;
|
animation.addEventListener("finish", () => {
|
||||||
root.close();
|
root.style.overflow = overflow;
|
||||||
});
|
root.close();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const overflow = root.style.overflow;
|
||||||
|
root.style.overflow = "hidden";
|
||||||
|
const animation = root.animate(
|
||||||
|
{
|
||||||
|
height: [`${rend.height}px`, `${rend.height / 2}px`],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
duration: (rend.height / 2 / 1600) * 1000,
|
||||||
|
easing: ANIM_CURVE_STD,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
animation.addEventListener("finish", () => {
|
||||||
|
root.style.overflow = overflow;
|
||||||
|
root.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -111,14 +157,15 @@ const Menu: ParentComponent<Props> = (props) => {
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
style={{
|
style={{
|
||||||
position: "absolute",
|
position: "fixed",
|
||||||
left: `${pos()[0]}px`,
|
left: px(anchorPos().left),
|
||||||
top: `${pos()[1]}px`,
|
top: px(anchorPos().top),
|
||||||
border: "none",
|
border: "none",
|
||||||
|
"border-radius": "2px",
|
||||||
padding: 0,
|
padding: 0,
|
||||||
"max-width": "560px",
|
"max-width": "560px",
|
||||||
width: "max-content",
|
width: "max-content",
|
||||||
/*"min-width": "20vw", */
|
/* FIXME: the content may be overflow */
|
||||||
"box-shadow": "var(--tutu-shadow-e8)",
|
"box-shadow": "var(--tutu-shadow-e8)",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
Loading…
Reference in a new issue