@gunkin/uiify 0.0.2-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/behavior.css +16 -1
- package/dist/components/behavior.css.map +1 -1
- 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/components/virtual-scroll/VirtualScroll.d.ts.map +1 -1
- package/dist/components/virtual-scroll/VirtualScroll.js +40 -44
- package/dist/components/virtual-scroll/VirtualScroll.js.map +1 -1
- package/dist/components/virtual-scroll/index.d.ts +1 -1
- package/dist/components/virtual-scroll/index.d.ts.map +1 -1
- package/dist/components/virtual-scroll/index.js.map +1 -1
- package/dist/components/virtual-scroll/types.d.ts +5 -0
- package/dist/components/virtual-scroll/types.d.ts.map +1 -1
- package/dist/components/virtual-scroll/types.js.map +1 -1
- package/dist/components/virtual-scroll/virtual-scroll-axis.d.ts +9 -0
- package/dist/components/virtual-scroll/virtual-scroll-axis.d.ts.map +1 -0
- package/dist/components/virtual-scroll/virtual-scroll-axis.js +18 -0
- package/dist/components/virtual-scroll/virtual-scroll-axis.js.map +1 -0
- package/dist/styles/index.css +1 -1
- package/package.json +1 -1
|
@@ -199,10 +199,25 @@
|
|
|
199
199
|
block-size: var( --uiify-virtual-scroll-total-size, calc(var(--uiify-virtual-scroll-count, 0) * var(--uiify-virtual-scroll-row-height)) );
|
|
200
200
|
}
|
|
201
201
|
[data-uiify-virtual-scroll] [data-part=row] {
|
|
202
|
+
--uiify-row-position: var( --uiify-row-offset, calc( (var(--uiify-range-start, 0) + var(--uiify-slot-index, 0)) * var(--uiify-virtual-scroll-row-height) ) );
|
|
202
203
|
position: absolute;
|
|
203
204
|
inset-inline: 0;
|
|
204
205
|
block-size: var(--uiify-row-height, var(--uiify-virtual-scroll-row-height));
|
|
205
|
-
translate: 0 var(
|
|
206
|
+
translate: 0 var(--uiify-row-position);
|
|
207
|
+
}
|
|
208
|
+
[data-uiify-virtual-scroll][data-orientation=horizontal] {
|
|
209
|
+
overflow: auto hidden;
|
|
210
|
+
}
|
|
211
|
+
[data-uiify-virtual-scroll][data-orientation=horizontal] > [data-part=spacer] {
|
|
212
|
+
block-size: 100%;
|
|
213
|
+
inline-size: var( --uiify-virtual-scroll-total-size, calc(var(--uiify-virtual-scroll-count, 0) * var(--uiify-virtual-scroll-row-height)) );
|
|
214
|
+
}
|
|
215
|
+
[data-uiify-virtual-scroll][data-orientation=horizontal] > [data-part=spacer] > [data-part=row] {
|
|
216
|
+
inset-block: 0;
|
|
217
|
+
inset-inline: var(--uiify-row-position) auto;
|
|
218
|
+
block-size: auto;
|
|
219
|
+
inline-size: var(--uiify-row-height, var(--uiify-virtual-scroll-row-height));
|
|
220
|
+
translate: none;
|
|
206
221
|
}
|
|
207
222
|
}
|
|
208
223
|
/*# sourceMappingURL=behavior.css.map */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/behavior.css"],"sourcesContent":["/* Behavioral rules for @gunkin/uiify/components — zero visual opinion.\n * Import is required: @import \"@gunkin/uiify/components/behavior.css\";\n * Cascade order: consumer app layer wins without specificity fights. */\n@layer ui.reset, ui.behavior, ui.tokens, ui.theme, app;\n\n@layer ui.behavior {\n [popover]:not(:popover-open) {\n display: none;\n }\n\n /* Native modal dialogs: wheel/touch scrolling inside the dialog must not chain to the page behind it. */\n dialog:modal {\n overscroll-behavior: contain;\n }\n\n /* Closed dialogs stay hidden even when author CSS sets display. */\n dialog:not([open]) {\n display: none;\n }\n\n [data-anchor]:not([data-positioning]) {\n anchor-name: var(--uiify-anchor, attr(data-anchor type(<custom-ident>)));\n }\n\n [data-positioning=\"native\"] {\n position: fixed;\n inset: auto;\n margin: 0;\n position-try-fallbacks: flip-block, flip-inline;\n }\n\n [data-positioning=\"native\"][data-anchor] {\n position-anchor: var(--uiify-anchor, attr(data-anchor type(<custom-ident>)));\n }\n\n [data-positioning=\"native\"][data-side=\"top\"] {\n position-area: top;\n margin-block-end: var(--uiify-anchor-offset, 0);\n }\n\n [data-positioning=\"native\"][data-side=\"right\"] {\n position-area: right;\n margin-inline-start: var(--uiify-anchor-offset, 0);\n }\n\n [data-positioning=\"native\"][data-side=\"bottom\"] {\n position-area: bottom;\n margin-block-start: var(--uiify-anchor-offset, 0);\n }\n\n [data-positioning=\"native\"][data-side=\"left\"] {\n position-area: left;\n margin-inline-end: var(--uiify-anchor-offset, 0);\n }\n\n [data-positioning=\"native\"][data-side=\"top\"][data-align=\"start\"] {\n position-area: top span-right;\n justify-self: start;\n }\n\n [data-positioning=\"native\"][data-side=\"top\"][data-align=\"end\"] {\n position-area: top span-left;\n justify-self: end;\n }\n\n [data-positioning=\"native\"][data-side=\"bottom\"][data-align=\"start\"] {\n position-area: bottom span-right;\n justify-self: start;\n }\n\n [data-positioning=\"native\"][data-side=\"bottom\"][data-align=\"end\"] {\n position-area: bottom span-left;\n justify-self: end;\n }\n\n [data-positioning=\"native\"][data-side=\"right\"][data-align=\"start\"] {\n align-self: start;\n position-area: right span-bottom;\n }\n\n [data-positioning=\"native\"][data-side=\"right\"][data-align=\"end\"] {\n align-self: end;\n position-area: right span-top;\n }\n\n [data-positioning=\"native\"][data-side=\"left\"][data-align=\"start\"] {\n align-self: start;\n position-area: left span-bottom;\n }\n\n [data-positioning=\"native\"][data-side=\"left\"][data-align=\"end\"] {\n align-self: end;\n position-area: left span-top;\n }\n\n /*\n * Local open-state hooks. Top layer does not change DOM ancestry, so a scope\n * that directly contains its surface can read the native state. Consumers\n * style with the selector or with the variable; neither is a second source of truth.\n *\n * Compound roots and their public trigger parts share one component identity plus\n * `data-part`, so native state stays readable without a second scope convention.\n */\n [data-uiify-modal][data-part=\"root\"]:has(> [data-uiify-modal][data-part=\"content\"][open])\n > [data-uiify-modal][data-part=\"trigger\"],\n [data-uiify-alert-modal][data-part=\"root\"]:has(\n > [data-uiify-alert-modal][data-part=\"content\"][open]\n )\n > [data-uiify-alert-modal][data-part=\"trigger\"],\n [data-uiify-popup][data-part=\"root\"]:has(> [data-uiify-popup][data-part=\"content\"]:popover-open)\n > [data-uiify-popup][data-part=\"trigger\"] {\n --uiify-trigger-open: 1;\n }\n\n [data-uiify-accordion] > [data-part=\"item\"][data-disabled] > summary,\n [data-uiify-collapsible][data-disabled] > summary {\n cursor: not-allowed;\n pointer-events: none;\n }\n\n /* Customizable select: opts the control and its picker into the browser's\n base styles so the picker becomes a styleable, top-layer popover with\n implicit anchor positioning. Behavior only — no visual opinion. Engines\n without support ignore this and keep the classic OS picker, which stays\n fully functional. */\n [data-uiify-select],\n [data-uiify-select]::picker(select) {\n appearance: base-select;\n }\n\n [data-uiify-tabs] > [data-part=\"item\"] > [data-part=\"control\"] {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n /* stylelint-disable-next-line property-no-deprecated -- kept for older browser support alongside clip-path */\n clip: rect(0, 0, 0, 0);\n clip-path: inset(50%);\n white-space: nowrap;\n border: 0;\n }\n\n [data-uiify-tabs] > [data-part=\"item\"] > [data-part=\"panel\"] {\n display: none;\n }\n\n [data-uiify-tabs] > [data-part=\"item\"] > [data-part=\"control\"]:checked ~ [data-part=\"panel\"] {\n display: block;\n }\n\n [data-uiify-stepper] > [data-part=\"step\"] > [data-part=\"panel\"] {\n display: none;\n }\n\n [data-uiify-stepper] > [data-part=\"step\"] > [data-part=\"control\"]:checked ~ [data-part=\"panel\"] {\n display: block;\n }\n\n [data-uiify-tabs] [data-part=\"trigger\"][data-disabled] {\n cursor: not-allowed;\n pointer-events: none;\n }\n\n [data-uiify-spinner][data-state=\"delayed\"] {\n animation-name: uiify-spinner-show;\n animation-duration: 0s;\n animation-delay: attr(data-delay type(<time>), 0s);\n animation-fill-mode: forwards;\n visibility: hidden;\n }\n\n @keyframes uiify-spinner-show {\n to {\n visibility: visible;\n }\n }\n\n [data-uiify-breadcrumb] [data-part=\"ellipsis\"] > ol {\n list-style: none;\n margin: 0;\n padding: 0;\n }\n\n [data-uiify-carousel] > [data-part=\"viewport\"] {\n display: grid;\n min-inline-size: 0;\n min-block-size: 0;\n gap: var(--uiify-carousel-gap, 0);\n scroll-behavior: auto;\n }\n\n [data-uiify-carousel][data-axis=\"inline\"] > [data-part=\"viewport\"] {\n grid-auto-flow: column;\n grid-auto-columns: var(--uiify-carousel-slide-size, 100%);\n overflow-inline: auto;\n overflow-block: hidden;\n scroll-snap-type: inline mandatory;\n }\n\n [data-uiify-carousel][data-axis=\"block\"] > [data-part=\"viewport\"] {\n grid-auto-flow: row;\n grid-auto-rows: var(--uiify-carousel-slide-size, 100%);\n overflow-block: auto;\n overflow-inline: hidden;\n scroll-snap-type: block mandatory;\n }\n\n [data-uiify-carousel] > [data-part=\"viewport\"] > [data-part=\"slide\"] {\n min-inline-size: 0;\n min-block-size: 0;\n scroll-snap-align: start;\n }\n\n [data-uiify-carousel] > [data-part=\"viewport\"]:focus-visible,\n [data-uiify-carousel] > button[data-part=\"controls\"]:focus-visible,\n [data-uiify-carousel] > [data-part=\"dots\"] > a:focus-visible {\n outline: auto;\n }\n\n @media (prefers-reduced-motion: no-preference) {\n [data-uiify-carousel] > [data-part=\"viewport\"] {\n scroll-behavior: smooth;\n }\n }\n\n [data-uiify-carousel] > [data-part=\"viewport\"][data-dragging] {\n scroll-behavior: auto;\n scroll-snap-type: none;\n }\n\n [data-uiify-carousel][data-mode=\"fade\"][data-enhanced] > [data-part=\"viewport\"] {\n grid-auto-flow: row;\n grid-template-columns: 100%;\n grid-auto-columns: auto;\n overflow: hidden;\n scroll-snap-type: none;\n touch-action: pan-y pinch-zoom;\n }\n\n [data-uiify-carousel][data-mode=\"fade\"][data-enhanced]\n > [data-part=\"viewport\"]\n > [data-part=\"slide\"] {\n grid-area: 1 / 1;\n opacity: 0;\n pointer-events: none;\n }\n\n [data-uiify-carousel][data-mode=\"fade\"][data-enhanced]\n > [data-part=\"viewport\"]\n > [data-part=\"slide\"][data-state=\"active\"] {\n opacity: 1;\n pointer-events: auto;\n }\n\n @media (prefers-reduced-motion: no-preference) {\n [data-uiify-carousel][data-mode=\"fade\"][data-enhanced]\n > [data-part=\"viewport\"]\n > [data-part=\"slide\"] {\n transition: opacity var(--uiify-carousel-fade-duration, 300ms) ease;\n }\n }\n\n [data-uiify-virtual-scroll] {\n --uiify-virtual-scroll-row-height: 64px;\n\n overflow-y: auto;\n }\n\n [data-uiify-virtual-scroll] > [data-part=\"spacer\"] {\n position: relative;\n block-size: var(\n --uiify-virtual-scroll-total-size,\n calc(var(--uiify-virtual-scroll-count, 0) * var(--uiify-virtual-scroll-row-height))\n );\n }\n\n [data-uiify-virtual-scroll] [data-part=\"row\"] {\n position: absolute;\n inset-inline: 0;\n block-size: var(--uiify-row-height, var(--uiify-virtual-scroll-row-height));\n translate: 0\n var(\n --uiify-row-offset,\n calc(\n (var(--uiify-range-start, 0) + var(--uiify-slot-index, 0)) *\n var(--uiify-virtual-scroll-row-height)\n )\n );\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/behavior.css"],"sourcesContent":["/* Behavioral rules for @gunkin/uiify/components — zero visual opinion.\n * Import is required: @import \"@gunkin/uiify/components/behavior.css\";\n * Cascade order: consumer app layer wins without specificity fights. */\n@layer ui.reset, ui.behavior, ui.tokens, ui.theme, app;\n\n@layer ui.behavior {\n [popover]:not(:popover-open) {\n display: none;\n }\n\n /* Native modal dialogs: wheel/touch scrolling inside the dialog must not chain to the page behind it. */\n dialog:modal {\n overscroll-behavior: contain;\n }\n\n /* Closed dialogs stay hidden even when author CSS sets display. */\n dialog:not([open]) {\n display: none;\n }\n\n [data-anchor]:not([data-positioning]) {\n anchor-name: var(--uiify-anchor, attr(data-anchor type(<custom-ident>)));\n }\n\n [data-positioning=\"native\"] {\n position: fixed;\n inset: auto;\n margin: 0;\n position-try-fallbacks: flip-block, flip-inline;\n }\n\n [data-positioning=\"native\"][data-anchor] {\n position-anchor: var(--uiify-anchor, attr(data-anchor type(<custom-ident>)));\n }\n\n [data-positioning=\"native\"][data-side=\"top\"] {\n position-area: top;\n margin-block-end: var(--uiify-anchor-offset, 0);\n }\n\n [data-positioning=\"native\"][data-side=\"right\"] {\n position-area: right;\n margin-inline-start: var(--uiify-anchor-offset, 0);\n }\n\n [data-positioning=\"native\"][data-side=\"bottom\"] {\n position-area: bottom;\n margin-block-start: var(--uiify-anchor-offset, 0);\n }\n\n [data-positioning=\"native\"][data-side=\"left\"] {\n position-area: left;\n margin-inline-end: var(--uiify-anchor-offset, 0);\n }\n\n [data-positioning=\"native\"][data-side=\"top\"][data-align=\"start\"] {\n position-area: top span-right;\n justify-self: start;\n }\n\n [data-positioning=\"native\"][data-side=\"top\"][data-align=\"end\"] {\n position-area: top span-left;\n justify-self: end;\n }\n\n [data-positioning=\"native\"][data-side=\"bottom\"][data-align=\"start\"] {\n position-area: bottom span-right;\n justify-self: start;\n }\n\n [data-positioning=\"native\"][data-side=\"bottom\"][data-align=\"end\"] {\n position-area: bottom span-left;\n justify-self: end;\n }\n\n [data-positioning=\"native\"][data-side=\"right\"][data-align=\"start\"] {\n align-self: start;\n position-area: right span-bottom;\n }\n\n [data-positioning=\"native\"][data-side=\"right\"][data-align=\"end\"] {\n align-self: end;\n position-area: right span-top;\n }\n\n [data-positioning=\"native\"][data-side=\"left\"][data-align=\"start\"] {\n align-self: start;\n position-area: left span-bottom;\n }\n\n [data-positioning=\"native\"][data-side=\"left\"][data-align=\"end\"] {\n align-self: end;\n position-area: left span-top;\n }\n\n /*\n * Local open-state hooks. Top layer does not change DOM ancestry, so a scope\n * that directly contains its surface can read the native state. Consumers\n * style with the selector or with the variable; neither is a second source of truth.\n *\n * Compound roots and their public trigger parts share one component identity plus\n * `data-part`, so native state stays readable without a second scope convention.\n */\n [data-uiify-modal][data-part=\"root\"]:has(> [data-uiify-modal][data-part=\"content\"][open])\n > [data-uiify-modal][data-part=\"trigger\"],\n [data-uiify-alert-modal][data-part=\"root\"]:has(\n > [data-uiify-alert-modal][data-part=\"content\"][open]\n )\n > [data-uiify-alert-modal][data-part=\"trigger\"],\n [data-uiify-popup][data-part=\"root\"]:has(> [data-uiify-popup][data-part=\"content\"]:popover-open)\n > [data-uiify-popup][data-part=\"trigger\"] {\n --uiify-trigger-open: 1;\n }\n\n [data-uiify-accordion] > [data-part=\"item\"][data-disabled] > summary,\n [data-uiify-collapsible][data-disabled] > summary {\n cursor: not-allowed;\n pointer-events: none;\n }\n\n /* Customizable select: opts the control and its picker into the browser's\n base styles so the picker becomes a styleable, top-layer popover with\n implicit anchor positioning. Behavior only — no visual opinion. Engines\n without support ignore this and keep the classic OS picker, which stays\n fully functional. */\n [data-uiify-select],\n [data-uiify-select]::picker(select) {\n appearance: base-select;\n }\n\n [data-uiify-tabs] > [data-part=\"item\"] > [data-part=\"control\"] {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n /* stylelint-disable-next-line property-no-deprecated -- kept for older browser support alongside clip-path */\n clip: rect(0, 0, 0, 0);\n clip-path: inset(50%);\n white-space: nowrap;\n border: 0;\n }\n\n [data-uiify-tabs] > [data-part=\"item\"] > [data-part=\"panel\"] {\n display: none;\n }\n\n [data-uiify-tabs] > [data-part=\"item\"] > [data-part=\"control\"]:checked ~ [data-part=\"panel\"] {\n display: block;\n }\n\n [data-uiify-stepper] > [data-part=\"step\"] > [data-part=\"panel\"] {\n display: none;\n }\n\n [data-uiify-stepper] > [data-part=\"step\"] > [data-part=\"control\"]:checked ~ [data-part=\"panel\"] {\n display: block;\n }\n\n [data-uiify-tabs] [data-part=\"trigger\"][data-disabled] {\n cursor: not-allowed;\n pointer-events: none;\n }\n\n [data-uiify-spinner][data-state=\"delayed\"] {\n animation-name: uiify-spinner-show;\n animation-duration: 0s;\n animation-delay: attr(data-delay type(<time>), 0s);\n animation-fill-mode: forwards;\n visibility: hidden;\n }\n\n @keyframes uiify-spinner-show {\n to {\n visibility: visible;\n }\n }\n\n [data-uiify-breadcrumb] [data-part=\"ellipsis\"] > ol {\n list-style: none;\n margin: 0;\n padding: 0;\n }\n\n [data-uiify-carousel] > [data-part=\"viewport\"] {\n display: grid;\n min-inline-size: 0;\n min-block-size: 0;\n gap: var(--uiify-carousel-gap, 0);\n scroll-behavior: auto;\n }\n\n [data-uiify-carousel][data-axis=\"inline\"] > [data-part=\"viewport\"] {\n grid-auto-flow: column;\n grid-auto-columns: var(--uiify-carousel-slide-size, 100%);\n overflow-inline: auto;\n overflow-block: hidden;\n scroll-snap-type: inline mandatory;\n }\n\n [data-uiify-carousel][data-axis=\"block\"] > [data-part=\"viewport\"] {\n grid-auto-flow: row;\n grid-auto-rows: var(--uiify-carousel-slide-size, 100%);\n overflow-block: auto;\n overflow-inline: hidden;\n scroll-snap-type: block mandatory;\n }\n\n [data-uiify-carousel] > [data-part=\"viewport\"] > [data-part=\"slide\"] {\n min-inline-size: 0;\n min-block-size: 0;\n scroll-snap-align: start;\n }\n\n [data-uiify-carousel] > [data-part=\"viewport\"]:focus-visible,\n [data-uiify-carousel] > button[data-part=\"controls\"]:focus-visible,\n [data-uiify-carousel] > [data-part=\"dots\"] > a:focus-visible {\n outline: auto;\n }\n\n @media (prefers-reduced-motion: no-preference) {\n [data-uiify-carousel] > [data-part=\"viewport\"] {\n scroll-behavior: smooth;\n }\n }\n\n [data-uiify-carousel] > [data-part=\"viewport\"][data-dragging] {\n scroll-behavior: auto;\n scroll-snap-type: none;\n }\n\n [data-uiify-carousel][data-mode=\"fade\"][data-enhanced] > [data-part=\"viewport\"] {\n grid-auto-flow: row;\n grid-template-columns: 100%;\n grid-auto-columns: auto;\n overflow: hidden;\n scroll-snap-type: none;\n touch-action: pan-y pinch-zoom;\n }\n\n [data-uiify-carousel][data-mode=\"fade\"][data-enhanced]\n > [data-part=\"viewport\"]\n > [data-part=\"slide\"] {\n grid-area: 1 / 1;\n opacity: 0;\n pointer-events: none;\n }\n\n [data-uiify-carousel][data-mode=\"fade\"][data-enhanced]\n > [data-part=\"viewport\"]\n > [data-part=\"slide\"][data-state=\"active\"] {\n opacity: 1;\n pointer-events: auto;\n }\n\n @media (prefers-reduced-motion: no-preference) {\n [data-uiify-carousel][data-mode=\"fade\"][data-enhanced]\n > [data-part=\"viewport\"]\n > [data-part=\"slide\"] {\n transition: opacity var(--uiify-carousel-fade-duration, 300ms) ease;\n }\n }\n\n [data-uiify-virtual-scroll] {\n --uiify-virtual-scroll-row-height: 64px;\n\n overflow-y: auto;\n }\n\n [data-uiify-virtual-scroll] > [data-part=\"spacer\"] {\n position: relative;\n block-size: var(\n --uiify-virtual-scroll-total-size,\n calc(var(--uiify-virtual-scroll-count, 0) * var(--uiify-virtual-scroll-row-height))\n );\n }\n\n [data-uiify-virtual-scroll] [data-part=\"row\"] {\n --uiify-row-position: var(\n --uiify-row-offset,\n calc(\n (var(--uiify-range-start, 0) + var(--uiify-slot-index, 0)) *\n var(--uiify-virtual-scroll-row-height)\n )\n );\n\n position: absolute;\n inset-inline: 0;\n block-size: var(--uiify-row-height, var(--uiify-virtual-scroll-row-height));\n translate: 0 var(--uiify-row-position);\n }\n\n /* Horizontal: the row size runs along the inline axis. Rows sit at a logical\n inline offset so RTL needs no direction selector; child combinators keep a\n vertical list nested inside a horizontal row on the vertical rules. */\n [data-uiify-virtual-scroll][data-orientation=\"horizontal\"] {\n overflow: auto hidden;\n }\n\n [data-uiify-virtual-scroll][data-orientation=\"horizontal\"] > [data-part=\"spacer\"] {\n block-size: 100%;\n inline-size: var(\n --uiify-virtual-scroll-total-size,\n calc(var(--uiify-virtual-scroll-count, 0) * var(--uiify-virtual-scroll-row-height))\n );\n }\n\n [data-uiify-virtual-scroll][data-orientation=\"horizontal\"]\n > [data-part=\"spacer\"]\n > [data-part=\"row\"] {\n inset-block: 0;\n inset-inline: var(--uiify-row-position) auto;\n block-size: auto;\n inline-size: var(--uiify-row-height, var(--uiify-virtual-scroll-row-height));\n translate: none;\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -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":"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","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"}
|