mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-11-07 02:55:24 +00:00
feat: shared tile look for cards and pages, plus new page pieces in SDK 1.1.0
This commit is contained in:
1 parent
1867be3f58
commit
19bdd589f3
41 files changed
+1497
-207
No files matched your search
@@ -0,0 +1,86 @@
|
||||
/* eslint-disable react-refresh/only-export-components */
|
||||
import {
|
||||
createContext,
|
||||
useContext,
|
||||
useLayoutEffect,
|
||||
useRef,
|
||||
useState,
|
||||
type ReactNode,
|
||||
} from "react";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export function scrollParent(element: HTMLElement | null) {
|
||||
for (let node = element?.parentElement; node; node = node.parentElement) {
|
||||
const { overflowY } = getComputedStyle(node);
|
||||
if (overflowY === "auto" || overflowY === "scroll") return node;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** True inside a TileStack or TileColumn section, which draws its own line. */
|
||||
const EdgeOwnedContext = createContext(false);
|
||||
|
||||
export function EdgeOwned({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<EdgeOwnedContext.Provider value={true}>
|
||||
{children}
|
||||
</EdgeOwnedContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the element's end reaches the bottom of the visible scroll area,
|
||||
* where a closing line would sit on the screen edge and only add noise.
|
||||
*/
|
||||
function useReachesBottom(ref: React.RefObject<HTMLElement | null>) {
|
||||
const [reaches, setReaches] = useState(false);
|
||||
const reachesRef = useRef(reaches);
|
||||
reachesRef.current = reaches;
|
||||
useLayoutEffect(() => {
|
||||
const element = ref.current;
|
||||
const scroller = scrollParent(element);
|
||||
if (!element || !scroller) return;
|
||||
// Measured without the line itself, so showing it never flips the answer.
|
||||
const update = () => {
|
||||
const end =
|
||||
element.getBoundingClientRect().bottom -
|
||||
scroller.getBoundingClientRect().top +
|
||||
scroller.scrollTop -
|
||||
(reachesRef.current ? 0 : 1);
|
||||
setReaches(end >= scroller.clientHeight - 0.5);
|
||||
};
|
||||
update();
|
||||
const observer = new ResizeObserver(update);
|
||||
observer.observe(scroller);
|
||||
observer.observe(element);
|
||||
return () => observer.disconnect();
|
||||
}, [ref]);
|
||||
return reaches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps tiles whose last row draws a line under itself. The line stays when
|
||||
* there is room below and is clipped when the tiles reach the bottom of the
|
||||
* screen, the same rule the tile grid uses. Inside a stack section the
|
||||
* section owns the line, so it is always clipped.
|
||||
*/
|
||||
export function BottomEdge({
|
||||
className,
|
||||
innerClassName,
|
||||
children,
|
||||
}: {
|
||||
className?: string;
|
||||
innerClassName?: string;
|
||||
children: ReactNode;
|
||||
}) {
|
||||
const owned = useContext(EdgeOwnedContext);
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const reaches = useReachesBottom(ref);
|
||||
return (
|
||||
<div ref={ref} className={cn("min-w-0 overflow-clip", className)}>
|
||||
<div className={cn((owned || reaches) && "-mb-px", innerClassName)}>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user