diff --git a/package.json b/package.json index 4d9e37a..4214584 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,7 @@ "scripts": { "dev": "vite --host 0.0.0.0", "preview": "vite preview", - "dist": "vite build", - "count-source-lines": "exec scripts/src-lc.sh" + "dist": "vite build" }, "keywords": [], "author": "Rubicon", diff --git a/scripts/src-lc.sh b/scripts/src-lc.sh deleted file mode 100755 index ac5ec04..0000000 --- a/scripts/src-lc.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh -# Count the source lines. - -find . '(' ! -path "./node_modules/**" ')' \ - -and '(' ! -path "./.git/**" ')' \ - -and '(' ! -path "./*dist/**" ')' \ - -and '(' ! -path "./bun.lockb" ')' \ - -and '(' ! -path "./docs/**" ')' \ - -type f -print0 \ - | wc -l --files0-from=- diff --git a/src/material/Menu.css b/src/material/Menu.css index e3e11e5..1837db3 100644 --- a/src/material/Menu.css +++ b/src/material/Menu.css @@ -7,7 +7,6 @@ width: max-content; box-shadow: var(--tutu-shadow-e8); contain: content; - overscroll-behavior: contain; &.e1 { box-shadow: var(--tutu-shadow-e9); diff --git a/src/material/Menu.tsx b/src/material/Menu.tsx index c0fe72b..0ca0f69 100644 --- a/src/material/Menu.tsx +++ b/src/material/Menu.tsx @@ -1,7 +1,6 @@ import { useWindowSize } from "@solid-primitives/resize-observer"; import { MenuList } from "@suid/material"; import { - batch, createEffect, createSignal, splitProps, @@ -66,39 +65,11 @@ export function createManagedMenuState() { return !!anchor(); }, anchor: anchor as () => Anchor, - onClose: (event: Event) => { - event.preventDefault(); - return setAnchor(); - }, + onClose: () => setAnchor(), }, ] as const; } -function animateGrowFromTopLeft( - element: HTMLElement, - opts?: Omit, -) { - const rend = element.getBoundingClientRect(); - const overflow = element.style.overflow; - element.style.overflow = "hidden"; - const duration = (rend.height / 1600) * 1000; - const animation = element.animate( - { - height: [`${rend.height / 2}px`, `${rend.height}px`], - width: [`${(rend.width / 4) * 3}px`, `${rend.width}px`], - }, - { - duration, - ...opts, - }, - ); - animation.addEventListener( - "finish", - () => (element.style.overflow = overflow), - ); - return animation; -} - /** * Material Menu Component. This component is * implemented with dialog and {@link MenuList} from SUID. @@ -107,16 +78,10 @@ function animateGrowFromTopLeft( * - Use {@link createManagedMenuState} and you don't need to manage the open and close. * - Use {@link MenuItem} from SUID as children. */ -const Menu: Component = (oprops) => { +const Menu: Component = (props) => { let root: HTMLDialogElement; const windowSize = useWindowSize(); - const [props, rest] = splitProps(oprops, [ - "open", - "onClose", - "anchor", - "MenuListProps", - "children", - ]); + const [, rest] = splitProps(props, ["open", "onClose", "anchor"]); const [anchorPos, setAnchorPos] = createSignal<{ left?: number; @@ -141,57 +106,51 @@ const Menu: Component = (oprops) => { let openAnimationOrigin: "lt" | "rt" = "lt"; - const animateOpen = () => { - const a = props.anchor(); - const { width } = windowSize; - const { left, top, right, e } = a; - const isOpened = root.open; - - // There are incomplete animations. - // For `getBoundingClientRect()`, WebKit reports the initial state - // of the element, whilst Firefox reports the final state. - // - // We skip if animations are still on the element - // to avoid the problem on WebKit. - // Here use the final state. - // - // This is a dirty workaround. It's here because the feature is still - // works with it. - // I am curious that why the ones on the other parts are works. (Rubicon) - if (root.getAnimations().length > 0) { - return; - } - - root.showModal(); - const rend = root.getBoundingClientRect(); - - if (left > width / 2) { - openAnimationOrigin = "rt"; - setAnchorPos({ - left: right - rend.width, - top, - e, - }); - } else { - openAnimationOrigin = "lt"; - setAnchorPos({ left, top, e }); - } - - if (!isOpened) { - switch (openAnimationOrigin) { - case "lt": - animateGrowFromTopLeft(root, { easing: ANIM_CURVE_STD }); - break; - case "rt": - animateGrowFromTopRight(root, { easing: ANIM_CURVE_STD }); - break; - } - } - }; - createEffect(() => { if (props.open) { - animateOpen(); + const a = props.anchor(); + + if (!root.open) { + root.showModal(); + const rend = root.getBoundingClientRect(); + + const { width } = windowSize; + const { left, top, right, e } = a; + if (left > width / 2) { + openAnimationOrigin = "rt"; + setAnchorPos({ + left: right - rend.width, + top, + e, + }); + + animateGrowFromTopRight(root, { easing: ANIM_CURVE_STD }); + } else { + openAnimationOrigin = "lt"; + setAnchorPos({ left, top, e }); + + 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 { + // TODO: update the pos + } } else { animateClose(); } @@ -226,42 +185,27 @@ const Menu: Component = (oprops) => { } }; - const onDialogClick = ( - event: MouseEvent & { currentTarget: HTMLDialogElement }, - ) => { - event.stopPropagation(); - if (event.currentTarget !== event.target) return; - if (!event.currentTarget.open) return; - - const rect = event.currentTarget.getBoundingClientRect(); - const isNotInDialog = - event.clientY < rect.top || - event.clientY > rect.bottom || - event.clientX < rect.left || - event.clientX > rect.right; - - if (isNotInDialog) { - if (props.onClose) { - if (Array.isArray(props.onClose)) { - props.onClose[0](props.onClose[1], event); - } else { - ( - props.onClose as ( - event: Event & { currentTarget: HTMLDialogElement }, - ) => void - )(event); - } - } - } - }; - return ( { + 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(); + } + }} + class={`Menu e${anchorPos().e || 0}`} style={{ left: px(anchorPos().left), top: px(anchorPos().top), diff --git a/src/material/Scaffold.css b/src/material/Scaffold.css index 30e18e5..b4a7c74 100644 --- a/src/material/Scaffold.css +++ b/src/material/Scaffold.css @@ -4,9 +4,6 @@ z-index: var(--tutu-zidx-nav, auto); .MuiToolbar-root { - margin-left: var(--safe-area-inset-left); - margin-right: var(--safe-area-inset-right); - >.MuiButtonBase-root { &:first-child { margin-left: -0.5em; diff --git a/src/platform/StackedRouter.css b/src/platform/StackedRouter.css index a946a2d..eac26ce 100644 --- a/src/platform/StackedRouter.css +++ b/src/platform/StackedRouter.css @@ -15,16 +15,6 @@ dialog.StackedPage { width: 560px; max-height: 100vh; max-height: 100dvh; - /* - * WebKit does not see contain-instric-size as the real element size. - * If the container does not have height, the child element using 100% - * height (usually Scafflod in our case) was have 0px computed height. - * - * This behaviour is different from Firefox. So we need to actually - * define the box height here. (Rubicon) - */ - height: 100vh; - height: 100dvh; background: none; display: none; @@ -41,9 +31,10 @@ dialog.StackedPage { @media (max-width: 560px) { & { - margin: 0; width: 100vw; width: 100dvw; + height: 100vh; + height: 100dvh; contain-intrinsic-size: 100vw 100vh; contain-intrinsic-size: 100dvw 100dvh; } diff --git a/src/timelines/ProfileMenuButton.tsx b/src/timelines/ProfileMenuButton.tsx index 3202ae8..25ff2c9 100644 --- a/src/timelines/ProfileMenuButton.tsx +++ b/src/timelines/ProfileMenuButton.tsx @@ -7,7 +7,11 @@ import { ListItemText, MenuItem, } from "@suid/material"; -import { Show, createUniqueId, type ParentComponent } from "solid-js"; +import { + Show, + createUniqueId, + type ParentComponent, +} from "solid-js"; import { Settings as SettingsIcon, Bookmark as BookmarkIcon, @@ -35,7 +39,9 @@ const ProfileMenuButton: ParentComponent<{ const [open, state] = createManagedMenuState(); - const onClick = (event: { currentTarget: HTMLElement }) => { + const onClick = ( + event: MouseEvent & { currentTarget: HTMLButtonElement }, + ) => { open(event.currentTarget.getBoundingClientRect()); }; @@ -48,8 +54,8 @@ const ProfileMenuButton: ParentComponent<{ sx={{ borderRadius: "50%" }} id={buttonId} onClick={onClick} - aria-controls={state.open ? menuId : undefined} - aria-expanded={state.open ? "true" : "false"} + aria-controls={open() ? menuId : undefined} + aria-expanded={open() ? "true" : undefined} >