@gunkin/uiify 0.0.3-alpha → 0.0.4-alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/candle-chart/CandleChart.d.ts +10 -1
- package/dist/components/candle-chart/CandleChart.d.ts.map +1 -1
- package/dist/components/candle-chart/CandleChart.js +82 -42
- package/dist/components/candle-chart/CandleChart.js.map +1 -1
- package/dist/components/candle-chart/client/CandleChartClient.d.ts +2 -2
- package/dist/components/candle-chart/client/CandleChartClient.d.ts.map +1 -1
- package/dist/components/candle-chart/client/CandleChartClient.js +103 -14
- package/dist/components/candle-chart/client/CandleChartClient.js.map +1 -1
- package/dist/components/candle-chart/client/index.d.ts +1 -0
- package/dist/components/candle-chart/client/index.d.ts.map +1 -1
- package/dist/components/candle-chart/client/index.js.map +1 -1
- package/dist/components/candle-chart/types.d.ts +39 -0
- package/dist/components/candle-chart/types.d.ts.map +1 -1
- package/dist/components/internal/chart-scrollbar/use-scrollbar-reserve.d.ts +11 -0
- package/dist/components/internal/chart-scrollbar/use-scrollbar-reserve.d.ts.map +1 -0
- package/dist/components/internal/chart-scrollbar/use-scrollbar-reserve.js +21 -0
- package/dist/components/internal/chart-scrollbar/use-scrollbar-reserve.js.map +1 -0
- package/dist/components/internal/live-chart/types.d.ts +36 -0
- package/dist/components/internal/live-chart/types.d.ts.map +1 -0
- package/dist/components/internal/live-chart/types.js +1 -0
- package/dist/components/internal/live-chart/types.js.map +1 -0
- package/dist/components/internal/live-chart/use-live-chart-window.d.ts +18 -0
- package/dist/components/internal/live-chart/use-live-chart-window.d.ts.map +1 -0
- package/dist/components/internal/live-chart/use-live-chart-window.js +75 -0
- package/dist/components/internal/live-chart/use-live-chart-window.js.map +1 -0
- package/dist/components/line-chart/LineChart.d.ts +6 -1
- package/dist/components/line-chart/LineChart.d.ts.map +1 -1
- package/dist/components/line-chart/LineChart.js +82 -48
- package/dist/components/line-chart/LineChart.js.map +1 -1
- package/dist/components/line-chart/client/LineChartClient.d.ts +2 -2
- package/dist/components/line-chart/client/LineChartClient.d.ts.map +1 -1
- package/dist/components/line-chart/client/LineChartClient.js +106 -14
- package/dist/components/line-chart/client/LineChartClient.js.map +1 -1
- package/dist/components/line-chart/client/index.d.ts +1 -1
- package/dist/components/line-chart/client/index.d.ts.map +1 -1
- package/dist/components/line-chart/client/index.js.map +1 -1
- package/dist/components/line-chart/line-chart-geometry.d.ts +7 -0
- package/dist/components/line-chart/line-chart-geometry.d.ts.map +1 -1
- package/dist/components/line-chart/line-chart-geometry.js +14 -2
- package/dist/components/line-chart/line-chart-geometry.js.map +1 -1
- package/dist/components/line-chart/types.d.ts +42 -0
- package/dist/components/line-chart/types.d.ts.map +1 -1
- package/dist/styles/index.css +1 -1
- package/package.json +1 -1
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import type { ElementType, ReactElement } from "react";
|
|
2
|
-
import type { CandleChartProps } from "./types.js";
|
|
2
|
+
import type { CandleChartCandleProps, CandleChartData, CandleChartProps } from "./types.js";
|
|
3
|
+
export declare function getPriceBounds(data: readonly CandleChartData[]): {
|
|
4
|
+
max: number;
|
|
5
|
+
min: number;
|
|
6
|
+
};
|
|
7
|
+
/** One candle glyph with its optional hit target; shared by the static list and virtual rows. */
|
|
8
|
+
export declare function CandleChartCandle({ as: Tag, candle, index, x, slot, min, max, interactive, isActive, isSelected, cursorY, onCandleClick, onCandlePointerLeave, onCandlePointerMove, }: CandleChartCandleProps): ReactElement;
|
|
9
|
+
export declare namespace CandleChartCandle {
|
|
10
|
+
var displayName: string;
|
|
11
|
+
}
|
|
3
12
|
export declare function CandleChart<TAs extends ElementType = "figure">(props: CandleChartProps<TAs>): ReactElement | null;
|
|
4
13
|
export declare namespace CandleChart {
|
|
5
14
|
var displayName: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CandleChart.d.ts","sourceRoot":"","sources":["../../../src/components/candle-chart/CandleChart.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,WAAW,EAGX,YAAY,EAEb,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"CandleChart.d.ts","sourceRoot":"","sources":["../../../src/components/candle-chart/CandleChart.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,WAAW,EAGX,YAAY,EAEb,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAEV,sBAAsB,EACtB,eAAe,EACf,gBAAgB,EAEjB,MAAM,YAAY,CAAC;AAsDpB,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,eAAe,EAAE,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAW7F;AAsHD,iGAAiG;AACjG,wBAAgB,iBAAiB,CAAC,EAChC,EAAE,EAAE,GAAG,EACP,MAAM,EACN,KAAK,EACL,CAAC,EACD,IAAI,EACJ,GAAG,EACH,GAAG,EACH,WAAW,EACX,QAAQ,EACR,UAAU,EACV,OAAO,EACP,aAAa,EACb,oBAAoB,EACpB,mBAAmB,GACpB,EAAE,sBAAsB,GAAG,YAAY,CAiCvC;yBAhDe,iBAAiB;;;AAmDjC,wBAAgB,WAAW,CAAC,GAAG,SAAS,WAAW,GAAG,QAAQ,EAC5D,KAAK,EAAE,gBAAgB,CAAC,GAAG,CAAC,GAC3B,YAAY,GAAG,IAAI,CA0JrB;yBA5Je,WAAW"}
|
|
@@ -63,7 +63,7 @@ function priceLabelsFor(min, max) {
|
|
|
63
63
|
};
|
|
64
64
|
});
|
|
65
65
|
}
|
|
66
|
-
function geometryFor(candle,
|
|
66
|
+
function geometryFor(candle, x, slot, min, max) {
|
|
67
67
|
const range = max - min;
|
|
68
68
|
const open = valueToPercent(candle.open, min, range);
|
|
69
69
|
const close = valueToPercent(candle.close, min, range);
|
|
@@ -73,10 +73,10 @@ function geometryFor(candle, index, count, min, max) {
|
|
|
73
73
|
bodyHeight: percent(Math.abs(close - open)),
|
|
74
74
|
bodyTop: percent(Math.min(open, close)),
|
|
75
75
|
direction: directionFor(candle),
|
|
76
|
-
slot: percent(
|
|
76
|
+
slot: percent(slot),
|
|
77
77
|
wickHeight: percent(Math.max(0, low - high)),
|
|
78
78
|
wickTop: percent(high),
|
|
79
|
-
x: percent(
|
|
79
|
+
x: percent(x)
|
|
80
80
|
};
|
|
81
81
|
}
|
|
82
82
|
function candleStyle(geometry, cursorY) {
|
|
@@ -110,6 +110,54 @@ function axisStyle(axis, position) {
|
|
|
110
110
|
function axisLabelsFor(overrides, fallback) {
|
|
111
111
|
return overrides ?? fallback;
|
|
112
112
|
}
|
|
113
|
+
function CandleChartCandle({
|
|
114
|
+
as: Tag,
|
|
115
|
+
candle,
|
|
116
|
+
index,
|
|
117
|
+
x,
|
|
118
|
+
slot,
|
|
119
|
+
min,
|
|
120
|
+
max,
|
|
121
|
+
interactive,
|
|
122
|
+
isActive,
|
|
123
|
+
isSelected,
|
|
124
|
+
cursorY,
|
|
125
|
+
onCandleClick,
|
|
126
|
+
onCandlePointerLeave,
|
|
127
|
+
onCandlePointerMove
|
|
128
|
+
}) {
|
|
129
|
+
const geometry = geometryFor(candle, x, slot, min, max);
|
|
130
|
+
const handleKeyDown = (event) => {
|
|
131
|
+
if (event.key !== "Enter" && event.key !== " ") return;
|
|
132
|
+
event.preventDefault();
|
|
133
|
+
onCandleClick?.(index);
|
|
134
|
+
};
|
|
135
|
+
return /* @__PURE__ */ jsx(
|
|
136
|
+
Tag,
|
|
137
|
+
{
|
|
138
|
+
"aria-hidden": interactive ? void 0 : true,
|
|
139
|
+
"data-part": "candle",
|
|
140
|
+
"data-direction": geometry.direction,
|
|
141
|
+
"data-crosshair-active": interactive && isActive ? "" : void 0,
|
|
142
|
+
"data-time": String(candle.time),
|
|
143
|
+
style: candleStyle(geometry, isActive ? cursorY : void 0),
|
|
144
|
+
children: interactive ? /* @__PURE__ */ jsx(
|
|
145
|
+
"button",
|
|
146
|
+
{
|
|
147
|
+
"data-part": "candle-hit",
|
|
148
|
+
type: "button",
|
|
149
|
+
"aria-label": `Candle ${String(candle.time)}`,
|
|
150
|
+
"aria-pressed": isSelected,
|
|
151
|
+
onClick: () => onCandleClick?.(index),
|
|
152
|
+
onKeyDown: handleKeyDown,
|
|
153
|
+
onPointerLeave: onCandlePointerLeave,
|
|
154
|
+
onPointerMove: (event) => onCandlePointerMove?.({ index, offsetY: event.nativeEvent.offsetY })
|
|
155
|
+
}
|
|
156
|
+
) : null
|
|
157
|
+
}
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
CandleChartCandle.displayName = "CandleChartCandle";
|
|
113
161
|
function CandleChart(props) {
|
|
114
162
|
const {
|
|
115
163
|
as,
|
|
@@ -125,6 +173,10 @@ function CandleChart(props) {
|
|
|
125
173
|
onCandlePointerLeave,
|
|
126
174
|
onCandlePointerMove,
|
|
127
175
|
selectedCandleIndex,
|
|
176
|
+
candleLayer,
|
|
177
|
+
announceReadout = true,
|
|
178
|
+
slotCount = data.length,
|
|
179
|
+
slotOffset = 0,
|
|
128
180
|
...consumerProps
|
|
129
181
|
} = props;
|
|
130
182
|
const { min, max } = getPriceBounds(data);
|
|
@@ -134,40 +186,26 @@ function CandleChart(props) {
|
|
|
134
186
|
const readoutIndex = activeCandleIndex ?? selectedCandleIndex ?? (data.length > 0 ? data.length - 1 : void 0);
|
|
135
187
|
const readoutCandle = readoutIndex === void 0 ? void 0 : data[readoutIndex];
|
|
136
188
|
const crosshairCandle = activeCandleIndex === void 0 ? void 0 : data[activeCandleIndex];
|
|
137
|
-
const candles = data.map((candle, index) =>
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
"data-part": "candle-hit",
|
|
158
|
-
type: "button",
|
|
159
|
-
"aria-label": `Candle ${String(candle.time)}`,
|
|
160
|
-
"aria-pressed": isSelected,
|
|
161
|
-
onClick: () => onCandleClick?.(index),
|
|
162
|
-
onKeyDown: handleKeyDown,
|
|
163
|
-
onPointerLeave: onCandlePointerLeave,
|
|
164
|
-
onPointerMove: (event) => onCandlePointerMove?.({ index, offsetY: event.nativeEvent.offsetY })
|
|
165
|
-
}
|
|
166
|
-
) : null
|
|
167
|
-
},
|
|
168
|
-
`${candle.time}-${index}`
|
|
169
|
-
);
|
|
170
|
-
});
|
|
189
|
+
const candles = candleLayer ?? /* @__PURE__ */ jsx("ol", { "data-part": "candles", "aria-label": `${label} candles`, children: data.map((candle, index) => /* @__PURE__ */ jsx(
|
|
190
|
+
CandleChartCandle,
|
|
191
|
+
{
|
|
192
|
+
as: "li",
|
|
193
|
+
candle,
|
|
194
|
+
index,
|
|
195
|
+
x: index / data.length * 100,
|
|
196
|
+
slot: 100 / data.length,
|
|
197
|
+
min,
|
|
198
|
+
max,
|
|
199
|
+
interactive,
|
|
200
|
+
isActive: activeCandleIndex === index,
|
|
201
|
+
isSelected: selectedCandleIndex === index,
|
|
202
|
+
cursorY,
|
|
203
|
+
onCandleClick,
|
|
204
|
+
onCandlePointerLeave,
|
|
205
|
+
onCandlePointerMove
|
|
206
|
+
},
|
|
207
|
+
`${candle.time}-${index}`
|
|
208
|
+
)) });
|
|
171
209
|
return useRenderElement({
|
|
172
210
|
as,
|
|
173
211
|
defaultTag: "figure",
|
|
@@ -178,7 +216,7 @@ function CandleChart(props) {
|
|
|
178
216
|
"data-interactive": interactive ? "" : void 0,
|
|
179
217
|
children: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
180
218
|
/* @__PURE__ */ jsx("figcaption", { children: label }),
|
|
181
|
-
interactive && readoutCandle ? /* @__PURE__ */ jsxs("div", { "data-part": "readout", "aria-live": "polite", children: [
|
|
219
|
+
interactive && readoutCandle ? /* @__PURE__ */ jsxs("div", { "data-part": "readout", "aria-live": announceReadout ? "polite" : "off", children: [
|
|
182
220
|
/* @__PURE__ */ jsx("span", { "data-part": "readout-time", children: String(readoutCandle.time) }),
|
|
183
221
|
/* @__PURE__ */ jsxs("span", { children: [
|
|
184
222
|
"O ",
|
|
@@ -200,13 +238,13 @@ function CandleChart(props) {
|
|
|
200
238
|
/* @__PURE__ */ jsxs("div", { "data-part": "plot", "aria-hidden": interactive ? void 0 : true, children: [
|
|
201
239
|
/* @__PURE__ */ jsx("div", { "data-part": "scroll-viewport", children: /* @__PURE__ */ jsxs("div", { "data-part": "canvas", children: [
|
|
202
240
|
/* @__PURE__ */ jsxs("div", { "data-part": "viewport", children: [
|
|
203
|
-
|
|
241
|
+
candles,
|
|
204
242
|
interactive && crosshairCandle && activeCandleIndex !== void 0 ? /* @__PURE__ */ jsx(
|
|
205
243
|
"div",
|
|
206
244
|
{
|
|
207
245
|
"data-part": "crosshair",
|
|
208
246
|
"aria-hidden": "true",
|
|
209
|
-
style: crosshairStyle(activeCandleIndex,
|
|
247
|
+
style: crosshairStyle(slotOffset + activeCandleIndex, slotCount, cursorY),
|
|
210
248
|
children: /* @__PURE__ */ jsx("span", { "data-part": "crosshair-marker", children: "+" })
|
|
211
249
|
}
|
|
212
250
|
) : null
|
|
@@ -224,7 +262,7 @@ function CandleChart(props) {
|
|
|
224
262
|
"li",
|
|
225
263
|
{
|
|
226
264
|
"data-part": "crosshair-time",
|
|
227
|
-
style: crosshairStyle(activeCandleIndex,
|
|
265
|
+
style: crosshairStyle(slotOffset + activeCandleIndex, slotCount, void 0),
|
|
228
266
|
children: String(crosshairCandle.time)
|
|
229
267
|
}
|
|
230
268
|
) : null
|
|
@@ -266,6 +304,8 @@ function CandleChart(props) {
|
|
|
266
304
|
}
|
|
267
305
|
CandleChart.displayName = "CandleChart";
|
|
268
306
|
export {
|
|
269
|
-
CandleChart
|
|
307
|
+
CandleChart,
|
|
308
|
+
CandleChartCandle,
|
|
309
|
+
getPriceBounds
|
|
270
310
|
};
|
|
271
311
|
//# sourceMappingURL=CandleChart.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/candle-chart/CandleChart.tsx"],"sourcesContent":["import type {\n CSSProperties,\n ElementType,\n KeyboardEvent,\n PointerEvent,\n ReactElement,\n ReactNode,\n} from \"react\";\nimport { useRenderElement } from \"@gunkin/uiify/core/render\";\nimport type { CandleChartAxisLabel, CandleChartData, CandleChartProps } from \"./types.js\";\n\ninterface CandleStyle extends CSSProperties {\n \"--candle-body-height\": string;\n \"--candle-body-top\": string;\n \"--candle-slot\": string;\n \"--candle-wick-height\": string;\n \"--candle-wick-top\": string;\n \"--candle-x\": string;\n \"--candle-cursor-y\"?: string;\n}\n\ninterface CrosshairStyle extends CSSProperties {\n \"--candle-crosshair-x\": string;\n \"--candle-cursor-y\"?: string;\n}\n\ninterface CursorStyle extends CSSProperties {\n \"--candle-cursor-y\"?: string;\n}\n\ninterface AxisStyle extends CSSProperties {\n \"--candle-axis-x\"?: string;\n \"--candle-axis-y\"?: string;\n}\n\ntype CandleDirection = \"down\" | \"flat\" | \"up\";\n\ninterface CandleGeometry {\n readonly bodyHeight: string;\n readonly bodyTop: string;\n readonly direction: CandleDirection;\n readonly slot: string;\n readonly wickHeight: string;\n readonly wickTop: string;\n readonly x: string;\n}\n\nfunction percent(value: number): string {\n const rounded = Math.round(value * 10000) / 10000;\n return `${rounded}%`;\n}\n\nfunction valueToPercent(value: number, min: number, range: number): number {\n if (range === 0) return 50;\n return Math.min(100, Math.max(0, ((min + range - value) / range) * 100));\n}\n\nfunction directionFor(candle: CandleChartData): CandleDirection {\n if (candle.close > candle.open) return \"up\";\n if (candle.close < candle.open) return \"down\";\n return \"flat\";\n}\n\nfunction getPriceBounds(data: readonly CandleChartData[]): { max: number; min: number } {\n let min = Number.POSITIVE_INFINITY;\n let max = Number.NEGATIVE_INFINITY;\n\n for (const candle of data) {\n min = Math.min(min, candle.low);\n max = Math.max(max, candle.high);\n }\n\n if (data.length === 0) return { max: 1, min: 0 };\n return { max, min };\n}\n\ninterface AxisLabel {\n readonly label: ReactNode;\n readonly position: number;\n}\n\nfunction monthKey(time: string | number): string {\n const date = new Date(time);\n if (Number.isNaN(date.getTime())) return String(time);\n return `${date.getUTCFullYear()}-${date.getUTCMonth()}`;\n}\n\nfunction monthLabel(time: string | number): string {\n const date = new Date(time);\n if (Number.isNaN(date.getTime())) return String(time);\n return new Intl.DateTimeFormat(\"en-US\", {\n month: \"short\",\n timeZone: \"UTC\",\n year: \"2-digit\",\n }).format(date);\n}\n\nfunction monthLabelsFor(data: readonly CandleChartData[]): AxisLabel[] {\n const labels: AxisLabel[] = [];\n const seen = new Set<string>();\n\n data.forEach((candle, index) => {\n const key = monthKey(candle.time);\n if (seen.has(key)) return;\n seen.add(key);\n labels.push({\n label: monthLabel(candle.time),\n position: (index / Math.max(data.length, 1)) * 100,\n });\n });\n\n return labels;\n}\n\nfunction priceLabelsFor(min: number, max: number): AxisLabel[] {\n const tickCount = 6;\n const range = max - min;\n\n return Array.from({ length: tickCount }, (_, index) => {\n const ratio = index / (tickCount - 1);\n const value = max - range * ratio;\n return {\n label: new Intl.NumberFormat(\"en-US\", { maximumFractionDigits: 2 }).format(value),\n position: ratio * 100,\n };\n });\n}\n\nfunction geometryFor(\n candle: CandleChartData,\n index: number,\n count: number,\n min: number,\n max: number,\n): CandleGeometry {\n const range = max - min;\n const open = valueToPercent(candle.open, min, range);\n const close = valueToPercent(candle.close, min, range);\n const high = valueToPercent(candle.high, min, range);\n const low = valueToPercent(candle.low, min, range);\n\n return {\n bodyHeight: percent(Math.abs(close - open)),\n bodyTop: percent(Math.min(open, close)),\n direction: directionFor(candle),\n slot: percent(100 / count),\n wickHeight: percent(Math.max(0, low - high)),\n wickTop: percent(high),\n x: percent((index / count) * 100),\n };\n}\n\nfunction candleStyle(geometry: CandleGeometry, cursorY: number | undefined): CandleStyle {\n return {\n \"--candle-body-height\": geometry.bodyHeight,\n \"--candle-body-top\": geometry.bodyTop,\n \"--candle-slot\": geometry.slot,\n \"--candle-wick-height\": geometry.wickHeight,\n \"--candle-wick-top\": geometry.wickTop,\n \"--candle-x\": geometry.x,\n ...(cursorY === undefined ? {} : { \"--candle-cursor-y\": `${Math.max(0, cursorY)}px` }),\n };\n}\n\nfunction crosshairStyle(index: number, count: number, cursorY: number | undefined): CrosshairStyle {\n return {\n \"--candle-crosshair-x\": percent(((index + 0.5) / Math.max(count, 1)) * 100),\n ...(cursorY === undefined ? {} : { \"--candle-cursor-y\": `${Math.max(0, cursorY)}px` }),\n };\n}\n\nfunction cursorStyle(cursorY: number | undefined): CursorStyle {\n return cursorY === undefined ? {} : { \"--candle-cursor-y\": `${Math.max(0, cursorY)}px` };\n}\n\nfunction clampAxisPosition(position: number): number {\n return Math.min(100, Math.max(0, position));\n}\n\nfunction axisStyle(axis: \"x\" | \"y\", position: number): AxisStyle {\n const value = percent(clampAxisPosition(position));\n if (axis === \"x\") return { \"--candle-axis-x\": value };\n return { \"--candle-axis-y\": value };\n}\n\nfunction axisLabelsFor(\n overrides: readonly CandleChartAxisLabel[] | undefined,\n fallback: readonly AxisLabel[],\n): readonly AxisLabel[] {\n return overrides ?? fallback;\n}\n\nexport function CandleChart<TAs extends ElementType = \"figure\">(\n props: CandleChartProps<TAs>,\n): ReactElement | null {\n const {\n as,\n render,\n data,\n xAxis,\n yAxis,\n \"aria-label\": ariaLabel,\n activeCandleIndex,\n cursorY,\n interactive = false,\n onCandleClick,\n onCandlePointerLeave,\n onCandlePointerMove,\n selectedCandleIndex,\n ...consumerProps\n } = props as CandleChartProps<\"figure\">;\n const { min, max } = getPriceBounds(data);\n const label = ariaLabel ?? \"Candlestick chart\";\n const monthLabels = axisLabelsFor(xAxis, monthLabelsFor(data));\n const priceLabels = axisLabelsFor(yAxis, priceLabelsFor(min, max));\n const readoutIndex =\n activeCandleIndex ?? selectedCandleIndex ?? (data.length > 0 ? data.length - 1 : undefined);\n const readoutCandle = readoutIndex === undefined ? undefined : data[readoutIndex];\n const crosshairCandle = activeCandleIndex === undefined ? undefined : data[activeCandleIndex];\n const candles = data.map((candle, index) => {\n const geometry = geometryFor(candle, index, data.length, min, max);\n const isActive = activeCandleIndex === index;\n const isSelected = selectedCandleIndex === index;\n const handleKeyDown = (event: KeyboardEvent<HTMLButtonElement>) => {\n if (event.key !== \"Enter\" && event.key !== \" \") return;\n event.preventDefault();\n onCandleClick?.(index);\n };\n\n return (\n <li\n key={`${candle.time}-${index}`}\n aria-hidden={interactive ? undefined : true}\n data-direction={geometry.direction}\n data-crosshair-active={interactive && isActive ? \"\" : undefined}\n data-time={String(candle.time)}\n style={candleStyle(geometry, isActive ? cursorY : undefined)}\n >\n {interactive ? (\n <button\n data-part=\"candle-hit\"\n type=\"button\"\n aria-label={`Candle ${String(candle.time)}`}\n aria-pressed={isSelected}\n onClick={() => onCandleClick?.(index)}\n onKeyDown={handleKeyDown}\n onPointerLeave={onCandlePointerLeave}\n onPointerMove={(event: PointerEvent<HTMLButtonElement>) =>\n onCandlePointerMove?.({ index, offsetY: event.nativeEvent.offsetY })\n }\n />\n ) : null}\n </li>\n );\n });\n\n return useRenderElement({\n as,\n defaultTag: \"figure\",\n props: {\n ...consumerProps,\n \"aria-label\": label,\n \"data-uiify-candle-chart\": \"\",\n \"data-interactive\": interactive ? \"\" : undefined,\n children: (\n <>\n <figcaption>{label}</figcaption>\n {interactive && readoutCandle ? (\n <div data-part=\"readout\" aria-live=\"polite\">\n <span data-part=\"readout-time\">{String(readoutCandle.time)}</span>\n <span>O {readoutCandle.open}</span>\n <span>H {readoutCandle.high}</span>\n <span>L {readoutCandle.low}</span>\n <span>C {readoutCandle.close}</span>\n </div>\n ) : null}\n <div data-part=\"plot\" aria-hidden={interactive ? undefined : true}>\n <div data-part=\"scroll-viewport\">\n <div data-part=\"canvas\">\n <div data-part=\"viewport\">\n <ol data-part=\"candles\" aria-label={`${label} candles`}>\n {candles}\n </ol>\n {interactive && crosshairCandle && activeCandleIndex !== undefined ? (\n <div\n data-part=\"crosshair\"\n aria-hidden=\"true\"\n style={crosshairStyle(activeCandleIndex, data.length, cursorY)}\n >\n <span data-part=\"crosshair-marker\">+</span>\n </div>\n ) : null}\n </div>\n <div data-part=\"time-viewport\">\n <ol data-part=\"time-axis\" data-axis=\"time\" aria-label={`${label} months`}>\n {monthLabels.map((axisLabel, index) => (\n <li\n key={`${axisLabel.position}-${index}`}\n style={axisStyle(\"x\", axisLabel.position)}\n >\n {axisLabel.label}\n </li>\n ))}\n {interactive && crosshairCandle && activeCandleIndex !== undefined ? (\n <li\n data-part=\"crosshair-time\"\n style={crosshairStyle(activeCandleIndex, data.length, undefined)}\n >\n {String(crosshairCandle.time)}\n </li>\n ) : null}\n </ol>\n </div>\n </div>\n </div>\n <ol data-part=\"price-axis\" data-axis=\"price\" aria-label={`${label} prices`}>\n {priceLabels.map((axisLabel, index) => (\n <li\n key={`${axisLabel.position}-${index}`}\n style={axisStyle(\"y\", axisLabel.position)}\n >\n {axisLabel.label}\n </li>\n ))}\n {interactive && crosshairCandle && activeCandleIndex !== undefined ? (\n <li data-part=\"crosshair-price\" style={cursorStyle(cursorY)}>\n {crosshairCandle.close}\n </li>\n ) : null}\n </ol>\n </div>\n <table data-part=\"data\" aria-label={`${label} data`}>\n <thead>\n <tr>\n <th scope=\"col\">Time</th>\n <th scope=\"col\">Open</th>\n <th scope=\"col\">High</th>\n <th scope=\"col\">Low</th>\n <th scope=\"col\">Close</th>\n </tr>\n </thead>\n <tbody>\n {data.map((candle, index) => (\n <tr key={`${candle.time}-${index}`}>\n <th scope=\"row\">{String(candle.time)}</th>\n <td>{candle.open}</td>\n <td>{candle.high}</td>\n <td>{candle.low}</td>\n <td>{candle.close}</td>\n </tr>\n ))}\n </tbody>\n </table>\n </>\n ),\n },\n render,\n state: {},\n });\n}\nCandleChart.displayName = \"CandleChart\";\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/candle-chart/CandleChart.tsx"],"sourcesContent":["import type {\n CSSProperties,\n ElementType,\n KeyboardEvent,\n PointerEvent,\n ReactElement,\n ReactNode,\n} from \"react\";\nimport { useRenderElement } from \"@gunkin/uiify/core/render\";\nimport type {\n CandleChartAxisLabel,\n CandleChartCandleProps,\n CandleChartData,\n CandleChartProps,\n CandleChartViewProps,\n} from \"./types.js\";\n\ninterface CandleStyle extends CSSProperties {\n \"--candle-body-height\": string;\n \"--candle-body-top\": string;\n \"--candle-slot\": string;\n \"--candle-wick-height\": string;\n \"--candle-wick-top\": string;\n \"--candle-x\": string;\n \"--candle-cursor-y\"?: string;\n}\n\ninterface CrosshairStyle extends CSSProperties {\n \"--candle-crosshair-x\": string;\n \"--candle-cursor-y\"?: string;\n}\n\ninterface CursorStyle extends CSSProperties {\n \"--candle-cursor-y\"?: string;\n}\n\ninterface AxisStyle extends CSSProperties {\n \"--candle-axis-x\"?: string;\n \"--candle-axis-y\"?: string;\n}\n\ntype CandleDirection = \"down\" | \"flat\" | \"up\";\n\ninterface CandleGeometry {\n readonly bodyHeight: string;\n readonly bodyTop: string;\n readonly direction: CandleDirection;\n readonly slot: string;\n readonly wickHeight: string;\n readonly wickTop: string;\n readonly x: string;\n}\n\nfunction percent(value: number): string {\n const rounded = Math.round(value * 10000) / 10000;\n return `${rounded}%`;\n}\n\nfunction valueToPercent(value: number, min: number, range: number): number {\n if (range === 0) return 50;\n return Math.min(100, Math.max(0, ((min + range - value) / range) * 100));\n}\n\nfunction directionFor(candle: CandleChartData): CandleDirection {\n if (candle.close > candle.open) return \"up\";\n if (candle.close < candle.open) return \"down\";\n return \"flat\";\n}\n\nexport function getPriceBounds(data: readonly CandleChartData[]): { max: number; min: number } {\n let min = Number.POSITIVE_INFINITY;\n let max = Number.NEGATIVE_INFINITY;\n\n for (const candle of data) {\n min = Math.min(min, candle.low);\n max = Math.max(max, candle.high);\n }\n\n if (data.length === 0) return { max: 1, min: 0 };\n return { max, min };\n}\n\ninterface AxisLabel {\n readonly label: ReactNode;\n readonly position: number;\n}\n\nfunction monthKey(time: string | number): string {\n const date = new Date(time);\n if (Number.isNaN(date.getTime())) return String(time);\n return `${date.getUTCFullYear()}-${date.getUTCMonth()}`;\n}\n\nfunction monthLabel(time: string | number): string {\n const date = new Date(time);\n if (Number.isNaN(date.getTime())) return String(time);\n return new Intl.DateTimeFormat(\"en-US\", {\n month: \"short\",\n timeZone: \"UTC\",\n year: \"2-digit\",\n }).format(date);\n}\n\nfunction monthLabelsFor(data: readonly CandleChartData[]): AxisLabel[] {\n const labels: AxisLabel[] = [];\n const seen = new Set<string>();\n\n data.forEach((candle, index) => {\n const key = monthKey(candle.time);\n if (seen.has(key)) return;\n seen.add(key);\n labels.push({\n label: monthLabel(candle.time),\n position: (index / Math.max(data.length, 1)) * 100,\n });\n });\n\n return labels;\n}\n\nfunction priceLabelsFor(min: number, max: number): AxisLabel[] {\n const tickCount = 6;\n const range = max - min;\n\n return Array.from({ length: tickCount }, (_, index) => {\n const ratio = index / (tickCount - 1);\n const value = max - range * ratio;\n return {\n label: new Intl.NumberFormat(\"en-US\", { maximumFractionDigits: 2 }).format(value),\n position: ratio * 100,\n };\n });\n}\n\nfunction geometryFor(\n candle: CandleChartData,\n x: number,\n slot: number,\n min: number,\n max: number,\n): CandleGeometry {\n const range = max - min;\n const open = valueToPercent(candle.open, min, range);\n const close = valueToPercent(candle.close, min, range);\n const high = valueToPercent(candle.high, min, range);\n const low = valueToPercent(candle.low, min, range);\n\n return {\n bodyHeight: percent(Math.abs(close - open)),\n bodyTop: percent(Math.min(open, close)),\n direction: directionFor(candle),\n slot: percent(slot),\n wickHeight: percent(Math.max(0, low - high)),\n wickTop: percent(high),\n x: percent(x),\n };\n}\n\nfunction candleStyle(geometry: CandleGeometry, cursorY: number | undefined): CandleStyle {\n return {\n \"--candle-body-height\": geometry.bodyHeight,\n \"--candle-body-top\": geometry.bodyTop,\n \"--candle-slot\": geometry.slot,\n \"--candle-wick-height\": geometry.wickHeight,\n \"--candle-wick-top\": geometry.wickTop,\n \"--candle-x\": geometry.x,\n ...(cursorY === undefined ? {} : { \"--candle-cursor-y\": `${Math.max(0, cursorY)}px` }),\n };\n}\n\nfunction crosshairStyle(index: number, count: number, cursorY: number | undefined): CrosshairStyle {\n return {\n \"--candle-crosshair-x\": percent(((index + 0.5) / Math.max(count, 1)) * 100),\n ...(cursorY === undefined ? {} : { \"--candle-cursor-y\": `${Math.max(0, cursorY)}px` }),\n };\n}\n\nfunction cursorStyle(cursorY: number | undefined): CursorStyle {\n return cursorY === undefined ? {} : { \"--candle-cursor-y\": `${Math.max(0, cursorY)}px` };\n}\n\nfunction clampAxisPosition(position: number): number {\n return Math.min(100, Math.max(0, position));\n}\n\nfunction axisStyle(axis: \"x\" | \"y\", position: number): AxisStyle {\n const value = percent(clampAxisPosition(position));\n if (axis === \"x\") return { \"--candle-axis-x\": value };\n return { \"--candle-axis-y\": value };\n}\n\nfunction axisLabelsFor(\n overrides: readonly CandleChartAxisLabel[] | undefined,\n fallback: readonly AxisLabel[],\n): readonly AxisLabel[] {\n return overrides ?? fallback;\n}\n\n/** One candle glyph with its optional hit target; shared by the static list and virtual rows. */\nexport function CandleChartCandle({\n as: Tag,\n candle,\n index,\n x,\n slot,\n min,\n max,\n interactive,\n isActive,\n isSelected,\n cursorY,\n onCandleClick,\n onCandlePointerLeave,\n onCandlePointerMove,\n}: CandleChartCandleProps): ReactElement {\n const geometry = geometryFor(candle, x, slot, min, max);\n const handleKeyDown = (event: KeyboardEvent<HTMLButtonElement>) => {\n if (event.key !== \"Enter\" && event.key !== \" \") return;\n event.preventDefault();\n onCandleClick?.(index);\n };\n\n return (\n <Tag\n aria-hidden={interactive ? undefined : true}\n data-part=\"candle\"\n data-direction={geometry.direction}\n data-crosshair-active={interactive && isActive ? \"\" : undefined}\n data-time={String(candle.time)}\n style={candleStyle(geometry, isActive ? cursorY : undefined)}\n >\n {interactive ? (\n <button\n data-part=\"candle-hit\"\n type=\"button\"\n aria-label={`Candle ${String(candle.time)}`}\n aria-pressed={isSelected}\n onClick={() => onCandleClick?.(index)}\n onKeyDown={handleKeyDown}\n onPointerLeave={onCandlePointerLeave}\n onPointerMove={(event: PointerEvent<HTMLButtonElement>) =>\n onCandlePointerMove?.({ index, offsetY: event.nativeEvent.offsetY })\n }\n />\n ) : null}\n </Tag>\n );\n}\nCandleChartCandle.displayName = \"CandleChartCandle\";\n\nexport function CandleChart<TAs extends ElementType = \"figure\">(\n props: CandleChartProps<TAs>,\n): ReactElement | null {\n const {\n as,\n render,\n data,\n xAxis,\n yAxis,\n \"aria-label\": ariaLabel,\n activeCandleIndex,\n cursorY,\n interactive = false,\n onCandleClick,\n onCandlePointerLeave,\n onCandlePointerMove,\n selectedCandleIndex,\n candleLayer,\n announceReadout = true,\n slotCount = data.length,\n slotOffset = 0,\n ...consumerProps\n } = props as CandleChartProps<\"figure\"> & CandleChartViewProps;\n const { min, max } = getPriceBounds(data);\n const label = ariaLabel ?? \"Candlestick chart\";\n const monthLabels = axisLabelsFor(xAxis, monthLabelsFor(data));\n const priceLabels = axisLabelsFor(yAxis, priceLabelsFor(min, max));\n const readoutIndex =\n activeCandleIndex ?? selectedCandleIndex ?? (data.length > 0 ? data.length - 1 : undefined);\n const readoutCandle = readoutIndex === undefined ? undefined : data[readoutIndex];\n const crosshairCandle = activeCandleIndex === undefined ? undefined : data[activeCandleIndex];\n const candles = candleLayer ?? (\n <ol data-part=\"candles\" aria-label={`${label} candles`}>\n {data.map((candle, index) => (\n <CandleChartCandle\n key={`${candle.time}-${index}`}\n as=\"li\"\n candle={candle}\n index={index}\n x={(index / data.length) * 100}\n slot={100 / data.length}\n min={min}\n max={max}\n interactive={interactive}\n isActive={activeCandleIndex === index}\n isSelected={selectedCandleIndex === index}\n cursorY={cursorY}\n onCandleClick={onCandleClick}\n onCandlePointerLeave={onCandlePointerLeave}\n onCandlePointerMove={onCandlePointerMove}\n />\n ))}\n </ol>\n );\n\n return useRenderElement({\n as,\n defaultTag: \"figure\",\n props: {\n ...consumerProps,\n \"aria-label\": label,\n \"data-uiify-candle-chart\": \"\",\n \"data-interactive\": interactive ? \"\" : undefined,\n children: (\n <>\n <figcaption>{label}</figcaption>\n {interactive && readoutCandle ? (\n <div data-part=\"readout\" aria-live={announceReadout ? \"polite\" : \"off\"}>\n <span data-part=\"readout-time\">{String(readoutCandle.time)}</span>\n <span>O {readoutCandle.open}</span>\n <span>H {readoutCandle.high}</span>\n <span>L {readoutCandle.low}</span>\n <span>C {readoutCandle.close}</span>\n </div>\n ) : null}\n <div data-part=\"plot\" aria-hidden={interactive ? undefined : true}>\n <div data-part=\"scroll-viewport\">\n <div data-part=\"canvas\">\n <div data-part=\"viewport\">\n {candles}\n {interactive && crosshairCandle && activeCandleIndex !== undefined ? (\n <div\n data-part=\"crosshair\"\n aria-hidden=\"true\"\n style={crosshairStyle(slotOffset + activeCandleIndex, slotCount, cursorY)}\n >\n <span data-part=\"crosshair-marker\">+</span>\n </div>\n ) : null}\n </div>\n <div data-part=\"time-viewport\">\n <ol data-part=\"time-axis\" data-axis=\"time\" aria-label={`${label} months`}>\n {monthLabels.map((axisLabel, index) => (\n <li\n key={`${axisLabel.position}-${index}`}\n style={axisStyle(\"x\", axisLabel.position)}\n >\n {axisLabel.label}\n </li>\n ))}\n {interactive && crosshairCandle && activeCandleIndex !== undefined ? (\n <li\n data-part=\"crosshair-time\"\n style={crosshairStyle(slotOffset + activeCandleIndex, slotCount, undefined)}\n >\n {String(crosshairCandle.time)}\n </li>\n ) : null}\n </ol>\n </div>\n </div>\n </div>\n <ol data-part=\"price-axis\" data-axis=\"price\" aria-label={`${label} prices`}>\n {priceLabels.map((axisLabel, index) => (\n <li\n key={`${axisLabel.position}-${index}`}\n style={axisStyle(\"y\", axisLabel.position)}\n >\n {axisLabel.label}\n </li>\n ))}\n {interactive && crosshairCandle && activeCandleIndex !== undefined ? (\n <li data-part=\"crosshair-price\" style={cursorStyle(cursorY)}>\n {crosshairCandle.close}\n </li>\n ) : null}\n </ol>\n </div>\n <table data-part=\"data\" aria-label={`${label} data`}>\n <thead>\n <tr>\n <th scope=\"col\">Time</th>\n <th scope=\"col\">Open</th>\n <th scope=\"col\">High</th>\n <th scope=\"col\">Low</th>\n <th scope=\"col\">Close</th>\n </tr>\n </thead>\n <tbody>\n {data.map((candle, index) => (\n <tr key={`${candle.time}-${index}`}>\n <th scope=\"row\">{String(candle.time)}</th>\n <td>{candle.open}</td>\n <td>{candle.high}</td>\n <td>{candle.low}</td>\n <td>{candle.close}</td>\n </tr>\n ))}\n </tbody>\n </table>\n </>\n ),\n },\n render,\n state: {},\n });\n}\nCandleChart.displayName = \"CandleChart\";\n"],"mappings":"AAwOQ,SAkFA,UAlFA,KAuFM,YAvFN;AAhOR,SAAS,wBAAwB;AA6CjC,SAAS,QAAQ,OAAuB;AACtC,QAAM,UAAU,KAAK,MAAM,QAAQ,GAAK,IAAI;AAC5C,SAAO,GAAG,OAAO;AACnB;AAEA,SAAS,eAAe,OAAe,KAAa,OAAuB;AACzE,MAAI,UAAU,EAAG,QAAO;AACxB,SAAO,KAAK,IAAI,KAAK,KAAK,IAAI,IAAK,MAAM,QAAQ,SAAS,QAAS,GAAG,CAAC;AACzE;AAEA,SAAS,aAAa,QAA0C;AAC9D,MAAI,OAAO,QAAQ,OAAO,KAAM,QAAO;AACvC,MAAI,OAAO,QAAQ,OAAO,KAAM,QAAO;AACvC,SAAO;AACT;AAEO,SAAS,eAAe,MAAgE;AAC7F,MAAI,MAAM,OAAO;AACjB,MAAI,MAAM,OAAO;AAEjB,aAAW,UAAU,MAAM;AACzB,UAAM,KAAK,IAAI,KAAK,OAAO,GAAG;AAC9B,UAAM,KAAK,IAAI,KAAK,OAAO,IAAI;AAAA,EACjC;AAEA,MAAI,KAAK,WAAW,EAAG,QAAO,EAAE,KAAK,GAAG,KAAK,EAAE;AAC/C,SAAO,EAAE,KAAK,IAAI;AACpB;AAOA,SAAS,SAAS,MAA+B;AAC/C,QAAM,OAAO,IAAI,KAAK,IAAI;AAC1B,MAAI,OAAO,MAAM,KAAK,QAAQ,CAAC,EAAG,QAAO,OAAO,IAAI;AACpD,SAAO,GAAG,KAAK,eAAe,CAAC,IAAI,KAAK,YAAY,CAAC;AACvD;AAEA,SAAS,WAAW,MAA+B;AACjD,QAAM,OAAO,IAAI,KAAK,IAAI;AAC1B,MAAI,OAAO,MAAM,KAAK,QAAQ,CAAC,EAAG,QAAO,OAAO,IAAI;AACpD,SAAO,IAAI,KAAK,eAAe,SAAS;AAAA,IACtC,OAAO;AAAA,IACP,UAAU;AAAA,IACV,MAAM;AAAA,EACR,CAAC,EAAE,OAAO,IAAI;AAChB;AAEA,SAAS,eAAe,MAA+C;AACrE,QAAM,SAAsB,CAAC;AAC7B,QAAM,OAAO,oBAAI,IAAY;AAE7B,OAAK,QAAQ,CAAC,QAAQ,UAAU;AAC9B,UAAM,MAAM,SAAS,OAAO,IAAI;AAChC,QAAI,KAAK,IAAI,GAAG,EAAG;AACnB,SAAK,IAAI,GAAG;AACZ,WAAO,KAAK;AAAA,MACV,OAAO,WAAW,OAAO,IAAI;AAAA,MAC7B,UAAW,QAAQ,KAAK,IAAI,KAAK,QAAQ,CAAC,IAAK;AAAA,IACjD,CAAC;AAAA,EACH,CAAC;AAED,SAAO;AACT;AAEA,SAAS,eAAe,KAAa,KAA0B;AAC7D,QAAM,YAAY;AAClB,QAAM,QAAQ,MAAM;AAEpB,SAAO,MAAM,KAAK,EAAE,QAAQ,UAAU,GAAG,CAAC,GAAG,UAAU;AACrD,UAAM,QAAQ,SAAS,YAAY;AACnC,UAAM,QAAQ,MAAM,QAAQ;AAC5B,WAAO;AAAA,MACL,OAAO,IAAI,KAAK,aAAa,SAAS,EAAE,uBAAuB,EAAE,CAAC,EAAE,OAAO,KAAK;AAAA,MAChF,UAAU,QAAQ;AAAA,IACpB;AAAA,EACF,CAAC;AACH;AAEA,SAAS,YACP,QACA,GACA,MACA,KACA,KACgB;AAChB,QAAM,QAAQ,MAAM;AACpB,QAAM,OAAO,eAAe,OAAO,MAAM,KAAK,KAAK;AACnD,QAAM,QAAQ,eAAe,OAAO,OAAO,KAAK,KAAK;AACrD,QAAM,OAAO,eAAe,OAAO,MAAM,KAAK,KAAK;AACnD,QAAM,MAAM,eAAe,OAAO,KAAK,KAAK,KAAK;AAEjD,SAAO;AAAA,IACL,YAAY,QAAQ,KAAK,IAAI,QAAQ,IAAI,CAAC;AAAA,IAC1C,SAAS,QAAQ,KAAK,IAAI,MAAM,KAAK,CAAC;AAAA,IACtC,WAAW,aAAa,MAAM;AAAA,IAC9B,MAAM,QAAQ,IAAI;AAAA,IAClB,YAAY,QAAQ,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;AAAA,IAC3C,SAAS,QAAQ,IAAI;AAAA,IACrB,GAAG,QAAQ,CAAC;AAAA,EACd;AACF;AAEA,SAAS,YAAY,UAA0B,SAA0C;AACvF,SAAO;AAAA,IACL,wBAAwB,SAAS;AAAA,IACjC,qBAAqB,SAAS;AAAA,IAC9B,iBAAiB,SAAS;AAAA,IAC1B,wBAAwB,SAAS;AAAA,IACjC,qBAAqB,SAAS;AAAA,IAC9B,cAAc,SAAS;AAAA,IACvB,GAAI,YAAY,SAAY,CAAC,IAAI,EAAE,qBAAqB,GAAG,KAAK,IAAI,GAAG,OAAO,CAAC,KAAK;AAAA,EACtF;AACF;AAEA,SAAS,eAAe,OAAe,OAAe,SAA6C;AACjG,SAAO;AAAA,IACL,wBAAwB,SAAU,QAAQ,OAAO,KAAK,IAAI,OAAO,CAAC,IAAK,GAAG;AAAA,IAC1E,GAAI,YAAY,SAAY,CAAC,IAAI,EAAE,qBAAqB,GAAG,KAAK,IAAI,GAAG,OAAO,CAAC,KAAK;AAAA,EACtF;AACF;AAEA,SAAS,YAAY,SAA0C;AAC7D,SAAO,YAAY,SAAY,CAAC,IAAI,EAAE,qBAAqB,GAAG,KAAK,IAAI,GAAG,OAAO,CAAC,KAAK;AACzF;AAEA,SAAS,kBAAkB,UAA0B;AACnD,SAAO,KAAK,IAAI,KAAK,KAAK,IAAI,GAAG,QAAQ,CAAC;AAC5C;AAEA,SAAS,UAAU,MAAiB,UAA6B;AAC/D,QAAM,QAAQ,QAAQ,kBAAkB,QAAQ,CAAC;AACjD,MAAI,SAAS,IAAK,QAAO,EAAE,mBAAmB,MAAM;AACpD,SAAO,EAAE,mBAAmB,MAAM;AACpC;AAEA,SAAS,cACP,WACA,UACsB;AACtB,SAAO,aAAa;AACtB;AAGO,SAAS,kBAAkB;AAAA,EAChC,IAAI;AAAA,EACJ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAyC;AACvC,QAAM,WAAW,YAAY,QAAQ,GAAG,MAAM,KAAK,GAAG;AACtD,QAAM,gBAAgB,CAAC,UAA4C;AACjE,QAAI,MAAM,QAAQ,WAAW,MAAM,QAAQ,IAAK;AAChD,UAAM,eAAe;AACrB,oBAAgB,KAAK;AAAA,EACvB;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,eAAa,cAAc,SAAY;AAAA,MACvC,aAAU;AAAA,MACV,kBAAgB,SAAS;AAAA,MACzB,yBAAuB,eAAe,WAAW,KAAK;AAAA,MACtD,aAAW,OAAO,OAAO,IAAI;AAAA,MAC7B,OAAO,YAAY,UAAU,WAAW,UAAU,MAAS;AAAA,MAE1D,wBACC;AAAA,QAAC;AAAA;AAAA,UACC,aAAU;AAAA,UACV,MAAK;AAAA,UACL,cAAY,UAAU,OAAO,OAAO,IAAI,CAAC;AAAA,UACzC,gBAAc;AAAA,UACd,SAAS,MAAM,gBAAgB,KAAK;AAAA,UACpC,WAAW;AAAA,UACX,gBAAgB;AAAA,UAChB,eAAe,CAAC,UACd,sBAAsB,EAAE,OAAO,SAAS,MAAM,YAAY,QAAQ,CAAC;AAAA;AAAA,MAEvE,IACE;AAAA;AAAA,EACN;AAEJ;AACA,kBAAkB,cAAc;AAEzB,SAAS,YACd,OACqB;AACrB,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,cAAc;AAAA,IACd;AAAA,IACA;AAAA,IACA,cAAc;AAAA,IACd;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,kBAAkB;AAAA,IAClB,YAAY,KAAK;AAAA,IACjB,aAAa;AAAA,IACb,GAAG;AAAA,EACL,IAAI;AACJ,QAAM,EAAE,KAAK,IAAI,IAAI,eAAe,IAAI;AACxC,QAAM,QAAQ,aAAa;AAC3B,QAAM,cAAc,cAAc,OAAO,eAAe,IAAI,CAAC;AAC7D,QAAM,cAAc,cAAc,OAAO,eAAe,KAAK,GAAG,CAAC;AACjE,QAAM,eACJ,qBAAqB,wBAAwB,KAAK,SAAS,IAAI,KAAK,SAAS,IAAI;AACnF,QAAM,gBAAgB,iBAAiB,SAAY,SAAY,KAAK,YAAY;AAChF,QAAM,kBAAkB,sBAAsB,SAAY,SAAY,KAAK,iBAAiB;AAC5F,QAAM,UAAU,eACd,oBAAC,QAAG,aAAU,WAAU,cAAY,GAAG,KAAK,YACzC,eAAK,IAAI,CAAC,QAAQ,UACjB;AAAA,IAAC;AAAA;AAAA,MAEC,IAAG;AAAA,MACH;AAAA,MACA;AAAA,MACA,GAAI,QAAQ,KAAK,SAAU;AAAA,MAC3B,MAAM,MAAM,KAAK;AAAA,MACjB;AAAA,MACA;AAAA,MACA;AAAA,MACA,UAAU,sBAAsB;AAAA,MAChC,YAAY,wBAAwB;AAAA,MACpC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,IAdK,GAAG,OAAO,IAAI,IAAI,KAAK;AAAA,EAe9B,CACD,GACH;AAGF,SAAO,iBAAiB;AAAA,IACtB;AAAA,IACA,YAAY;AAAA,IACZ,OAAO;AAAA,MACL,GAAG;AAAA,MACH,cAAc;AAAA,MACd,2BAA2B;AAAA,MAC3B,oBAAoB,cAAc,KAAK;AAAA,MACvC,UACE,iCACE;AAAA,4BAAC,gBAAY,iBAAM;AAAA,QAClB,eAAe,gBACd,qBAAC,SAAI,aAAU,WAAU,aAAW,kBAAkB,WAAW,OAC/D;AAAA,8BAAC,UAAK,aAAU,gBAAgB,iBAAO,cAAc,IAAI,GAAE;AAAA,UAC3D,qBAAC,UAAK;AAAA;AAAA,YAAG,cAAc;AAAA,aAAK;AAAA,UAC5B,qBAAC,UAAK;AAAA;AAAA,YAAG,cAAc;AAAA,aAAK;AAAA,UAC5B,qBAAC,UAAK;AAAA;AAAA,YAAG,cAAc;AAAA,aAAI;AAAA,UAC3B,qBAAC,UAAK;AAAA;AAAA,YAAG,cAAc;AAAA,aAAM;AAAA,WAC/B,IACE;AAAA,QACJ,qBAAC,SAAI,aAAU,QAAO,eAAa,cAAc,SAAY,MAC3D;AAAA,8BAAC,SAAI,aAAU,mBACb,+BAAC,SAAI,aAAU,UACb;AAAA,iCAAC,SAAI,aAAU,YACZ;AAAA;AAAA,cACA,eAAe,mBAAmB,sBAAsB,SACvD;AAAA,gBAAC;AAAA;AAAA,kBACC,aAAU;AAAA,kBACV,eAAY;AAAA,kBACZ,OAAO,eAAe,aAAa,mBAAmB,WAAW,OAAO;AAAA,kBAExE,8BAAC,UAAK,aAAU,oBAAmB,eAAC;AAAA;AAAA,cACtC,IACE;AAAA,eACN;AAAA,YACA,oBAAC,SAAI,aAAU,iBACb,+BAAC,QAAG,aAAU,aAAY,aAAU,QAAO,cAAY,GAAG,KAAK,WAC5D;AAAA,0BAAY,IAAI,CAAC,WAAW,UAC3B;AAAA,gBAAC;AAAA;AAAA,kBAEC,OAAO,UAAU,KAAK,UAAU,QAAQ;AAAA,kBAEvC,oBAAU;AAAA;AAAA,gBAHN,GAAG,UAAU,QAAQ,IAAI,KAAK;AAAA,cAIrC,CACD;AAAA,cACA,eAAe,mBAAmB,sBAAsB,SACvD;AAAA,gBAAC;AAAA;AAAA,kBACC,aAAU;AAAA,kBACV,OAAO,eAAe,aAAa,mBAAmB,WAAW,MAAS;AAAA,kBAEzE,iBAAO,gBAAgB,IAAI;AAAA;AAAA,cAC9B,IACE;AAAA,eACN,GACF;AAAA,aACF,GACF;AAAA,UACA,qBAAC,QAAG,aAAU,cAAa,aAAU,SAAQ,cAAY,GAAG,KAAK,WAC9D;AAAA,wBAAY,IAAI,CAAC,WAAW,UAC3B;AAAA,cAAC;AAAA;AAAA,gBAEC,OAAO,UAAU,KAAK,UAAU,QAAQ;AAAA,gBAEvC,oBAAU;AAAA;AAAA,cAHN,GAAG,UAAU,QAAQ,IAAI,KAAK;AAAA,YAIrC,CACD;AAAA,YACA,eAAe,mBAAmB,sBAAsB,SACvD,oBAAC,QAAG,aAAU,mBAAkB,OAAO,YAAY,OAAO,GACvD,0BAAgB,OACnB,IACE;AAAA,aACN;AAAA,WACF;AAAA,QACA,qBAAC,WAAM,aAAU,QAAO,cAAY,GAAG,KAAK,SAC1C;AAAA,8BAAC,WACC,+BAAC,QACC;AAAA,gCAAC,QAAG,OAAM,OAAM,kBAAI;AAAA,YACpB,oBAAC,QAAG,OAAM,OAAM,kBAAI;AAAA,YACpB,oBAAC,QAAG,OAAM,OAAM,kBAAI;AAAA,YACpB,oBAAC,QAAG,OAAM,OAAM,iBAAG;AAAA,YACnB,oBAAC,QAAG,OAAM,OAAM,mBAAK;AAAA,aACvB,GACF;AAAA,UACA,oBAAC,WACE,eAAK,IAAI,CAAC,QAAQ,UACjB,qBAAC,QACC;AAAA,gCAAC,QAAG,OAAM,OAAO,iBAAO,OAAO,IAAI,GAAE;AAAA,YACrC,oBAAC,QAAI,iBAAO,MAAK;AAAA,YACjB,oBAAC,QAAI,iBAAO,MAAK;AAAA,YACjB,oBAAC,QAAI,iBAAO,KAAI;AAAA,YAChB,oBAAC,QAAI,iBAAO,OAAM;AAAA,eALX,GAAG,OAAO,IAAI,IAAI,KAAK,EAMhC,CACD,GACH;AAAA,WACF;AAAA,SACF;AAAA,IAEJ;AAAA,IACA;AAAA,IACA,OAAO,CAAC;AAAA,EACV,CAAC;AACH;AACA,YAAY,cAAc;","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ElementType, ReactElement } from "react";
|
|
2
|
-
import type {
|
|
3
|
-
export declare function CandleChartClient<TAs extends ElementType = "figure">(props:
|
|
2
|
+
import type { CandleChartClientProps } from "../types.js";
|
|
3
|
+
export declare function CandleChartClient<TAs extends ElementType = "figure">(props: CandleChartClientProps<TAs>): ReactElement | null;
|
|
4
4
|
export declare namespace CandleChartClient {
|
|
5
5
|
var displayName: string;
|
|
6
6
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CandleChartClient.d.ts","sourceRoot":"","sources":["../../../../src/components/candle-chart/client/CandleChartClient.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAiB,WAAW,EAAE,YAAY,EAAO,MAAM,OAAO,CAAC;AAE3E,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"CandleChartClient.d.ts","sourceRoot":"","sources":["../../../../src/components/candle-chart/client/CandleChartClient.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAiB,WAAW,EAAE,YAAY,EAAO,MAAM,OAAO,CAAC;AAE3E,OAAO,KAAK,EACV,sBAAsB,EAKvB,MAAM,aAAa,CAAC;AA+BrB,wBAAgB,iBAAiB,CAAC,GAAG,SAAS,WAAW,GAAG,QAAQ,EAClE,KAAK,EAAE,sBAAsB,CAAC,GAAG,CAAC,GACjC,YAAY,GAAG,IAAI,CAkJrB;yBApJe,iBAAiB"}
|
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
|
-
import { useCallback, useState } from "react";
|
|
4
|
-
import { CandleChart } from "../CandleChart.js";
|
|
3
|
+
import { useCallback, useMemo, useState } from "react";
|
|
4
|
+
import { CandleChart, CandleChartCandle, getPriceBounds } from "../CandleChart.js";
|
|
5
|
+
import { VirtualScroll } from "../../virtual-scroll/VirtualScroll.js";
|
|
6
|
+
import {
|
|
7
|
+
liveTimeAxis,
|
|
8
|
+
useLiveChartWindow
|
|
9
|
+
} from "../../internal/live-chart/use-live-chart-window.js";
|
|
10
|
+
import { useScrollbarReserve } from "../../internal/chart-scrollbar/use-scrollbar-reserve.js";
|
|
5
11
|
import { useChartViewport } from "../../../hooks/use-chart-viewport.js";
|
|
6
12
|
import { useMergedRefs } from "../../../hooks/use-merged-refs.js";
|
|
13
|
+
import { useResizeObserver } from "../../../hooks/use-resize-observer.js";
|
|
7
14
|
const Y_AXIS_COLUMN_WIDTH = 72 + 12;
|
|
8
15
|
function CandleChartClient(props) {
|
|
9
16
|
const {
|
|
10
17
|
data,
|
|
18
|
+
visibleCount,
|
|
19
|
+
xAxis,
|
|
11
20
|
ref: consumerRef,
|
|
12
21
|
style: consumerStyle,
|
|
13
22
|
onCandleClick: consumerOnCandleClick,
|
|
@@ -15,14 +24,27 @@ function CandleChartClient(props) {
|
|
|
15
24
|
onCandlePointerMove: consumerOnCandlePointerMove,
|
|
16
25
|
...consumerProps
|
|
17
26
|
} = props;
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
27
|
+
const live = visibleCount !== void 0 && visibleCount > 0;
|
|
28
|
+
const viewport = useChartViewport(
|
|
29
|
+
live ? {
|
|
30
|
+
enabled: false,
|
|
31
|
+
initialScale: 1,
|
|
32
|
+
itemCount: visibleCount,
|
|
33
|
+
measuredWidthOffset: Y_AXIS_COLUMN_WIDTH
|
|
34
|
+
} : { itemCount: data.length, measuredWidthOffset: Y_AXIS_COLUMN_WIDTH, minItemWidth: 24 }
|
|
35
|
+
);
|
|
36
|
+
const figureSize = useResizeObserver(viewport.ref);
|
|
37
|
+
const liveWindow = useLiveChartWindow({
|
|
38
|
+
count: data.length,
|
|
39
|
+
visibleCount,
|
|
40
|
+
itemWidth: viewport.itemWidth,
|
|
41
|
+
figureHeight: figureSize?.height,
|
|
42
|
+
scrollVariable: "--candle-scroll-x"
|
|
22
43
|
});
|
|
44
|
+
useScrollbarReserve(liveWindow.chartRef, "--candle-scrollbar-size");
|
|
23
45
|
const [cursor, setCursor] = useState(null);
|
|
24
46
|
const [selectedCandleIndex, setSelectedCandleIndex] = useState();
|
|
25
|
-
const chartRef = useMergedRefs(viewport.ref, consumerRef);
|
|
47
|
+
const chartRef = useMergedRefs(viewport.ref, consumerRef, liveWindow.chartRef);
|
|
26
48
|
const handleCandleClick = useCallback(
|
|
27
49
|
(index) => {
|
|
28
50
|
setSelectedCandleIndex(index);
|
|
@@ -41,22 +63,89 @@ function CandleChartClient(props) {
|
|
|
41
63
|
},
|
|
42
64
|
[consumerOnCandlePointerMove]
|
|
43
65
|
);
|
|
66
|
+
const liveItems = useMemo(
|
|
67
|
+
() => live ? data.map((candle, index) => ({ id: String(index), candle })) : [],
|
|
68
|
+
[data, live]
|
|
69
|
+
);
|
|
44
70
|
const style = {
|
|
45
71
|
...consumerStyle,
|
|
46
72
|
"--candle-scale": String(viewport.scale)
|
|
47
73
|
};
|
|
74
|
+
if (!live) {
|
|
75
|
+
const chartProps2 = {
|
|
76
|
+
...consumerProps,
|
|
77
|
+
...xAxis ? { xAxis } : {},
|
|
78
|
+
data,
|
|
79
|
+
activeCandleIndex: cursor?.index,
|
|
80
|
+
cursorY: cursor?.offsetY,
|
|
81
|
+
interactive: true,
|
|
82
|
+
onCandleClick: handleCandleClick,
|
|
83
|
+
onCandlePointerLeave: handleCandlePointerLeave,
|
|
84
|
+
onCandlePointerMove: handleCandlePointerMove,
|
|
85
|
+
ref: chartRef,
|
|
86
|
+
selectedCandleIndex,
|
|
87
|
+
style
|
|
88
|
+
};
|
|
89
|
+
return /* @__PURE__ */ jsx(CandleChart, { ...chartProps2 });
|
|
90
|
+
}
|
|
91
|
+
const { sliceStart, sliceEnd, contentSize, plotHeight } = liveWindow;
|
|
92
|
+
const slice = data.slice(sliceStart, sliceEnd);
|
|
93
|
+
const { min, max } = getPriceBounds(slice);
|
|
94
|
+
const toSliceIndex = (index) => index !== void 0 && index >= sliceStart && index < sliceEnd ? index - sliceStart : void 0;
|
|
95
|
+
const candleLayer = /* @__PURE__ */ jsx(
|
|
96
|
+
VirtualScroll,
|
|
97
|
+
{
|
|
98
|
+
height: plotHeight,
|
|
99
|
+
items: liveItems,
|
|
100
|
+
orientation: "horizontal",
|
|
101
|
+
overscan: 2,
|
|
102
|
+
rowHeight: viewport.itemWidth,
|
|
103
|
+
scrollRef: liveWindow.scrollRef,
|
|
104
|
+
onRangeChange: liveWindow.onRangeChange,
|
|
105
|
+
renderRow: ({ candle }, index) => {
|
|
106
|
+
const inSlice = index >= sliceStart && index < sliceEnd;
|
|
107
|
+
const isActive = cursor?.index === index;
|
|
108
|
+
return /* @__PURE__ */ jsx(
|
|
109
|
+
CandleChartCandle,
|
|
110
|
+
{
|
|
111
|
+
as: "div",
|
|
112
|
+
candle,
|
|
113
|
+
index,
|
|
114
|
+
x: 0,
|
|
115
|
+
slot: 100,
|
|
116
|
+
min,
|
|
117
|
+
max,
|
|
118
|
+
interactive: inSlice,
|
|
119
|
+
isActive,
|
|
120
|
+
isSelected: selectedCandleIndex === index,
|
|
121
|
+
cursorY: isActive ? cursor?.offsetY : void 0,
|
|
122
|
+
onCandleClick: handleCandleClick,
|
|
123
|
+
onCandlePointerLeave: handleCandlePointerLeave,
|
|
124
|
+
onCandlePointerMove: handleCandlePointerMove
|
|
125
|
+
}
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
);
|
|
130
|
+
const liveStyle = {
|
|
131
|
+
...style,
|
|
132
|
+
"--candle-content-size": `${contentSize}px`,
|
|
133
|
+
"--candle-plot-height": `${plotHeight}px`
|
|
134
|
+
};
|
|
48
135
|
const chartProps = {
|
|
49
136
|
...consumerProps,
|
|
50
|
-
data,
|
|
51
|
-
|
|
137
|
+
data: slice,
|
|
138
|
+
xAxis: xAxis ?? liveTimeAxis(data, sliceStart, sliceEnd, visibleCount),
|
|
139
|
+
activeCandleIndex: toSliceIndex(cursor?.index),
|
|
140
|
+
announceReadout: false,
|
|
141
|
+
candleLayer,
|
|
52
142
|
cursorY: cursor?.offsetY,
|
|
53
143
|
interactive: true,
|
|
54
|
-
onCandleClick: handleCandleClick,
|
|
55
|
-
onCandlePointerLeave: handleCandlePointerLeave,
|
|
56
|
-
onCandlePointerMove: handleCandlePointerMove,
|
|
57
144
|
ref: chartRef,
|
|
58
|
-
selectedCandleIndex,
|
|
59
|
-
|
|
145
|
+
selectedCandleIndex: toSliceIndex(selectedCandleIndex),
|
|
146
|
+
slotCount: data.length,
|
|
147
|
+
slotOffset: sliceStart,
|
|
148
|
+
style: liveStyle
|
|
60
149
|
};
|
|
61
150
|
return /* @__PURE__ */ jsx(CandleChart, { ...chartProps });
|
|
62
151
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/components/candle-chart/client/CandleChartClient.tsx"],"sourcesContent":["\"use client\";\n\nimport { useCallback, useState } from \"react\";\nimport type { CSSProperties, ElementType, ReactElement, Ref } from \"react\";\nimport { CandleChart } from \"../CandleChart.js\";\nimport type {
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/candle-chart/client/CandleChartClient.tsx"],"sourcesContent":["\"use client\";\n\nimport { useCallback, useMemo, useState } from \"react\";\nimport type { CSSProperties, ElementType, ReactElement, Ref } from \"react\";\nimport { CandleChart, CandleChartCandle, getPriceBounds } from \"../CandleChart.js\";\nimport type {\n CandleChartClientProps,\n CandleChartData,\n CandleChartPointerPosition,\n CandleChartProps,\n CandleChartViewProps,\n} from \"../types.js\";\nimport { VirtualScroll } from \"../../virtual-scroll/VirtualScroll.js\";\nimport {\n liveTimeAxis,\n useLiveChartWindow,\n} from \"../../internal/live-chart/use-live-chart-window.js\";\nimport { useScrollbarReserve } from \"../../internal/chart-scrollbar/use-scrollbar-reserve.js\";\nimport { useChartViewport } from \"../../../hooks/use-chart-viewport.js\";\nimport { useMergedRefs } from \"../../../hooks/use-merged-refs.js\";\nimport { useResizeObserver } from \"../../../hooks/use-resize-observer.js\";\n\ninterface InteractiveCandleStyle extends CSSProperties {\n \"--candle-scale\": string;\n \"--candle-content-size\"?: string;\n \"--candle-plot-height\"?: string;\n}\n\ninterface CursorState extends CandleChartPointerPosition {}\n\ninterface LiveCandle {\n readonly id: string;\n readonly candle: CandleChartData;\n}\n\n/**\n * `[data-part=\"plot\"]`'s fixed price-axis column (4.5rem) plus its grid gap\n * (0.75rem) — see candle-chart.css. `useChartViewport` measures the whole\n * `<figure>`, but candles only ever get the width left over after this\n * column, so it must be subtracted before sizing `minItemWidth`.\n */\nconst Y_AXIS_COLUMN_WIDTH = 72 + 12;\nexport function CandleChartClient<TAs extends ElementType = \"figure\">(\n props: CandleChartClientProps<TAs>,\n): ReactElement | null {\n const {\n data,\n visibleCount,\n xAxis,\n ref: consumerRef,\n style: consumerStyle,\n onCandleClick: consumerOnCandleClick,\n onCandlePointerLeave: consumerOnCandlePointerLeave,\n onCandlePointerMove: consumerOnCandlePointerMove,\n ...consumerProps\n } = props as CandleChartClientProps<\"figure\"> & {\n ref?: Ref<HTMLElement>;\n style?: CSSProperties;\n };\n const live = visibleCount !== undefined && visibleCount > 0;\n const viewport = useChartViewport(\n live\n ? {\n enabled: false,\n initialScale: 1,\n itemCount: visibleCount,\n measuredWidthOffset: Y_AXIS_COLUMN_WIDTH,\n }\n : { itemCount: data.length, measuredWidthOffset: Y_AXIS_COLUMN_WIDTH, minItemWidth: 24 },\n );\n const figureSize = useResizeObserver(viewport.ref);\n const liveWindow = useLiveChartWindow({\n count: data.length,\n visibleCount,\n itemWidth: viewport.itemWidth,\n figureHeight: figureSize?.height,\n scrollVariable: \"--candle-scroll-x\",\n });\n useScrollbarReserve(liveWindow.chartRef, \"--candle-scrollbar-size\");\n // Cursor and selection hold absolute indices so they survive scrolling and appends.\n const [cursor, setCursor] = useState<CursorState | null>(null);\n const [selectedCandleIndex, setSelectedCandleIndex] = useState<number | undefined>();\n const chartRef = useMergedRefs<HTMLElement>(viewport.ref, consumerRef, liveWindow.chartRef);\n const handleCandleClick = useCallback(\n (index: number) => {\n setSelectedCandleIndex(index);\n consumerOnCandleClick?.(index);\n },\n [consumerOnCandleClick],\n );\n const handleCandlePointerLeave = useCallback(() => {\n setCursor(null);\n consumerOnCandlePointerLeave?.();\n }, [consumerOnCandlePointerLeave]);\n const handleCandlePointerMove = useCallback(\n (position: CandleChartPointerPosition) => {\n setCursor(position);\n consumerOnCandlePointerMove?.(position);\n },\n [consumerOnCandlePointerMove],\n );\n const liveItems = useMemo<readonly LiveCandle[]>(\n () => (live ? data.map((candle, index) => ({ id: String(index), candle })) : []),\n [data, live],\n );\n const style: InteractiveCandleStyle = {\n ...consumerStyle,\n \"--candle-scale\": String(viewport.scale),\n };\n\n if (!live) {\n const chartProps = {\n ...consumerProps,\n ...(xAxis ? { xAxis } : {}),\n data,\n activeCandleIndex: cursor?.index,\n cursorY: cursor?.offsetY,\n interactive: true,\n onCandleClick: handleCandleClick,\n onCandlePointerLeave: handleCandlePointerLeave,\n onCandlePointerMove: handleCandlePointerMove,\n ref: chartRef,\n selectedCandleIndex,\n style,\n } as CandleChartProps<\"figure\">;\n return <CandleChart {...chartProps} />;\n }\n\n const { sliceStart, sliceEnd, contentSize, plotHeight } = liveWindow;\n const slice = data.slice(sliceStart, sliceEnd);\n const { min, max } = getPriceBounds(slice);\n const toSliceIndex = (index: number | undefined): number | undefined =>\n index !== undefined && index >= sliceStart && index < sliceEnd ? index - sliceStart : undefined;\n const candleLayer = (\n <VirtualScroll\n height={plotHeight}\n items={liveItems}\n orientation=\"horizontal\"\n overscan={2}\n rowHeight={viewport.itemWidth}\n scrollRef={liveWindow.scrollRef}\n onRangeChange={liveWindow.onRangeChange}\n renderRow={({ candle }, index) => {\n // Overscan candles stay inert, so focus never scrolls hidden candles into view.\n const inSlice = index >= sliceStart && index < sliceEnd;\n const isActive = cursor?.index === index;\n return (\n <CandleChartCandle\n as=\"div\"\n candle={candle}\n index={index}\n x={0}\n slot={100}\n min={min}\n max={max}\n interactive={inSlice}\n isActive={isActive}\n isSelected={selectedCandleIndex === index}\n cursorY={isActive ? cursor?.offsetY : undefined}\n onCandleClick={handleCandleClick}\n onCandlePointerLeave={handleCandlePointerLeave}\n onCandlePointerMove={handleCandlePointerMove}\n />\n );\n }}\n />\n );\n\n const liveStyle: InteractiveCandleStyle = {\n ...style,\n \"--candle-content-size\": `${contentSize}px`,\n \"--candle-plot-height\": `${plotHeight}px`,\n };\n const chartProps = {\n ...consumerProps,\n data: slice,\n xAxis: xAxis ?? liveTimeAxis(data, sliceStart, sliceEnd, visibleCount),\n activeCandleIndex: toSliceIndex(cursor?.index),\n announceReadout: false,\n candleLayer,\n cursorY: cursor?.offsetY,\n interactive: true,\n ref: chartRef,\n selectedCandleIndex: toSliceIndex(selectedCandleIndex),\n slotCount: data.length,\n slotOffset: sliceStart,\n style: liveStyle,\n } as CandleChartProps<\"figure\"> & CandleChartViewProps;\n\n return <CandleChart {...chartProps} />;\n}\nCandleChartClient.displayName = \"CandleChartClient\";\n"],"mappings":"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","names":["chartProps"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/candle-chart/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/candle-chart/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,YAAY,EAAE,yBAAyB,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/components/candle-chart/client/index.ts"],"sourcesContent":["export { CandleChartClient } from \"./CandleChartClient.js\";\n"],"mappings":"AAAA,SAAS,yBAAyB;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/candle-chart/client/index.ts"],"sourcesContent":["export { CandleChartClient } from \"./CandleChartClient.js\";\nexport type { CandleChartClientOwnProps, CandleChartClientProps } from \"../types.js\";\n"],"mappings":"AAAA,SAAS,yBAAyB;","names":[]}
|
|
@@ -31,4 +31,43 @@ export interface CandleChartOwnProps {
|
|
|
31
31
|
readonly yAxis?: readonly CandleChartAxisLabel[];
|
|
32
32
|
}
|
|
33
33
|
export type CandleChartProps<TAs extends ElementType = "figure"> = RenderableProps<TAs, CandleChartOwnProps, Record<string, never>, HTMLElement>;
|
|
34
|
+
export interface CandleChartClientOwnProps extends CandleChartOwnProps {
|
|
35
|
+
/**
|
|
36
|
+
* Live mode: sizes candles so `visibleCount` fill the viewport and virtualizes
|
|
37
|
+
* the full history with a horizontal VirtualScroll. The chart follows new
|
|
38
|
+
* candles while the reader is at the newest one; the price scale, axes,
|
|
39
|
+
* readout, and data table follow the visible candles. Zoom is disabled.
|
|
40
|
+
*/
|
|
41
|
+
readonly visibleCount?: number;
|
|
42
|
+
}
|
|
43
|
+
export type CandleChartClientProps<TAs extends ElementType = "figure"> = RenderableProps<TAs, CandleChartClientOwnProps, Record<string, never>, HTMLElement>;
|
|
44
|
+
/** Internal props the client entry passes to `CandleChart`; not part of the public API. */
|
|
45
|
+
export interface CandleChartViewProps extends CandleChartOwnProps {
|
|
46
|
+
/** Replaces the static candle list (e.g. with a virtualized layer). */
|
|
47
|
+
readonly candleLayer?: ReactNode;
|
|
48
|
+
/** Announce readout changes; off for streaming data. Defaults to `true`. */
|
|
49
|
+
readonly announceReadout?: boolean;
|
|
50
|
+
/** Slots the crosshair is positioned across. Defaults to `data.length`. */
|
|
51
|
+
readonly slotCount?: number;
|
|
52
|
+
/** Slot of `data[0]` within those slots, when `data` is a slice. Defaults to `0`. */
|
|
53
|
+
readonly slotOffset?: number;
|
|
54
|
+
}
|
|
55
|
+
export interface CandleChartCandleProps {
|
|
56
|
+
readonly as: "div" | "li";
|
|
57
|
+
readonly candle: CandleChartData;
|
|
58
|
+
/** Index reported to the pointer and click callbacks. */
|
|
59
|
+
readonly index: number;
|
|
60
|
+
/** Candle position and width, as percentages of the candle layer. */
|
|
61
|
+
readonly x: number;
|
|
62
|
+
readonly slot: number;
|
|
63
|
+
readonly min: number;
|
|
64
|
+
readonly max: number;
|
|
65
|
+
readonly interactive: boolean;
|
|
66
|
+
readonly isActive: boolean;
|
|
67
|
+
readonly isSelected: boolean;
|
|
68
|
+
readonly cursorY?: number | undefined;
|
|
69
|
+
readonly onCandleClick?: ((index: number) => void) | undefined;
|
|
70
|
+
readonly onCandlePointerLeave?: (() => void) | undefined;
|
|
71
|
+
readonly onCandlePointerMove?: ((position: CandleChartPointerPosition) => void) | undefined;
|
|
72
|
+
}
|
|
34
73
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/candle-chart/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACpD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAEjE,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,0DAA0D;IAC1D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,gEAAgE;IAChE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,SAAS,eAAe,EAAE,CAAC;IAC1C,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACjD,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3C,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC,QAAQ,EAAE,0BAA0B,KAAK,IAAI,CAAC;IAC9E,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,oBAAoB,EAAE,CAAC;IACjD,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,oBAAoB,EAAE,CAAC;CAClD;AAED,MAAM,MAAM,gBAAgB,CAAC,GAAG,SAAS,WAAW,GAAG,QAAQ,IAAI,eAAe,CAChF,GAAG,EACH,mBAAmB,EACnB,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EACrB,WAAW,CACZ,CAAC"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/candle-chart/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type RefObject } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Publishes the horizontal scrollbar thickness of a chart's
|
|
4
|
+
* `[data-part="scroll-viewport"]` as `variable` on the chart element, so the
|
|
5
|
+
* plot grid can reserve that height below the time axis. A classic scrollbar
|
|
6
|
+
* otherwise takes its height from the scroll viewport, and the flexible
|
|
7
|
+
* candle row absorbs it, misaligning the value axis. Overlay scrollbars
|
|
8
|
+
* measure 0. Read from ResizeObserver box sizes: no forced layout.
|
|
9
|
+
*/
|
|
10
|
+
export declare function useScrollbarReserve(chartRef: RefObject<HTMLElement | null>, variable: `--${string}`): void;
|
|
11
|
+
//# sourceMappingURL=use-scrollbar-reserve.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-scrollbar-reserve.d.ts","sourceRoot":"","sources":["../../../../src/components/internal/chart-scrollbar/use-scrollbar-reserve.ts"],"names":[],"mappings":"AAEA,OAAO,EAAa,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAElD;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,EACvC,QAAQ,EAAE,KAAK,MAAM,EAAE,GACtB,IAAI,CAeN"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useEffect } from "react";
|
|
3
|
+
function useScrollbarReserve(chartRef, variable) {
|
|
4
|
+
useEffect(() => {
|
|
5
|
+
const chart = chartRef.current;
|
|
6
|
+
const scroller = chart?.querySelector('[data-part="scroll-viewport"]');
|
|
7
|
+
if (!chart || !scroller || typeof ResizeObserver === "undefined") return;
|
|
8
|
+
const observer = new ResizeObserver(([entry]) => {
|
|
9
|
+
const border = entry?.borderBoxSize[0]?.blockSize;
|
|
10
|
+
const content = entry?.contentBoxSize[0]?.blockSize;
|
|
11
|
+
if (border === void 0 || content === void 0) return;
|
|
12
|
+
chart.style.setProperty(variable, `${Math.max(0, border - content)}px`);
|
|
13
|
+
});
|
|
14
|
+
observer.observe(scroller);
|
|
15
|
+
return () => observer.disconnect();
|
|
16
|
+
}, [chartRef, variable]);
|
|
17
|
+
}
|
|
18
|
+
export {
|
|
19
|
+
useScrollbarReserve
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=use-scrollbar-reserve.js.map
|