"use client";
import { ThemeProvider as BjorkDemoThemeProvider } from "next-themes";
import { useEffect, useMemo, useRef, useState } from "react";
import { OrderBook, createOrderBookFeed, type MyOrder, type OrderBookHandle, type OrderBookLayout, type OrderBookFeed, type OrderBookLevelInfo, type OrderBookSnapshot } from "@/components/ui/bjork-order-book";
import { DepthChart } from "@/components/ui/bjork-order-book-utils/components-bjork-ui-charts-depth-chart";
import { formatFixed } from "@/components/ui/bjork-order-book-utils/components-bjork-ui-charts-_kit-scale";
import { DemoColumn } from "@/components/ui/bjork-order-book-utils/app-charts-_demo-controls";
const TICKS = [0.05, 0.1, 0.25, 0.5, 1];
const PREVIEW = (() => {
const feed = createOrderBookFeed({ seed: 11 });
let last: number | undefined;
for (let i = 0; i < 40; i++) last = feed.step().last ?? last;
return { ...feed.snapshot(), last };
})();
function myOrdersAround(book: OrderBookSnapshot): MyOrder[] {
const b = book.bids[3]?.price;
const a = book.asks[5]?.price;
return [
...(b !== undefined ? [{ id: "o1", side: "bid" as const, price: b, size: 40 }] : []),
...(a !== undefined ? [{ id: "o2", side: "ask" as const, price: a, size: 25 }] : []),
];
}
function LiveBook({
feed,
layout,
rows,
live,
mine,
}: {
feed: OrderBookFeed;
layout: OrderBookLayout;
rows: number;
live: boolean;
mine: boolean;
}) {
const bookRef = useRef<OrderBookHandle>(null);
const [picked, setPicked] = useState<OrderBookLevelInfo | null>(null);
const [depth, setDepth] = useState<OrderBookSnapshot>(() => feed.snapshot());
const myOrders = useMemo(() => (mine ? myOrdersAround(feed.snapshot()) : undefined), [mine, feed]);
// The feed stands in for an exchange socket: one snapshot, then deltas.
useEffect(() => feed.subscribe((msg) => bookRef.current?.apply(msg)), [feed]);
useEffect(() => feed.setPaused(!live), [feed, live]);
// The depth chart reads the same book, twice a second.
useEffect(() => {
if (!live) return;
const id = setInterval(() => {
const snap = bookRef.current?.getSnapshot();
if (snap) setDepth(snap);
}, 500);
return () => clearInterval(id);
}, [live]);
return (
<>
<div className="flex flex-wrap items-start gap-x-8 gap-y-10">
<div className={layout === "side-by-side" ? "w-full" : "w-full min-w-0 max-w-[440px] flex-1 basis-[360px]"}>
<OrderBook
ref={bookRef}
layout={layout}
rows={rows}
tickSizes={TICKS}
priceLabel="Price (USD)"
sizeLabel="Size (KSTL)"
myOrders={myOrders}
onPriceSelect={setPicked}
ariaLabel="KSTL-USD order book"
/>
</div>
{layout !== "side-by-side" && (
<div className="min-w-0 flex-[1.4] basis-[320px]">
<DepthChart bids={depth.bids} asks={depth.asks} height={300} ariaLabel="KSTL-USD depth" />
</div>
)}
</div>
<p aria-live="polite" className="min-h-4 text-center font-mono text-[11px] tabular-nums text-[color:var(--bjork-text-muted)]">
{picked
? `Limit ${picked.side === "ask" ? "buy" : "sell"} ${formatFixed(picked.price, 2)} ยท ${picked.size.toLocaleString("en-US")} on the level`
: "Click a level to fill the order ticket"}
</p>
</>
);
}
function SourceDemo(){
const isPreview = false;
const feed = useMemo(() => createOrderBookFeed({ seed: 7 }), []);
const [layout, setLayout] = useState<OrderBookLayout>("auto");
const [rows, setRows] = useState(8);
const [live, setLive] = useState(true);
const [mine, setMine] = useState(true);
function resetControls() {
setLayout("auto");
setRows(8);
setLive(true);
setMine(true);
}
return <>
{isPreview ? (
<div className="w-[740px]">
<OrderBook
bids={PREVIEW.bids}
asks={PREVIEW.asks}
lastPrice={PREVIEW.last}
layout="side-by-side"
rows={10}
tickSizes={TICKS}
priceLabel="Price (USD)"
sizeLabel="Size (KSTL)"
myOrders={myOrdersAround(PREVIEW)}
showImbalance
ariaLabel="KSTL-USD order book"
/>
</div>
) : (
<DemoColumn width={960}>
<LiveBook feed={feed} layout={layout} rows={rows} live={live} mine={mine} />
</DemoColumn>
)}
</>;
}
export default function BjorkRegistryPreview(){return <BjorkDemoThemeProvider attribute="class" defaultTheme="dark" enableSystem={false} storageKey="bjork-review-demo-theme"><div data-component-demo className="dark flex min-h-screen w-full items-center justify-center bg-[var(--bjork-bg)] p-8 text-[var(--bjork-text)]"><div className="w-full max-w-[850px]"><SourceDemo/></div></div></BjorkDemoThemeProvider>}