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}>
{/* `truncate`, not a wrap: a 71-character identifier with nowhere to break
would otherwise spill 255px past its box at 375px (measured by
check:frame-overflow, which is what caught it). A toolbar label is one
line; the full string stays in the DOM and in the accessible name. */}
絞り込み · {UNBREAKABLE}
{paneP ? "affixed" : "in flow"}
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)}
),
},
]}
/>
);
}