@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.
Files changed (59) hide show
  1. package/dist/components/behavior.css +16 -1
  2. package/dist/components/behavior.css.map +1 -1
  3. package/dist/components/candle-chart/CandleChart.d.ts +10 -1
  4. package/dist/components/candle-chart/CandleChart.d.ts.map +1 -1
  5. package/dist/components/candle-chart/CandleChart.js +82 -42
  6. package/dist/components/candle-chart/CandleChart.js.map +1 -1
  7. package/dist/components/candle-chart/client/CandleChartClient.d.ts +2 -2
  8. package/dist/components/candle-chart/client/CandleChartClient.d.ts.map +1 -1
  9. package/dist/components/candle-chart/client/CandleChartClient.js +103 -14
  10. package/dist/components/candle-chart/client/CandleChartClient.js.map +1 -1
  11. package/dist/components/candle-chart/client/index.d.ts +1 -0
  12. package/dist/components/candle-chart/client/index.d.ts.map +1 -1
  13. package/dist/components/candle-chart/client/index.js.map +1 -1
  14. package/dist/components/candle-chart/types.d.ts +39 -0
  15. package/dist/components/candle-chart/types.d.ts.map +1 -1
  16. package/dist/components/internal/chart-scrollbar/use-scrollbar-reserve.d.ts +11 -0
  17. package/dist/components/internal/chart-scrollbar/use-scrollbar-reserve.d.ts.map +1 -0
  18. package/dist/components/internal/chart-scrollbar/use-scrollbar-reserve.js +21 -0
  19. package/dist/components/internal/chart-scrollbar/use-scrollbar-reserve.js.map +1 -0
  20. package/dist/components/internal/live-chart/types.d.ts +36 -0
  21. package/dist/components/internal/live-chart/types.d.ts.map +1 -0
  22. package/dist/components/internal/live-chart/types.js +1 -0
  23. package/dist/components/internal/live-chart/types.js.map +1 -0
  24. package/dist/components/internal/live-chart/use-live-chart-window.d.ts +18 -0
  25. package/dist/components/internal/live-chart/use-live-chart-window.d.ts.map +1 -0
  26. package/dist/components/internal/live-chart/use-live-chart-window.js +75 -0
  27. package/dist/components/internal/live-chart/use-live-chart-window.js.map +1 -0
  28. package/dist/components/line-chart/LineChart.d.ts +6 -1
  29. package/dist/components/line-chart/LineChart.d.ts.map +1 -1
  30. package/dist/components/line-chart/LineChart.js +82 -48
  31. package/dist/components/line-chart/LineChart.js.map +1 -1
  32. package/dist/components/line-chart/client/LineChartClient.d.ts +2 -2
  33. package/dist/components/line-chart/client/LineChartClient.d.ts.map +1 -1
  34. package/dist/components/line-chart/client/LineChartClient.js +106 -14
  35. package/dist/components/line-chart/client/LineChartClient.js.map +1 -1
  36. package/dist/components/line-chart/client/index.d.ts +1 -1
  37. package/dist/components/line-chart/client/index.d.ts.map +1 -1
  38. package/dist/components/line-chart/client/index.js.map +1 -1
  39. package/dist/components/line-chart/line-chart-geometry.d.ts +7 -0
  40. package/dist/components/line-chart/line-chart-geometry.d.ts.map +1 -1
  41. package/dist/components/line-chart/line-chart-geometry.js +14 -2
  42. package/dist/components/line-chart/line-chart-geometry.js.map +1 -1
  43. package/dist/components/line-chart/types.d.ts +42 -0
  44. package/dist/components/line-chart/types.d.ts.map +1 -1
  45. package/dist/components/virtual-scroll/VirtualScroll.d.ts.map +1 -1
  46. package/dist/components/virtual-scroll/VirtualScroll.js +40 -44
  47. package/dist/components/virtual-scroll/VirtualScroll.js.map +1 -1
  48. package/dist/components/virtual-scroll/index.d.ts +1 -1
  49. package/dist/components/virtual-scroll/index.d.ts.map +1 -1
  50. package/dist/components/virtual-scroll/index.js.map +1 -1
  51. package/dist/components/virtual-scroll/types.d.ts +5 -0
  52. package/dist/components/virtual-scroll/types.d.ts.map +1 -1
  53. package/dist/components/virtual-scroll/types.js.map +1 -1
  54. package/dist/components/virtual-scroll/virtual-scroll-axis.d.ts +9 -0
  55. package/dist/components/virtual-scroll/virtual-scroll-axis.d.ts.map +1 -0
  56. package/dist/components/virtual-scroll/virtual-scroll-axis.js +18 -0
  57. package/dist/components/virtual-scroll/virtual-scroll-axis.js.map +1 -0
  58. package/dist/styles/index.css +1 -1
  59. 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( --uiify-row-offset, calc( (var(--uiify-range-start, 0) + var(--uiify-slot-index, 0)) * var(--uiify-virtual-scroll-row-height) ) );
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,EAAyC,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAuL1F,wBAAgB,WAAW,CAAC,GAAG,SAAS,WAAW,GAAG,QAAQ,EAC5D,KAAK,EAAE,gBAAgB,CAAC,GAAG,CAAC,GAC3B,YAAY,GAAG,IAAI,CAqKrB;yBAvKe,WAAW"}
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, index, count, min, max) {
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(100 / count),
76
+ slot: percent(slot),
77
77
  wickHeight: percent(Math.max(0, low - high)),
78
78
  wickTop: percent(high),
79
- x: percent(index / count * 100)
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
- const geometry = geometryFor(candle, index, data.length, min, max);
139
- const isActive = activeCandleIndex === index;
140
- const isSelected = selectedCandleIndex === index;
141
- const handleKeyDown = (event) => {
142
- if (event.key !== "Enter" && event.key !== " ") return;
143
- event.preventDefault();
144
- onCandleClick?.(index);
145
- };
146
- return /* @__PURE__ */ jsx(
147
- "li",
148
- {
149
- "aria-hidden": interactive ? void 0 : true,
150
- "data-direction": geometry.direction,
151
- "data-crosshair-active": interactive && isActive ? "" : void 0,
152
- "data-time": String(candle.time),
153
- style: candleStyle(geometry, isActive ? cursorY : void 0),
154
- children: interactive ? /* @__PURE__ */ jsx(
155
- "button",
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
- /* @__PURE__ */ jsx("ol", { "data-part": "candles", "aria-label": `${label} candles`, children: candles }),
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, data.length, cursorY),
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, data.length, void 0),
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 { CandleChartProps } from "../types.js";
3
- export declare function CandleChartClient<TAs extends ElementType = "figure">(props: CandleChartProps<TAs>): ReactElement | null;
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,EAA8B,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAkBhF,wBAAgB,iBAAiB,CAAC,GAAG,SAAS,WAAW,GAAG,QAAQ,EAClE,KAAK,EAAE,gBAAgB,CAAC,GAAG,CAAC,GAC3B,YAAY,GAAG,IAAI,CA2DrB;yBA7De,iBAAiB"}
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 viewport = useChartViewport({
19
- itemCount: data.length,
20
- measuredWidthOffset: Y_AXIS_COLUMN_WIDTH,
21
- minItemWidth: 24
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
- activeCandleIndex: cursor?.index,
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
- style
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 { CandleChartPointerPosition, CandleChartProps } from \"../types.js\";\nimport { useChartViewport } from \"../../../hooks/use-chart-viewport.js\";\nimport { useMergedRefs } from \"../../../hooks/use-merged-refs.js\";\n\ninterface InteractiveCandleStyle extends CSSProperties {\n \"--candle-scale\": string;\n}\n\ninterface CursorState extends CandleChartPointerPosition {}\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;\n\nexport function CandleChartClient<TAs extends ElementType = \"figure\">(\n props: CandleChartProps<TAs>,\n): ReactElement | null {\n const {\n data,\n ref: consumerRef,\n style: consumerStyle,\n onCandleClick: consumerOnCandleClick,\n onCandlePointerLeave: consumerOnCandlePointerLeave,\n onCandlePointerMove: consumerOnCandlePointerMove,\n ...consumerProps\n } = props as CandleChartProps<\"figure\"> & {\n ref?: Ref<HTMLElement>;\n style?: CSSProperties;\n };\n const viewport = useChartViewport({\n itemCount: data.length,\n measuredWidthOffset: Y_AXIS_COLUMN_WIDTH,\n minItemWidth: 24,\n });\n const [cursor, setCursor] = useState<CursorState | null>(null);\n const [selectedCandleIndex, setSelectedCandleIndex] = useState<number | undefined>();\n const chartRef = useMergedRefs<HTMLElement>(viewport.ref, consumerRef);\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 style: InteractiveCandleStyle = {\n ...consumerStyle,\n \"--candle-scale\": String(viewport.scale),\n };\n\n const chartProps = {\n ...consumerProps,\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\n return <CandleChart {...chartProps} />;\n}\nCandleChartClient.displayName = \"CandleChartClient\";\n"],"mappings":";AAmFS;AAjFT,SAAS,aAAa,gBAAgB;AAEtC,SAAS,mBAAmB;AAE5B,SAAS,wBAAwB;AACjC,SAAS,qBAAqB;AAc9B,MAAM,sBAAsB,KAAK;AAE1B,SAAS,kBACd,OACqB;AACrB,QAAM;AAAA,IACJ;AAAA,IACA,KAAK;AAAA,IACL,OAAO;AAAA,IACP,eAAe;AAAA,IACf,sBAAsB;AAAA,IACtB,qBAAqB;AAAA,IACrB,GAAG;AAAA,EACL,IAAI;AAIJ,QAAM,WAAW,iBAAiB;AAAA,IAChC,WAAW,KAAK;AAAA,IAChB,qBAAqB;AAAA,IACrB,cAAc;AAAA,EAChB,CAAC;AACD,QAAM,CAAC,QAAQ,SAAS,IAAI,SAA6B,IAAI;AAC7D,QAAM,CAAC,qBAAqB,sBAAsB,IAAI,SAA6B;AACnF,QAAM,WAAW,cAA2B,SAAS,KAAK,WAAW;AACrE,QAAM,oBAAoB;AAAA,IACxB,CAAC,UAAkB;AACjB,6BAAuB,KAAK;AAC5B,8BAAwB,KAAK;AAAA,IAC/B;AAAA,IACA,CAAC,qBAAqB;AAAA,EACxB;AACA,QAAM,2BAA2B,YAAY,MAAM;AACjD,cAAU,IAAI;AACd,mCAA+B;AAAA,EACjC,GAAG,CAAC,4BAA4B,CAAC;AACjC,QAAM,0BAA0B;AAAA,IAC9B,CAAC,aAAyC;AACxC,gBAAU,QAAQ;AAClB,oCAA8B,QAAQ;AAAA,IACxC;AAAA,IACA,CAAC,2BAA2B;AAAA,EAC9B;AACA,QAAM,QAAgC;AAAA,IACpC,GAAG;AAAA,IACH,kBAAkB,OAAO,SAAS,KAAK;AAAA,EACzC;AAEA,QAAM,aAAa;AAAA,IACjB,GAAG;AAAA,IACH;AAAA,IACA,mBAAmB,QAAQ;AAAA,IAC3B,SAAS,QAAQ;AAAA,IACjB,aAAa;AAAA,IACb,eAAe;AAAA,IACf,sBAAsB;AAAA,IACtB,qBAAqB;AAAA,IACrB,KAAK;AAAA,IACL;AAAA,IACA;AAAA,EACF;AAEA,SAAO,oBAAC,eAAa,GAAG,YAAY;AACtC;AACA,kBAAkB,cAAc;","names":[]}
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,2 +1,3 @@
1
1
  export { CandleChartClient } from "./CandleChartClient.js";
2
+ export type { CandleChartClientOwnProps, CandleChartClientProps } from "../types.js";
2
3
  //# sourceMappingURL=index.d.ts.map
@@ -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"}