URL에 저장되는 상태
드래그·휠로 보는 구간을 바꾸면 주소창의 쿼리(?view=…)가 따라온다. 새로고침해도 그대로다 — getState/applyState/serializeChartState/parseChartState 네 계약 위다. (갤러리 자체가 해시로 라우팅하므로 여기서는 해시가 아니라 쿼리에 저장한다 — 상태의 자리만 다르고 계약은 README의 예제와 같다.)
소스
apps/examples/src/cases/state-url.ts — CI가 타입체크하는 실물이다.
ts
import { PlotBuilder, browserDeps } from "@finchart/dom";
import type { OHLC } from "@finchart/core";
import { candleSeries, parseChartState, priceFormat, serializeChartState, timeTicks } from "@finchart/core";
import { fixtureCandles } from "./fixture";
import { chartHost } from "./stage";
export const title = "URL에 저장되는 상태";
export const description =
"드래그·휠로 보는 구간을 바꾸면 주소창의 쿼리(?view=…)가 따라온다. 새로고침해도 그대로다 — getState/applyState/serializeChartState/parseChartState 네 계약 위다. " +
"(갤러리 자체가 해시로 라우팅하므로 여기서는 해시가 아니라 쿼리에 저장한다 — 상태의 자리만 다르고 계약은 README의 예제와 같다.)";
const PARAM = "view";
function readFromUrl(): string | null {
return new URLSearchParams(location.search).get(PARAM);
}
function writeToUrl(serialized: string): void {
const url = new URL(location.href);
url.searchParams.set(PARAM, serialized);
history.replaceState(null, "", url);
}
export function mount(container: HTMLElement): () => void {
const status = document.createElement("p");
status.style.cssText = "font-size: 13px; color: #64748b; margin: 0 0 8px";
container.append(status);
const host = chartHost(container, 420);
const plot = PlotBuilder.create<OHLC>(browserDeps({ autoSize: true }))
.setSize(container.clientWidth || 900, 420)
.setAxis({
x: { ticks: timeTicks({ timeZone: "UTC", locale: "ko" }) },
y: { position: "right", format: priceFormat({ compact: true, locale: "ko" }) },
})
.build(host);
plot.mainPane.addSeries({
series: candleSeries(),
data: fixtureCandles(),
name: "가격",
});
// 데이터보다 먼저 와도 되는 것이 applyState의 계약이다 — 여기서는
// addSeries 뒤에 부르지만 순서를 요구하지 않는다는 걸 보여주려는
// 것이지 순서가 중요해서가 아니다.
const saved = readFromUrl();
const restored = saved ? parseChartState(saved) : null;
if (restored) plot.applyState(restored);
status.textContent = restored
? "URL의 상태를 복원했다 — 새로고침해도 유지된다."
: "드래그하거나 휠로 확대해 보라 — URL이 따라온다.";
const unsubscribe = plot.on("stateChange", (state) => {
writeToUrl(serializeChartState(state));
status.textContent = "URL의 상태를 복원했다 — 새로고침해도 유지된다.";
});
return Object.assign(
() => {
unsubscribe();
plot.destroy();
host.remove();
status.remove();
// 케이스를 떠나면 쿼리도 치운다 — 다음 케이스 진입에 유령 상태를
// 안 남긴다(해시 라우팅과 같은 결의 청소).
const url = new URL(location.href);
url.searchParams.delete(PARAM);
history.replaceState(null, "", url);
},
{ requestRender: () => plot.requestRender() },
);
}