import { useRef, useState } from "react"; import { Badge, Card, CardContent, CardDescription, CardHeader, CardTitle, Descriptions, ScrollArea, } from "@godxjp/ui/data-display"; import { Button, Heading, Paragraph, Text } from "@godxjp/ui/general"; import { Affix, Flex, PageContainer } from "@godxjp/ui/layout"; import { Check, Filter } from "lucide-react"; /** * Affix — Ant Design `Affix` (6.6.5). Pin an element to its scrollport, and REPORT that it is * pinned — which is the whole difference from `position: sticky`, and the reason both website * showcases in docs/showcase/ wrote `position: sticky` and got nothing usable out of it. * * The cases on this page are the ones that break a hand-rolled sticky header: * * — the PAGE JUMP. Taking a bar out of flow removes its height, so everything below it moves up by * exactly that height. Card 1 prints the measured number it is holding open; * — the CONDENSE. The bar is a different bar once pinned — a smaller control, a shorter title, a * monogram instead of a wordmark — and nothing but the reported boolean can drive that; * — a SCROLLING CONTAINER that is not the page, which is `target`; * — the BLOCK-END pin, which is the sticky form footer and not the sticky header; * — a bar of CJK beside Latin and a 71-character unbreakable token, because the measured height is * only right if it is measured at the width it is actually laid out at; * — and the narrow case, where the same bar wraps to two lines and the placeholder has to follow. * * Composed only from real @godxjp/ui components. */ /** A real filename in a real toolbar: 71 characters with nowhere to break. */ const UNBREAKABLE = "RegionalDeploymentConfigurationParameterNamespaceIdentifierOverrides123"; function Filler({ id, title, lines }: { id: string; title: string; lines: number }) { return (
{title} {Array.from({ length: lines }, (_, line) => ( スクロールしてください。Scroll on, and keep scrolling: the bar above only becomes interesting once the page has gone far enough past it for the pin to fire. ))}
); } export default function Demo() { const paneRef = useRef(null); const [pinned, setPinned] = useState(false); const [paneP, setPanePinned] = useState(false); const [footerPinned, setFooterPinned] = useState(false); return ( 1 · The condensing bar, against the page Everything that changes below is driven by `onChange` alone: the title loses its qualifier, the primary action drops a size tier, and the state badge flips. Nothing here is a second measurement, and `position: sticky` can drive none of it. {pinned ? "請求書 · Invoices" : "請求書 · Invoices, 2026 年度 (all regions)"} {pinned ? "affixed" : "in flow"} 2 · Inside a scrolling target, not the page `target` is Ant Design's, and it is the answer to a page whose scroll belongs to a pane: the bar pins against the pane's edge and stops there, not at the top of the window. The label carries a 71-character unbreakable token beside CJK, so the measured height is taken at the width the bar is really laid out at. paneRef.current} onChange={setPanePinned}> 3 · offsetBlockEnd · the sticky form footer Passing the block-END offset (and no block-start one) is what selects bottom pinning, exactly as in Ant Design. It releases the moment the real end of the form reaches it, which is the behaviour a `position: sticky` footer cannot express without a second element. {footerPinned ? "未保存の変更があります" : "すべて保存済み"} {footerPinned ? "affixed" : "in flow"} 4 · What the component is actually reporting Three independent Affixes, three independent booleans. Each one fires on its own pin transition and on nothing else, never once per scroll frame. {String(pinned)} ), }, { label: "2 · pane bar", children: ( {String(paneP)} ), }, { label: "3 · form footer", children: ( {String(footerPinned)} ), }, ]} /> ); }