@opendata-ai/openchart-vanilla 6.25.3 → 6.26.0
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/index.d.ts +54 -2
- package/dist/index.js +646 -30
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +3 -3
- package/src/__tests__/compound-labels.test.ts +122 -0
- package/src/__tests__/crosshair.test.ts +121 -0
- package/src/__tests__/tilemap.test.ts +158 -0
- package/src/graph-mount.ts +1 -1
- package/src/index.ts +3 -0
- package/src/mount.ts +25 -4
- package/src/renderers/axes.ts +81 -20
- package/src/renderers/legend.ts +6 -2
- package/src/sankey-renderer.ts +4 -2
- package/src/svg-renderer.ts +21 -1
- package/src/tilemap-mount.ts +394 -0
- package/src/tilemap-renderer.ts +425 -0
package/dist/styles.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.oc-root,.oc-chart-root,.oc-table-wrapper,.oc-table-root,.oc-graph-wrapper,.oc-graph-root,.oc-sankey-root{--oc-font-family:Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;--oc-font-mono:"JetBrains Mono", "Fira Code", "Cascadia Code", monospace;--oc-ease-smooth:linear(0, .157, .438, .64, .766, .85, .906, .941, .964, .978, .988, .994, .998, 1);--oc-ease-snappy:linear(0, .012, .048, .108, .194, .302, .426, .559, .69, .808, .905, .973, 1.013, 1.028, 1.023, 1.006, .984, .966, .957, .957, .964, .975, .986, .995, 1, 1.003, 1.002, 1, .998, .998, .999, 1);--oc-animation-duration:.5s;--oc-animation-stagger:80ms;--oc-annotation-delay:.2s;--oc-title-size:22px;--oc-title-weight:700;--oc-title-tracking:-.02em;--oc-subtitle-size:14px;--oc-subtitle-weight:400;--oc-source-size:12px;--oc-source-weight:400;--oc-body-size:13px;--oc-bg:#fff;--oc-text:#1d1d1d;--oc-text-secondary:#5c5c5c;--oc-text-muted:#999;--oc-gridline:#e8e8e8;--oc-axis:#888;--oc-border:#e2e2e2;--oc-border-radius:4px;--oc-focus:#3b82f6;--oc-hover-bg:rgba(0,0,0,.024);--oc-tooltip-bg:rgba(255,255,255,.88);--oc-tooltip-border:rgba(0,0,0,.08);--oc-tooltip-shadow:0 2px 8px rgba(0,0,0,.08), 0 0 1px rgba(0,0,0,.12);--oc-tooltip-text:#1d1d1d;--oc-legend-text:#555}.oc-dark{--oc-bg:#1a1a2e;--oc-text:#e0e0e0;--oc-text-secondary:#b0b0b0;--oc-text-muted:gray;--oc-gridline:#335;--oc-axis:#999;--oc-border:#446;--oc-focus:#60a5fa;--oc-hover-bg:rgba(255,255,255,.05);--oc-tooltip-bg:rgba(30,30,50,.85);--oc-tooltip-border:rgba(255,255,255,.08);--oc-tooltip-shadow:0 2px 8px rgba(0,0,0,.3), 0 0 1px rgba(0,0,0,.4);--oc-tooltip-text:#e0e0e0;--oc-legend-text:#b0b0b0}.oc-chart-root{width:100%}.oc-table-root,.oc-graph-root,.oc-sankey-root{width:100%;height:100%}.oc-table-root{overflow:auto}.oc-chart{font-family:var(--oc-font-family);width:100%;display:block}.oc-sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.oc-editable-hover{outline-offset:2px;border-radius:2px;outline:1.5px solid rgba(79,70,229,.35)}.oc-chrome{font-family:var(--oc-font-family)}.oc-title{font-size:var(--oc-title-size);font-weight:var(--oc-title-weight);letter-spacing:var(--oc-title-tracking);fill:var(--oc-text)}.oc-subtitle{font-size:var(--oc-subtitle-size);font-weight:var(--oc-subtitle-weight);fill:var(--oc-text-secondary)}.oc-source,.oc-byline,.oc-footer{font-size:var(--oc-source-size);font-weight:var(--oc-source-weight);fill:var(--oc-text-muted)}.oc-chrome-footer{padding-top:16px}.oc-tooltip{pointer-events:none;z-index:1000;background:var(--oc-tooltip-bg);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border:1px solid var(--oc-tooltip-border);box-shadow:var(--oc-tooltip-shadow);color:var(--oc-tooltip-text);font-family:var(--oc-font-family);border-radius:8px;min-width:140px;max-width:260px;padding:0;font-size:12px;line-height:1.4;animation:.12s ease-out oc-tooltip-in;display:none;position:absolute}.oc-tooltip .oc-tooltip-header{align-items:center;gap:6px;padding:8px 12px 6px;display:flex}.oc-tooltip .oc-tooltip-dot{border-radius:50%;flex-shrink:0;width:8px;height:8px}.oc-tooltip .oc-tooltip-title{letter-spacing:-.01em;color:var(--oc-tooltip-text);white-space:nowrap;text-overflow:ellipsis;font-size:12px;font-weight:600;overflow:hidden}.oc-tooltip .oc-tooltip-body{border-top:1px solid var(--oc-tooltip-border);padding:4px 12px 8px}.oc-tooltip .oc-tooltip-header+.oc-tooltip-body{padding-top:6px}.oc-tooltip .oc-tooltip-body:first-child{border-top:none;padding-top:8px}.oc-tooltip .oc-tooltip-row{justify-content:space-between;align-items:baseline;gap:12px;padding:1px 0;display:flex}.oc-tooltip .oc-tooltip-label{color:var(--oc-text-muted);white-space:nowrap;flex-shrink:0;font-size:11px}.oc-tooltip .oc-tooltip-value{font-variant-numeric:tabular-nums;text-align:right;text-overflow:ellipsis;white-space:nowrap;font-size:11px;font-weight:500;overflow:hidden}.oc-legend{font-family:var(--oc-font-family);font-size:var(--oc-body-size)}.oc-legend-entry{cursor:default}.oc-legend text{fill:var(--oc-legend-text)}.oc-table-wrapper{font-family:var(--oc-font-family);color:var(--oc-text);background:var(--oc-bg)}.oc-table-wrapper>.oc-chrome{margin-bottom:16px;padding-left:16px;padding-right:16px}.oc-table-wrapper>.oc-chrome:first-child{padding-top:4px}.oc-table-wrapper table{border-collapse:collapse;width:100%}.oc-table-wrapper th{text-align:left;border-bottom:1px solid var(--oc-border);padding:10px 16px}.oc-table-wrapper td{text-align:left;border-bottom:1px solid var(--oc-border);padding:10px 16px}.oc-table-wrapper th{text-transform:uppercase;letter-spacing:.05em;color:var(--oc-text-secondary);white-space:nowrap;font-size:12px;font-weight:600}.oc-table-wrapper thead{z-index:2;background:var(--oc-bg);position:sticky;top:0}.oc-table-wrapper thead th{border-bottom-width:2px}.oc-table-wrapper td{font-variant-numeric:tabular-nums;font-size:14px}.oc-table-wrapper th:focus{outline:2px solid var(--oc-focus);outline-offset:-2px}.oc-table-wrapper tbody:focus{outline:none}.oc-table-title{font-size:var(--oc-title-computed-size,var(--oc-title-size));font-weight:var(--oc-title-computed-weight,var(--oc-title-weight));color:var(--oc-title-computed-color,var(--oc-text));margin-bottom:4px}.oc-table-subtitle{font-size:var(--oc-subtitle-computed-size,var(--oc-subtitle-size));font-weight:var(--oc-subtitle-computed-weight,var(--oc-subtitle-weight));color:var(--oc-subtitle-computed-color,var(--oc-text-secondary));margin-bottom:8px}.oc-table-source{font-size:var(--oc-source-computed-size,var(--oc-source-size));color:var(--oc-source-computed-color,var(--oc-text-muted))}.oc-table-footer-text{font-size:var(--oc-footer-computed-size,var(--oc-source-size));color:var(--oc-footer-computed-color,var(--oc-text-muted))}.oc-table-scroll{overflow-x:auto}.oc-table--sticky th:first-child{z-index:1;background:var(--oc-bg);position:sticky;left:0}.oc-table--sticky td:first-child{z-index:1;background:var(--oc-bg);position:sticky;left:0}.oc-table-sort-btn{cursor:pointer;vertical-align:middle;background:0 0;border:none;flex-direction:column;align-items:center;gap:2px;margin-left:6px;padding:2px;display:inline-flex}.oc-table-sort-btn:before{content:"";border-left:5px solid transparent;border-right:5px solid transparent;width:0;height:0;transition:opacity .15s,border-color .15s;display:block}.oc-table-sort-btn:after{content:"";border-left:5px solid transparent;border-right:5px solid transparent;width:0;height:0;transition:opacity .15s,border-color .15s;display:block}.oc-table-sort-btn:before{border-bottom:4.5px solid var(--oc-text-secondary);opacity:.45}.oc-table-sort-btn:after{border-top:4.5px solid var(--oc-text-secondary);opacity:.45}.oc-table-sort-btn:hover:before{opacity:.75}.oc-table-sort-btn:hover:after{opacity:.75}th[aria-sort=ascending] .oc-table-sort-btn:before{opacity:1;border-bottom-color:var(--oc-text)}th[aria-sort=ascending] .oc-table-sort-btn:after{opacity:.15}th[aria-sort=descending] .oc-table-sort-btn:after{opacity:1;border-top-color:var(--oc-text)}th[aria-sort=descending] .oc-table-sort-btn:before{opacity:.15}.oc-table-search{padding:8px 0}.oc-table-search input{border:1px solid var(--oc-border);background:var(--oc-bg);width:100%;color:var(--oc-text);box-sizing:border-box;border-radius:6px;padding:8px 12px;font-family:inherit;font-size:13px;transition:border-color .15s}.oc-table-search input::-ms-input-placeholder{color:var(--oc-text-muted);font-size:13px}.oc-table-search input::placeholder{color:var(--oc-text-muted);font-size:13px}.oc-table-search input:focus{border-color:var(--oc-focus);outline:none;box-shadow:0 0 0 3px rgba(59,130,246,.1)}.oc-table-pagination{color:var(--oc-text-secondary);justify-content:space-between;align-items:center;padding:12px 0 4px;font-size:13px;display:flex}.oc-table-pagination button{border:1px solid var(--oc-border);background:var(--oc-bg);color:var(--oc-text);cursor:pointer;border-radius:6px;padding:6px 14px;font-family:inherit;font-size:13px;transition:background .15s,border-color .15s}.oc-table-pagination button:disabled{opacity:.35;cursor:not-allowed}.oc-table-pagination button:hover:not(:disabled){background:var(--oc-hover-bg);border-color:var(--oc-axis)}.oc-table-pagination button:focus-visible{outline:2px solid var(--oc-focus);outline-offset:1px}.oc-table-pagination-info{font-variant-numeric:tabular-nums}.oc-table-pagination-btns{gap:8px;display:flex}.oc-table-bar{position:relative}.oc-table-bar-fill{opacity:.15;pointer-events:none;border-radius:2px;position:absolute;top:6px;bottom:6px;left:0}.oc-table-bar-value{z-index:1;position:relative}.oc-table-sparkline{width:100%;display:block;position:relative}.oc-table-sparkline svg{width:100%;display:block;overflow:visible}.oc-table-sparkline-dot{border-radius:50%;width:5px;height:5px;position:absolute}.oc-table-sparkline-labels{justify-content:space-between;font-size:11px;line-height:1;display:flex}.oc-table-image{vertical-align:middle;display:inline-block}.oc-table-image img{object-fit:cover}.oc-table-image-rounded img{border-radius:50%}.oc-table-flag{font-size:1.2em}.oc-table--compact th{padding:4px 8px;font-size:13px}.oc-table--compact td{padding:4px 8px;font-size:13px}.oc-table--compact th{font-size:11px}.oc-table--clickable tbody tr{cursor:pointer}.oc-table--clickable tbody tr:hover{background:var(--oc-hover-bg)}.oc-table-cell-focus{outline:2px solid var(--oc-focus);outline-offset:-2px}.oc-table-empty{text-align:center;color:var(--oc-text-secondary);padding:32px 16px;font-size:14px;font-style:italic}.oc-table-wrapper.oc-animate>.oc-chrome{animation:oc-enter-fade calc(var(--oc-animation-duration) * .6) var(--oc-animation-ease,var(--oc-ease-smooth)) both}.oc-table-wrapper.oc-animate thead{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .4) var(--oc-animation-ease,var(--oc-ease-smooth)) both}.oc-table-wrapper.oc-animate tbody tr{animation:oc-table-enter-row var(--oc-animation-duration) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-row-index,0))}.oc-table-wrapper.oc-animate tbody td{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .5) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-row-index,0))}.oc-table-wrapper.oc-animate td.oc-table-heatmap{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .7) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-row-index,0) + var(--oc-animation-duration) * .3)}.oc-table-wrapper.oc-animate td.oc-table-category{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .7) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-row-index,0) + var(--oc-animation-duration) * .3)}.oc-table-wrapper.oc-animate .oc-table-bar-fill{animation:oc-table-enter-bar-fill calc(var(--oc-animation-duration) * .8) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-row-index,0) + var(--oc-animation-duration) * .3)}.oc-table-wrapper.oc-animate .oc-table-sparkline>svg{animation:oc-enter-line calc(var(--oc-animation-duration) * .8) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-row-index,0) + var(--oc-animation-duration) * .4)}.oc-table-wrapper.oc-animate .oc-table-sparkline-dot{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .3) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-row-index,0) + var(--oc-animation-duration) * .8)}.oc-table-wrapper.oc-animate .oc-table-sparkline-labels{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .3) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-row-index,0) + var(--oc-animation-duration) * .8)}.oc-table-wrapper.oc-animate .oc-table-search{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .5) var(--oc-animation-ease,var(--oc-ease-smooth)) both}.oc-table-wrapper.oc-animate .oc-table-pagination{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .5) var(--oc-animation-ease,var(--oc-ease-smooth)) both}.oc-graph-wrapper{background:var(--oc-bg);font-family:var(--oc-font-family);width:100%;height:100%;position:relative;overflow:hidden}.oc-graph-canvas{cursor:grab;width:100%;height:100%;display:block}.oc-graph-canvas--dragging{cursor:grabbing}.oc-graph-chrome{z-index:2;pointer-events:none;padding:16px 16px 8px;position:absolute;top:0;left:0;right:0}.oc-graph-chrome .oc-title{font-size:var(--oc-title-size);font-weight:var(--oc-title-weight);letter-spacing:var(--oc-title-tracking);color:var(--oc-text);--_stroke:color-mix(in srgb, var(--oc-bg) 80%, transparent);text-shadow:-2px -2px 0 var(--_stroke), 2px -2px 0 var(--_stroke), -2px 2px 0 var(--_stroke), 2px 2px 0 var(--_stroke), 0 -2px 0 var(--_stroke), 0 2px 0 var(--_stroke), -2px 0 0 var(--_stroke), 2px 0 0 var(--_stroke);margin:0 0 4px}.oc-graph-chrome .oc-subtitle{font-size:var(--oc-subtitle-size);color:var(--oc-text-secondary);--_stroke:color-mix(in srgb, var(--oc-bg) 80%, transparent);text-shadow:-1px -1px 0 var(--_stroke), 1px -1px 0 var(--_stroke), -1px 1px 0 var(--_stroke), 1px 1px 0 var(--_stroke);margin:0}.oc-graph-legend{background:var(--oc-bg);border:1px solid var(--oc-border);border-radius:var(--oc-border-radius);color:var(--oc-text-secondary);max-height:200px;padding:8px 12px;font-size:12px;position:absolute;top:8px;right:8px;overflow-y:auto}.oc-graph-legend-item{align-items:center;gap:6px;padding:2px 0;display:flex}.oc-graph-legend-swatch{border-radius:50%;flex-shrink:0;width:10px;height:10px}.oc-graph-search{position:absolute;top:8px;left:8px}.oc-graph-search input{font-family:var(--oc-font-family);font-size:var(--oc-body-size);border:1px solid var(--oc-border);border-radius:var(--oc-border-radius);background:var(--oc-bg);color:var(--oc-text);outline:none;padding:6px 10px}.oc-graph-search input:focus{border-color:var(--oc-focus);box-shadow:0 0 0 2px rgba(59,130,246,.25)}.oc-dark .oc-graph-wrapper,.oc-graph-wrapper.oc-dark{--oc-bg:#0d1117}.oc-dark .oc-graph-legend,.oc-dark.oc-graph-wrapper .oc-graph-legend,.oc-dark .oc-graph-search input{background:rgba(13,17,23,.85);border-color:rgba(255,255,255,.1)}@keyframes oc-enter-bar{0%{clip-path:inset(100% 0 0);opacity:0}75%{opacity:1}to{clip-path:inset(0);opacity:1}}@keyframes oc-enter-bar-h{0%{clip-path:inset(0 100% 0 0);opacity:0}75%{opacity:1}to{clip-path:inset(0);opacity:1}}@keyframes oc-enter-line{0%{clip-path:inset(0 100% 0 0);opacity:0}15%{opacity:1}to{clip-path:inset(0);opacity:1}}@keyframes oc-enter-point{0%{opacity:0;transform:scale(.3)}to{opacity:1;transform:scale(1)}}@keyframes oc-enter-fade-only{0%{opacity:0}to{opacity:1}}@keyframes oc-enter-fade{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}@keyframes oc-table-enter-row{0%{transform:translateY(6px)}to{transform:translateY(0)}}@keyframes oc-table-enter-bar-fill{0%{clip-path:inset(0 100% 0 0)}to{clip-path:inset(0)}}@keyframes oc-tooltip-in{0%{opacity:0;transform:translateY(2px)}to{opacity:1;transform:translateY(0)}}.oc-animate .oc-mark-rect rect{animation:oc-enter-bar var(--oc-animation-duration) var(--oc-ease-smooth) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-mark-bar rect{animation:oc-enter-bar var(--oc-animation-duration) var(--oc-ease-smooth) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-mark-rect[data-orient=horizontal] rect{animation-name:oc-enter-bar-h}.oc-animate .oc-mark-bar[data-orient=horizontal] rect{animation-name:oc-enter-bar-h}.oc-animate .oc-mark-rect[data-stack-pos] rect{animation-duration:var(--oc-stack-segment-duration,.15s);animation-timing-function:linear;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0) + var(--oc-stack-pos,0) * var(--oc-stack-segment-duration,.15s))}.oc-animate .oc-mark-line{animation:oc-enter-line var(--oc-animation-duration) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-mark-area{animation:oc-enter-line var(--oc-animation-duration) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-mark-arc{animation:oc-enter-fade-only var(--oc-animation-duration) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate circle.oc-mark-point{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .4) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate circle.oc-mark-circle{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .4) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-mark-line~circle.oc-mark-point{animation-delay:calc(var(--oc-animation-duration) * .35 + var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-mark-area~circle.oc-mark-point{animation-delay:calc(var(--oc-animation-duration) * .35 + var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-mark-text text{animation:oc-enter-fade calc(var(--oc-animation-duration) * .6) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-mark-rule line{animation:oc-enter-fade calc(var(--oc-animation-duration) * .5) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-mark-tick line{animation:oc-enter-fade calc(var(--oc-animation-duration) * .5) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-mark-label{animation:oc-enter-fade .3s var(--oc-ease-smooth) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0) + var(--oc-animation-duration) * .7)}.oc-animate .oc-annotation{animation:oc-enter-fade .4s var(--oc-ease-smooth) both;animation-delay:calc(var(--oc-animation-duration) + var(--oc-annotation-delay,.2s))}.oc-animate .oc-sankey-node rect{animation:oc-enter-fade-only var(--oc-animation-duration) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-sankey-link path{animation:oc-enter-fade-only var(--oc-animation-duration) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-duration) * .3 + var(--oc-animation-stagger) * var(--oc-mark-index,0))}@media (prefers-reduced-motion:reduce){.oc-table-sort-btn:before,.oc-table-sort-btn:after,.oc-table-search input,.oc-table-pagination button{transition:none}.oc-animate .oc-mark-rect rect,.oc-animate .oc-mark-bar rect,.oc-animate .oc-mark-arc,.oc-animate .oc-mark-line,.oc-animate .oc-mark-area,.oc-animate circle.oc-mark-point,.oc-animate circle.oc-mark-circle,.oc-animate .oc-mark-text text,.oc-animate .oc-mark-rule line,.oc-animate .oc-mark-tick line,.oc-animate .oc-mark-label,.oc-animate .oc-annotation,.oc-animate .oc-sankey-node rect,.oc-animate .oc-sankey-link path,.oc-table-wrapper.oc-animate>.oc-chrome,.oc-table-wrapper.oc-animate thead,.oc-table-wrapper.oc-animate tbody tr,.oc-table-wrapper.oc-animate tbody td,.oc-table-wrapper.oc-animate td.oc-table-heatmap,.oc-table-wrapper.oc-animate td.oc-table-category,.oc-table-wrapper.oc-animate .oc-table-bar-fill,.oc-table-wrapper.oc-animate .oc-table-sparkline>svg,.oc-table-wrapper.oc-animate .oc-table-sparkline-dot,.oc-table-wrapper.oc-animate .oc-table-sparkline-labels,.oc-table-wrapper.oc-animate .oc-table-search,.oc-table-wrapper.oc-animate .oc-table-pagination{animation:none}}
|
|
1
|
+
.oc-root,.oc-chart-root,.oc-table-wrapper,.oc-table-root,.oc-graph-wrapper,.oc-graph-root,.oc-sankey-root,.oc-tilemap-root{--oc-font-family:Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;--oc-font-mono:"JetBrains Mono", "Fira Code", "Cascadia Code", monospace;--oc-ease-smooth:linear(0, .157, .438, .64, .766, .85, .906, .941, .964, .978, .988, .994, .998, 1);--oc-ease-snappy:linear(0, .012, .048, .108, .194, .302, .426, .559, .69, .808, .905, .973, 1.013, 1.028, 1.023, 1.006, .984, .966, .957, .957, .964, .975, .986, .995, 1, 1.003, 1.002, 1, .998, .998, .999, 1);--oc-animation-duration:.5s;--oc-animation-stagger:80ms;--oc-annotation-delay:.2s;--oc-title-size:22px;--oc-title-weight:700;--oc-title-tracking:-.02em;--oc-subtitle-size:14px;--oc-subtitle-weight:400;--oc-source-size:12px;--oc-source-weight:400;--oc-body-size:13px;--oc-bg:#fff;--oc-text:#1d1d1d;--oc-text-secondary:#5c5c5c;--oc-text-muted:#999;--oc-gridline:#e8e8e8;--oc-axis:#888;--oc-border:#e2e2e2;--oc-border-radius:4px;--oc-focus:#3b82f6;--oc-hover-bg:rgba(0,0,0,.024);--oc-tooltip-bg:rgba(255,255,255,.88);--oc-tooltip-border:rgba(0,0,0,.08);--oc-tooltip-shadow:0 2px 8px rgba(0,0,0,.08), 0 0 1px rgba(0,0,0,.12);--oc-tooltip-text:#1d1d1d;--oc-legend-text:#555}.oc-dark{--oc-bg:#1a1a2e;--oc-text:#e0e0e0;--oc-text-secondary:#b0b0b0;--oc-text-muted:gray;--oc-gridline:#335;--oc-axis:#999;--oc-border:#446;--oc-focus:#60a5fa;--oc-hover-bg:rgba(255,255,255,.05);--oc-tooltip-bg:rgba(30,30,50,.85);--oc-tooltip-border:rgba(255,255,255,.08);--oc-tooltip-shadow:0 2px 8px rgba(0,0,0,.3), 0 0 1px rgba(0,0,0,.4);--oc-tooltip-text:#e0e0e0;--oc-legend-text:#b0b0b0}.oc-chart-root{width:100%}.oc-table-root,.oc-graph-root,.oc-sankey-root,.oc-tilemap-root{width:100%;height:100%}.oc-table-root{overflow:auto}.oc-chart{font-family:var(--oc-font-family);width:100%;display:block}.oc-sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.oc-editable-hover{outline-offset:2px;border-radius:2px;outline:1.5px solid rgba(79,70,229,.35)}.oc-chrome{font-family:var(--oc-font-family)}.oc-title{font-size:var(--oc-title-size);font-weight:var(--oc-title-weight);letter-spacing:var(--oc-title-tracking);fill:var(--oc-text)}.oc-subtitle{font-size:var(--oc-subtitle-size);font-weight:var(--oc-subtitle-weight);fill:var(--oc-text-secondary)}.oc-source,.oc-byline,.oc-footer{font-size:var(--oc-source-size);font-weight:var(--oc-source-weight);fill:var(--oc-text-muted)}.oc-chrome-footer{padding-top:16px}.oc-tooltip{pointer-events:none;z-index:1000;background:var(--oc-tooltip-bg);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border:1px solid var(--oc-tooltip-border);box-shadow:var(--oc-tooltip-shadow);color:var(--oc-tooltip-text);font-family:var(--oc-font-family);border-radius:8px;min-width:140px;max-width:260px;padding:0;font-size:12px;line-height:1.4;animation:.12s ease-out oc-tooltip-in;display:none;position:absolute}.oc-tooltip .oc-tooltip-header{align-items:center;gap:6px;padding:8px 12px 6px;display:flex}.oc-tooltip .oc-tooltip-dot{border-radius:50%;flex-shrink:0;width:8px;height:8px}.oc-tooltip .oc-tooltip-title{letter-spacing:-.01em;color:var(--oc-tooltip-text);white-space:nowrap;text-overflow:ellipsis;font-size:12px;font-weight:600;overflow:hidden}.oc-tooltip .oc-tooltip-body{border-top:1px solid var(--oc-tooltip-border);padding:4px 12px 8px}.oc-tooltip .oc-tooltip-header+.oc-tooltip-body{padding-top:6px}.oc-tooltip .oc-tooltip-body:first-child{border-top:none;padding-top:8px}.oc-tooltip .oc-tooltip-row{justify-content:space-between;align-items:baseline;gap:12px;padding:1px 0;display:flex}.oc-tooltip .oc-tooltip-label{color:var(--oc-text-muted);white-space:nowrap;flex-shrink:0;font-size:11px}.oc-tooltip .oc-tooltip-value{font-variant-numeric:tabular-nums;text-align:right;text-overflow:ellipsis;white-space:nowrap;font-size:11px;font-weight:500;overflow:hidden}.oc-legend{font-family:var(--oc-font-family);font-size:var(--oc-body-size)}.oc-legend-entry{cursor:default}.oc-legend text{fill:var(--oc-legend-text)}.oc-table-wrapper{font-family:var(--oc-font-family);color:var(--oc-text);background:var(--oc-bg)}.oc-table-wrapper>.oc-chrome{margin-bottom:16px;padding-left:16px;padding-right:16px}.oc-table-wrapper>.oc-chrome:first-child{padding-top:4px}.oc-table-wrapper table{border-collapse:collapse;width:100%}.oc-table-wrapper th{text-align:left;border-bottom:1px solid var(--oc-border);padding:10px 16px}.oc-table-wrapper td{text-align:left;border-bottom:1px solid var(--oc-border);padding:10px 16px}.oc-table-wrapper th{text-transform:uppercase;letter-spacing:.05em;color:var(--oc-text-secondary);white-space:nowrap;font-size:12px;font-weight:600}.oc-table-wrapper thead{z-index:2;background:var(--oc-bg);position:sticky;top:0}.oc-table-wrapper thead th{border-bottom-width:2px}.oc-table-wrapper td{font-variant-numeric:tabular-nums;font-size:14px}.oc-table-wrapper th:focus{outline:2px solid var(--oc-focus);outline-offset:-2px}.oc-table-wrapper tbody:focus{outline:none}.oc-table-title{font-size:var(--oc-title-computed-size,var(--oc-title-size));font-weight:var(--oc-title-computed-weight,var(--oc-title-weight));color:var(--oc-title-computed-color,var(--oc-text));margin-bottom:4px}.oc-table-subtitle{font-size:var(--oc-subtitle-computed-size,var(--oc-subtitle-size));font-weight:var(--oc-subtitle-computed-weight,var(--oc-subtitle-weight));color:var(--oc-subtitle-computed-color,var(--oc-text-secondary));margin-bottom:8px}.oc-table-source{font-size:var(--oc-source-computed-size,var(--oc-source-size));color:var(--oc-source-computed-color,var(--oc-text-muted))}.oc-table-footer-text{font-size:var(--oc-footer-computed-size,var(--oc-source-size));color:var(--oc-footer-computed-color,var(--oc-text-muted))}.oc-table-scroll{overflow-x:auto}.oc-table--sticky th:first-child{z-index:1;background:var(--oc-bg);position:sticky;left:0}.oc-table--sticky td:first-child{z-index:1;background:var(--oc-bg);position:sticky;left:0}.oc-table-sort-btn{cursor:pointer;vertical-align:middle;background:0 0;border:none;flex-direction:column;align-items:center;gap:2px;margin-left:6px;padding:2px;display:inline-flex}.oc-table-sort-btn:before{content:"";border-left:5px solid transparent;border-right:5px solid transparent;width:0;height:0;transition:opacity .15s,border-color .15s;display:block}.oc-table-sort-btn:after{content:"";border-left:5px solid transparent;border-right:5px solid transparent;width:0;height:0;transition:opacity .15s,border-color .15s;display:block}.oc-table-sort-btn:before{border-bottom:4.5px solid var(--oc-text-secondary);opacity:.45}.oc-table-sort-btn:after{border-top:4.5px solid var(--oc-text-secondary);opacity:.45}.oc-table-sort-btn:hover:before{opacity:.75}.oc-table-sort-btn:hover:after{opacity:.75}th[aria-sort=ascending] .oc-table-sort-btn:before{opacity:1;border-bottom-color:var(--oc-text)}th[aria-sort=ascending] .oc-table-sort-btn:after{opacity:.15}th[aria-sort=descending] .oc-table-sort-btn:after{opacity:1;border-top-color:var(--oc-text)}th[aria-sort=descending] .oc-table-sort-btn:before{opacity:.15}.oc-table-search{padding:8px 0}.oc-table-search input{border:1px solid var(--oc-border);background:var(--oc-bg);width:100%;color:var(--oc-text);box-sizing:border-box;border-radius:6px;padding:8px 12px;font-family:inherit;font-size:13px;transition:border-color .15s}.oc-table-search input::-ms-input-placeholder{color:var(--oc-text-muted);font-size:13px}.oc-table-search input::placeholder{color:var(--oc-text-muted);font-size:13px}.oc-table-search input:focus{border-color:var(--oc-focus);outline:none;box-shadow:0 0 0 3px rgba(59,130,246,.1)}.oc-table-pagination{color:var(--oc-text-secondary);justify-content:space-between;align-items:center;padding:12px 0 4px;font-size:13px;display:flex}.oc-table-pagination button{border:1px solid var(--oc-border);background:var(--oc-bg);color:var(--oc-text);cursor:pointer;border-radius:6px;padding:6px 14px;font-family:inherit;font-size:13px;transition:background .15s,border-color .15s}.oc-table-pagination button:disabled{opacity:.35;cursor:not-allowed}.oc-table-pagination button:hover:not(:disabled){background:var(--oc-hover-bg);border-color:var(--oc-axis)}.oc-table-pagination button:focus-visible{outline:2px solid var(--oc-focus);outline-offset:1px}.oc-table-pagination-info{font-variant-numeric:tabular-nums}.oc-table-pagination-btns{gap:8px;display:flex}.oc-table-bar{position:relative}.oc-table-bar-fill{opacity:.15;pointer-events:none;border-radius:2px;position:absolute;top:6px;bottom:6px;left:0}.oc-table-bar-value{z-index:1;position:relative}.oc-table-sparkline{width:100%;display:block;position:relative}.oc-table-sparkline svg{width:100%;display:block;overflow:visible}.oc-table-sparkline-dot{border-radius:50%;width:5px;height:5px;position:absolute}.oc-table-sparkline-labels{justify-content:space-between;font-size:11px;line-height:1;display:flex}.oc-table-image{vertical-align:middle;display:inline-block}.oc-table-image img{object-fit:cover}.oc-table-image-rounded img{border-radius:50%}.oc-table-flag{font-size:1.2em}.oc-table--compact th{padding:4px 8px;font-size:13px}.oc-table--compact td{padding:4px 8px;font-size:13px}.oc-table--compact th{font-size:11px}.oc-table--clickable tbody tr{cursor:pointer}.oc-table--clickable tbody tr:hover{background:var(--oc-hover-bg)}.oc-table-cell-focus{outline:2px solid var(--oc-focus);outline-offset:-2px}.oc-table-empty{text-align:center;color:var(--oc-text-secondary);padding:32px 16px;font-size:14px;font-style:italic}.oc-table-wrapper.oc-animate>.oc-chrome{animation:oc-enter-fade calc(var(--oc-animation-duration) * .6) var(--oc-animation-ease,var(--oc-ease-smooth)) both}.oc-table-wrapper.oc-animate thead{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .4) var(--oc-animation-ease,var(--oc-ease-smooth)) both}.oc-table-wrapper.oc-animate tbody tr{animation:oc-table-enter-row var(--oc-animation-duration) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-row-index,0))}.oc-table-wrapper.oc-animate tbody td{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .5) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-row-index,0))}.oc-table-wrapper.oc-animate td.oc-table-heatmap{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .7) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-row-index,0) + var(--oc-animation-duration) * .3)}.oc-table-wrapper.oc-animate td.oc-table-category{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .7) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-row-index,0) + var(--oc-animation-duration) * .3)}.oc-table-wrapper.oc-animate .oc-table-bar-fill{animation:oc-table-enter-bar-fill calc(var(--oc-animation-duration) * .8) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-row-index,0) + var(--oc-animation-duration) * .3)}.oc-table-wrapper.oc-animate .oc-table-sparkline>svg{animation:oc-enter-line calc(var(--oc-animation-duration) * .8) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-row-index,0) + var(--oc-animation-duration) * .4)}.oc-table-wrapper.oc-animate .oc-table-sparkline-dot{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .3) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-row-index,0) + var(--oc-animation-duration) * .8)}.oc-table-wrapper.oc-animate .oc-table-sparkline-labels{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .3) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-row-index,0) + var(--oc-animation-duration) * .8)}.oc-table-wrapper.oc-animate .oc-table-search{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .5) var(--oc-animation-ease,var(--oc-ease-smooth)) both}.oc-table-wrapper.oc-animate .oc-table-pagination{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .5) var(--oc-animation-ease,var(--oc-ease-smooth)) both}.oc-graph-wrapper{background:var(--oc-bg);font-family:var(--oc-font-family);width:100%;height:100%;position:relative;overflow:hidden}.oc-graph-canvas{cursor:grab;width:100%;height:100%;display:block}.oc-graph-canvas--dragging{cursor:grabbing}.oc-graph-chrome{z-index:2;pointer-events:none;padding:16px 16px 8px;position:absolute;top:0;left:0;right:0}.oc-graph-chrome .oc-title{font-size:var(--oc-title-size);font-weight:var(--oc-title-weight);letter-spacing:var(--oc-title-tracking);color:var(--oc-text);--_stroke:color-mix(in srgb, var(--oc-bg) 80%, transparent);text-shadow:-2px -2px 0 var(--_stroke), 2px -2px 0 var(--_stroke), -2px 2px 0 var(--_stroke), 2px 2px 0 var(--_stroke), 0 -2px 0 var(--_stroke), 0 2px 0 var(--_stroke), -2px 0 0 var(--_stroke), 2px 0 0 var(--_stroke);margin:0 0 4px}.oc-graph-chrome .oc-subtitle{font-size:var(--oc-subtitle-size);color:var(--oc-text-secondary);--_stroke:color-mix(in srgb, var(--oc-bg) 80%, transparent);text-shadow:-1px -1px 0 var(--_stroke), 1px -1px 0 var(--_stroke), -1px 1px 0 var(--_stroke), 1px 1px 0 var(--_stroke);margin:0}.oc-graph-legend{background:var(--oc-bg);border:1px solid var(--oc-border);border-radius:var(--oc-border-radius);color:var(--oc-text-secondary);max-height:200px;padding:8px 12px;font-size:12px;position:absolute;top:8px;right:8px;overflow-y:auto}.oc-graph-legend-item{align-items:center;gap:6px;padding:2px 0;display:flex}.oc-graph-legend-swatch{border-radius:50%;flex-shrink:0;width:10px;height:10px}.oc-graph-search{position:absolute;top:8px;left:8px}.oc-graph-search input{font-family:var(--oc-font-family);font-size:var(--oc-body-size);border:1px solid var(--oc-border);border-radius:var(--oc-border-radius);background:var(--oc-bg);color:var(--oc-text);outline:none;padding:6px 10px}.oc-graph-search input:focus{border-color:var(--oc-focus);box-shadow:0 0 0 2px rgba(59,130,246,.25)}.oc-dark .oc-graph-wrapper,.oc-graph-wrapper.oc-dark{--oc-bg:#0d1117}.oc-dark .oc-graph-legend,.oc-dark.oc-graph-wrapper .oc-graph-legend,.oc-dark .oc-graph-search input{background:rgba(13,17,23,.85);border-color:rgba(255,255,255,.1)}@keyframes oc-enter-bar{0%{clip-path:inset(100% 0 0);opacity:0}75%{opacity:1}to{clip-path:inset(0);opacity:1}}@keyframes oc-enter-bar-h{0%{clip-path:inset(0 100% 0 0);opacity:0}75%{opacity:1}to{clip-path:inset(0);opacity:1}}@keyframes oc-enter-line{0%{clip-path:inset(0 100% 0 0);opacity:0}15%{opacity:1}to{clip-path:inset(0);opacity:1}}@keyframes oc-enter-point{0%{opacity:0;transform:scale(.3)}to{opacity:1;transform:scale(1)}}@keyframes oc-enter-fade-only{0%{opacity:0}to{opacity:1}}@keyframes oc-enter-fade{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}@keyframes oc-table-enter-row{0%{transform:translateY(6px)}to{transform:translateY(0)}}@keyframes oc-table-enter-bar-fill{0%{clip-path:inset(0 100% 0 0)}to{clip-path:inset(0)}}@keyframes oc-tooltip-in{0%{opacity:0;transform:translateY(2px)}to{opacity:1;transform:translateY(0)}}.oc-animate .oc-mark-rect rect{animation:oc-enter-bar var(--oc-animation-duration) var(--oc-ease-smooth) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-mark-bar rect{animation:oc-enter-bar var(--oc-animation-duration) var(--oc-ease-smooth) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-mark-rect[data-orient=horizontal] rect{animation-name:oc-enter-bar-h}.oc-animate .oc-mark-bar[data-orient=horizontal] rect{animation-name:oc-enter-bar-h}.oc-animate .oc-mark-rect[data-stack-pos] rect{animation-duration:var(--oc-stack-segment-duration,.15s);animation-timing-function:linear;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0) + var(--oc-stack-pos,0) * var(--oc-stack-segment-duration,.15s))}.oc-animate .oc-mark-line{animation:oc-enter-line var(--oc-animation-duration) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-mark-area{animation:oc-enter-line var(--oc-animation-duration) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-mark-arc{animation:oc-enter-fade-only var(--oc-animation-duration) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate circle.oc-mark-point{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .4) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate circle.oc-mark-circle{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .4) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-mark-line~circle.oc-mark-point{animation-delay:calc(var(--oc-animation-duration) * .35 + var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-mark-area~circle.oc-mark-point{animation-delay:calc(var(--oc-animation-duration) * .35 + var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-mark-text text{animation:oc-enter-fade calc(var(--oc-animation-duration) * .6) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-mark-rule line{animation:oc-enter-fade calc(var(--oc-animation-duration) * .5) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-mark-tick line{animation:oc-enter-fade calc(var(--oc-animation-duration) * .5) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-mark-label{animation:oc-enter-fade .3s var(--oc-ease-smooth) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0) + var(--oc-animation-duration) * .7)}.oc-animate .oc-annotation{animation:oc-enter-fade .4s var(--oc-ease-smooth) both;animation-delay:calc(var(--oc-animation-duration) + var(--oc-annotation-delay,.2s))}.oc-animate .oc-tilemap-tile{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .35) ease-in both;animation-delay:var(--oc-tile-delay,0s)}.oc-animate .oc-sankey-node rect{animation:oc-enter-fade-only var(--oc-animation-duration) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-sankey-link path{animation:oc-enter-fade-only var(--oc-animation-duration) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-duration) * .3 + var(--oc-animation-stagger) * var(--oc-mark-index,0))}@media (prefers-reduced-motion:reduce){.oc-table-sort-btn:before,.oc-table-sort-btn:after,.oc-table-search input,.oc-table-pagination button{transition:none}.oc-animate .oc-mark-rect rect,.oc-animate .oc-mark-bar rect,.oc-animate .oc-mark-arc,.oc-animate .oc-mark-line,.oc-animate .oc-mark-area,.oc-animate circle.oc-mark-point,.oc-animate circle.oc-mark-circle,.oc-animate .oc-mark-text text,.oc-animate .oc-mark-rule line,.oc-animate .oc-mark-tick line,.oc-animate .oc-mark-label,.oc-animate .oc-annotation,.oc-animate .oc-sankey-node rect,.oc-animate .oc-sankey-link path,.oc-table-wrapper.oc-animate>.oc-chrome,.oc-table-wrapper.oc-animate thead,.oc-table-wrapper.oc-animate tbody tr,.oc-table-wrapper.oc-animate tbody td,.oc-table-wrapper.oc-animate td.oc-table-heatmap,.oc-table-wrapper.oc-animate td.oc-table-category,.oc-table-wrapper.oc-animate .oc-table-bar-fill,.oc-table-wrapper.oc-animate .oc-table-sparkline>svg,.oc-table-wrapper.oc-animate .oc-table-sparkline-dot,.oc-table-wrapper.oc-animate .oc-table-sparkline-labels,.oc-table-wrapper.oc-animate .oc-table-search,.oc-table-wrapper.oc-animate .oc-table-pagination{animation:none}}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opendata-ai/openchart-vanilla",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.26.0",
|
|
4
4
|
"description": "Vanilla JS renderer for openchart: SVG charts, HTML tables, force-directed graphs",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Riley Hilliard",
|
|
@@ -50,8 +50,8 @@
|
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"@floating-ui/dom": "^1.7.6",
|
|
53
|
-
"@opendata-ai/openchart-core": "6.
|
|
54
|
-
"@opendata-ai/openchart-engine": "6.
|
|
53
|
+
"@opendata-ai/openchart-core": "6.26.0",
|
|
54
|
+
"@opendata-ai/openchart-engine": "6.26.0",
|
|
55
55
|
"d3-force": "^3.0.0",
|
|
56
56
|
"d3-quadtree": "^3.0.1"
|
|
57
57
|
},
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import type { ChartSpec } from '@opendata-ai/openchart-engine';
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
3
|
+
import { createContainer } from '../__test-fixtures__/dom';
|
|
4
|
+
import { createChart } from '../mount';
|
|
5
|
+
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
// Fixtures
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
|
|
10
|
+
const albumData = [
|
|
11
|
+
{ album: 'Abbey Road', artist: 'The Beatles', sales: 31 },
|
|
12
|
+
{ album: 'Thriller', artist: 'Michael Jackson', sales: 66 },
|
|
13
|
+
{ album: 'Back in Black', artist: 'AC/DC', sales: 50 },
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
const barWithLabelField: ChartSpec = {
|
|
17
|
+
mark: 'bar',
|
|
18
|
+
data: albumData,
|
|
19
|
+
encoding: {
|
|
20
|
+
x: { field: 'sales', type: 'quantitative' },
|
|
21
|
+
y: {
|
|
22
|
+
field: 'album',
|
|
23
|
+
type: 'nominal',
|
|
24
|
+
axis: { labelField: 'artist' },
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const barWithoutLabelField: ChartSpec = {
|
|
30
|
+
mark: 'bar',
|
|
31
|
+
data: albumData,
|
|
32
|
+
encoding: {
|
|
33
|
+
x: { field: 'sales', type: 'quantitative' },
|
|
34
|
+
y: { field: 'album', type: 'nominal' },
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
// Tests
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
|
|
42
|
+
describe('compound axis labels rendering', () => {
|
|
43
|
+
let container: HTMLDivElement;
|
|
44
|
+
|
|
45
|
+
beforeEach(() => {
|
|
46
|
+
container = createContainer();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
afterEach(() => {
|
|
50
|
+
document.body.innerHTML = '';
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('renders tspan elements when subtitle is present', () => {
|
|
54
|
+
const chart = createChart(container, barWithLabelField);
|
|
55
|
+
|
|
56
|
+
const yAxisTicks = container.querySelectorAll('.oc-axis-y .oc-axis-tick');
|
|
57
|
+
expect(yAxisTicks.length).toBeGreaterThan(0);
|
|
58
|
+
|
|
59
|
+
// At least one tick should have tspan children (compound label)
|
|
60
|
+
let hasTspan = false;
|
|
61
|
+
for (const tick of yAxisTicks) {
|
|
62
|
+
const tspans = tick.querySelectorAll('tspan');
|
|
63
|
+
if (tspans.length > 0) {
|
|
64
|
+
hasTspan = true;
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
expect(hasTspan).toBe(true);
|
|
69
|
+
|
|
70
|
+
chart.destroy();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('primary tspan has the tick label font-weight', () => {
|
|
74
|
+
const chart = createChart(container, barWithLabelField);
|
|
75
|
+
|
|
76
|
+
const yAxisTicks = container.querySelectorAll('.oc-axis-y .oc-axis-tick');
|
|
77
|
+
for (const tick of yAxisTicks) {
|
|
78
|
+
const tspans = tick.querySelectorAll('tspan');
|
|
79
|
+
if (tspans.length >= 2) {
|
|
80
|
+
const primarySpan = tspans[0];
|
|
81
|
+
const fontWeight = primarySpan.getAttribute('font-weight');
|
|
82
|
+
expect(fontWeight).not.toBeNull();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
chart.destroy();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('secondary tspan has normal weight (400) and reduced opacity', () => {
|
|
90
|
+
const chart = createChart(container, barWithLabelField);
|
|
91
|
+
|
|
92
|
+
const yAxisTicks = container.querySelectorAll('.oc-axis-y .oc-axis-tick');
|
|
93
|
+
for (const tick of yAxisTicks) {
|
|
94
|
+
const tspans = tick.querySelectorAll('tspan');
|
|
95
|
+
if (tspans.length >= 2) {
|
|
96
|
+
const subtitleSpan = tspans[1];
|
|
97
|
+
expect(subtitleSpan.getAttribute('font-weight')).toBe('400');
|
|
98
|
+
expect(subtitleSpan.getAttribute('fill-opacity')).toBe('0.6');
|
|
99
|
+
expect(subtitleSpan.getAttribute('dx')).toBe('0.5em');
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
chart.destroy();
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('renders plain textContent without tspans when labelField is omitted', () => {
|
|
107
|
+
const chart = createChart(container, barWithoutLabelField);
|
|
108
|
+
|
|
109
|
+
const yAxisTicks = container.querySelectorAll('.oc-axis-y .oc-axis-tick');
|
|
110
|
+
expect(yAxisTicks.length).toBeGreaterThan(0);
|
|
111
|
+
|
|
112
|
+
for (const tick of yAxisTicks) {
|
|
113
|
+
const tspans = tick.querySelectorAll('tspan');
|
|
114
|
+
// No tspan elements when there's no subtitle
|
|
115
|
+
expect(tspans.length).toBe(0);
|
|
116
|
+
// Should have plain text content
|
|
117
|
+
expect(tick.textContent).toBeTruthy();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
chart.destroy();
|
|
121
|
+
});
|
|
122
|
+
});
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type { ChartSpec } from '@opendata-ai/openchart-engine';
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
3
|
+
import { createContainer } from '../__test-fixtures__/dom';
|
|
4
|
+
import { barSpec } from '../__test-fixtures__/specs';
|
|
5
|
+
import { createChart } from '../mount';
|
|
6
|
+
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Fixtures
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
const lineSpecWithCrosshair: ChartSpec = {
|
|
12
|
+
mark: 'line',
|
|
13
|
+
data: [
|
|
14
|
+
{ date: '2020-01-01', value: 10, country: 'US' },
|
|
15
|
+
{ date: '2021-01-01', value: 40, country: 'US' },
|
|
16
|
+
{ date: '2020-01-01', value: 15, country: 'UK' },
|
|
17
|
+
{ date: '2021-01-01', value: 35, country: 'UK' },
|
|
18
|
+
],
|
|
19
|
+
encoding: {
|
|
20
|
+
x: { field: 'date', type: 'temporal' },
|
|
21
|
+
y: { field: 'value', type: 'quantitative' },
|
|
22
|
+
color: { field: 'country', type: 'nominal' },
|
|
23
|
+
},
|
|
24
|
+
crosshair: true,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const lineSpecNoCrosshair: ChartSpec = {
|
|
28
|
+
mark: 'line',
|
|
29
|
+
data: [
|
|
30
|
+
{ date: '2020-01-01', value: 10, country: 'US' },
|
|
31
|
+
{ date: '2021-01-01', value: 40, country: 'US' },
|
|
32
|
+
{ date: '2020-01-01', value: 15, country: 'UK' },
|
|
33
|
+
{ date: '2021-01-01', value: 35, country: 'UK' },
|
|
34
|
+
],
|
|
35
|
+
encoding: {
|
|
36
|
+
x: { field: 'date', type: 'temporal' },
|
|
37
|
+
y: { field: 'value', type: 'quantitative' },
|
|
38
|
+
color: { field: 'country', type: 'nominal' },
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
// Tests
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
|
|
46
|
+
describe('crosshair', () => {
|
|
47
|
+
let container: HTMLDivElement;
|
|
48
|
+
|
|
49
|
+
beforeEach(() => {
|
|
50
|
+
container = createContainer();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
afterEach(() => {
|
|
54
|
+
document.body.innerHTML = '';
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('creates a crosshair line element when crosshair: true on a line chart', () => {
|
|
58
|
+
const chart = createChart(container, lineSpecWithCrosshair);
|
|
59
|
+
|
|
60
|
+
const crosshair = container.querySelector('[data-crosshair]');
|
|
61
|
+
expect(crosshair).not.toBeNull();
|
|
62
|
+
expect(crosshair?.tagName.toLowerCase()).toBe('line');
|
|
63
|
+
expect(crosshair?.getAttribute('class')).toBe('oc-crosshair');
|
|
64
|
+
|
|
65
|
+
chart.destroy();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('crosshair is hidden initially (display: none)', () => {
|
|
69
|
+
const chart = createChart(container, lineSpecWithCrosshair);
|
|
70
|
+
|
|
71
|
+
const crosshair = container.querySelector('[data-crosshair]') as SVGLineElement;
|
|
72
|
+
expect(crosshair).not.toBeNull();
|
|
73
|
+
expect(crosshair.style.display).toBe('none');
|
|
74
|
+
|
|
75
|
+
chart.destroy();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('does not create crosshair when crosshair is false or omitted', () => {
|
|
79
|
+
const chart = createChart(container, lineSpecNoCrosshair);
|
|
80
|
+
|
|
81
|
+
const crosshair = container.querySelector('[data-crosshair]');
|
|
82
|
+
expect(crosshair).toBeNull();
|
|
83
|
+
|
|
84
|
+
chart.destroy();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('does not create crosshair on bar chart (no voronoi overlay)', () => {
|
|
88
|
+
const barWithCrosshair: ChartSpec = {
|
|
89
|
+
...barSpec,
|
|
90
|
+
crosshair: true,
|
|
91
|
+
};
|
|
92
|
+
const chart = createChart(container, barWithCrosshair);
|
|
93
|
+
|
|
94
|
+
const crosshair = container.querySelector('[data-crosshair]');
|
|
95
|
+
expect(crosshair).toBeNull();
|
|
96
|
+
|
|
97
|
+
chart.destroy();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('crosshair has correct stroke attributes', () => {
|
|
101
|
+
const chart = createChart(container, lineSpecWithCrosshair);
|
|
102
|
+
|
|
103
|
+
const crosshair = container.querySelector('[data-crosshair]') as SVGLineElement;
|
|
104
|
+
expect(crosshair).not.toBeNull();
|
|
105
|
+
expect(crosshair.getAttribute('stroke-dasharray')).toBe('4,3');
|
|
106
|
+
expect(crosshair.getAttribute('stroke-width')).toBe('1');
|
|
107
|
+
expect(crosshair.getAttribute('pointer-events')).toBe('none');
|
|
108
|
+
|
|
109
|
+
chart.destroy();
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('cleanup removes crosshair on destroy', () => {
|
|
113
|
+
const chart = createChart(container, lineSpecWithCrosshair);
|
|
114
|
+
|
|
115
|
+
expect(container.querySelector('[data-crosshair]')).not.toBeNull();
|
|
116
|
+
|
|
117
|
+
chart.destroy();
|
|
118
|
+
|
|
119
|
+
expect(container.querySelector('[data-crosshair]')).toBeNull();
|
|
120
|
+
});
|
|
121
|
+
});
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
2
|
+
import { createContainer } from '../__test-fixtures__/dom';
|
|
3
|
+
import { createTileMap } from '../tilemap-mount';
|
|
4
|
+
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// Shared fixtures
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
|
|
9
|
+
const basicTileMapSpec = {
|
|
10
|
+
type: 'tilemap' as const,
|
|
11
|
+
data: { CA: 5.4, TX: 4.1, NY: 4.5, FL: 3.3, IL: 4.6 } as Record<string, number>,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Tests
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
describe('createTileMap', () => {
|
|
19
|
+
let container: HTMLDivElement;
|
|
20
|
+
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
container = createContainer();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
afterEach(() => {
|
|
26
|
+
document.body.innerHTML = '';
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('mounts an SVG element into the container', () => {
|
|
30
|
+
const instance = createTileMap(container, basicTileMapSpec, { responsive: false });
|
|
31
|
+
|
|
32
|
+
const svg = container.querySelector('svg');
|
|
33
|
+
expect(svg).not.toBeNull();
|
|
34
|
+
expect(svg?.classList.contains('oc-tilemap')).toBe(true);
|
|
35
|
+
|
|
36
|
+
instance.destroy();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('renders all 51 state tiles', () => {
|
|
40
|
+
const instance = createTileMap(container, basicTileMapSpec, { responsive: false });
|
|
41
|
+
|
|
42
|
+
const tiles = container.querySelectorAll('.oc-tilemap-tile');
|
|
43
|
+
expect(tiles).toHaveLength(51);
|
|
44
|
+
|
|
45
|
+
instance.destroy();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('update() re-renders with new data', () => {
|
|
49
|
+
const instance = createTileMap(container, basicTileMapSpec, { responsive: false });
|
|
50
|
+
|
|
51
|
+
// Update with full data
|
|
52
|
+
const updatedSpec = {
|
|
53
|
+
...basicTileMapSpec,
|
|
54
|
+
data: {
|
|
55
|
+
AL: 2.7,
|
|
56
|
+
AK: 6.4,
|
|
57
|
+
AZ: 3.5,
|
|
58
|
+
AR: 3.4,
|
|
59
|
+
CA: 5.4,
|
|
60
|
+
CO: 3.4,
|
|
61
|
+
CT: 4.1,
|
|
62
|
+
DE: 4.4,
|
|
63
|
+
FL: 3.3,
|
|
64
|
+
GA: 3.4,
|
|
65
|
+
HI: 3.2,
|
|
66
|
+
ID: 3.0,
|
|
67
|
+
IL: 4.6,
|
|
68
|
+
IN: 3.3,
|
|
69
|
+
IA: 2.7,
|
|
70
|
+
KS: 3.2,
|
|
71
|
+
KY: 4.4,
|
|
72
|
+
LA: 3.6,
|
|
73
|
+
ME: 3.6,
|
|
74
|
+
MD: 1.8,
|
|
75
|
+
MA: 3.3,
|
|
76
|
+
MI: 4.2,
|
|
77
|
+
MN: 2.8,
|
|
78
|
+
MS: 3.7,
|
|
79
|
+
MO: 3.5,
|
|
80
|
+
MT: 2.9,
|
|
81
|
+
NE: 2.2,
|
|
82
|
+
NV: 5.4,
|
|
83
|
+
NH: 2.4,
|
|
84
|
+
NJ: 4.8,
|
|
85
|
+
NM: 4.1,
|
|
86
|
+
NY: 4.5,
|
|
87
|
+
NC: 3.5,
|
|
88
|
+
ND: 1.9,
|
|
89
|
+
OH: 4.0,
|
|
90
|
+
OK: 3.9,
|
|
91
|
+
OR: 4.2,
|
|
92
|
+
PA: 3.4,
|
|
93
|
+
RI: 3.8,
|
|
94
|
+
SC: 3.3,
|
|
95
|
+
SD: 2.0,
|
|
96
|
+
TN: 3.5,
|
|
97
|
+
TX: 4.1,
|
|
98
|
+
UT: 2.9,
|
|
99
|
+
VT: 2.3,
|
|
100
|
+
VA: 2.9,
|
|
101
|
+
WA: 4.6,
|
|
102
|
+
WV: 4.0,
|
|
103
|
+
WI: 2.9,
|
|
104
|
+
WY: 3.2,
|
|
105
|
+
DC: 5.2,
|
|
106
|
+
} as Record<string, number>,
|
|
107
|
+
};
|
|
108
|
+
instance.update(updatedSpec);
|
|
109
|
+
|
|
110
|
+
// All tiles should have data now
|
|
111
|
+
const dataTiles = instance.layout.tiles.filter((t) => t.hasData);
|
|
112
|
+
expect(dataTiles).toHaveLength(51);
|
|
113
|
+
|
|
114
|
+
instance.destroy();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('destroy() removes the SVG from the container', () => {
|
|
118
|
+
const instance = createTileMap(container, basicTileMapSpec, { responsive: false });
|
|
119
|
+
|
|
120
|
+
const svgBefore = container.querySelector('svg');
|
|
121
|
+
expect(svgBefore).not.toBeNull();
|
|
122
|
+
|
|
123
|
+
instance.destroy();
|
|
124
|
+
|
|
125
|
+
const svgAfter = container.querySelector('svg');
|
|
126
|
+
expect(svgAfter).toBeNull();
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('gradient legend renders', () => {
|
|
130
|
+
const instance = createTileMap(container, basicTileMapSpec, { responsive: false });
|
|
131
|
+
|
|
132
|
+
const legend = container.querySelector('.oc-tilemap-legend');
|
|
133
|
+
expect(legend).not.toBeNull();
|
|
134
|
+
|
|
135
|
+
instance.destroy();
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('layout property returns the compiled layout', () => {
|
|
139
|
+
const instance = createTileMap(container, basicTileMapSpec, { responsive: false });
|
|
140
|
+
|
|
141
|
+
expect(instance.layout).toBeDefined();
|
|
142
|
+
expect(instance.layout.tiles).toHaveLength(51);
|
|
143
|
+
expect(instance.layout.gradientLegend).not.toBeNull();
|
|
144
|
+
|
|
145
|
+
instance.destroy();
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('export("svg") returns a string containing SVG content', () => {
|
|
149
|
+
const instance = createTileMap(container, basicTileMapSpec, { responsive: false });
|
|
150
|
+
|
|
151
|
+
const svgString = instance.export('svg');
|
|
152
|
+
expect(typeof svgString).toBe('string');
|
|
153
|
+
expect(svgString).toContain('<svg');
|
|
154
|
+
expect(svgString).toContain('oc-tilemap');
|
|
155
|
+
|
|
156
|
+
instance.destroy();
|
|
157
|
+
});
|
|
158
|
+
});
|
package/src/graph-mount.ts
CHANGED
|
@@ -342,7 +342,7 @@ export function createGraph(
|
|
|
342
342
|
function renderLegend(): void {
|
|
343
343
|
if (!legendEl) return;
|
|
344
344
|
|
|
345
|
-
const entries = compilation.legend.entries;
|
|
345
|
+
const entries = 'entries' in compilation.legend ? compilation.legend.entries : [];
|
|
346
346
|
if (entries.length === 0) {
|
|
347
347
|
legendEl.style.display = 'none';
|
|
348
348
|
return;
|
package/src/index.ts
CHANGED
|
@@ -56,6 +56,9 @@ export { createTable } from './table-mount';
|
|
|
56
56
|
export { renderTable } from './table-renderer';
|
|
57
57
|
export type { TextEditOverlayConfig } from './text-edit-overlay';
|
|
58
58
|
export { createTextEditOverlay } from './text-edit-overlay';
|
|
59
|
+
export type { TileMapInstance, TileMapMountOptions } from './tilemap-mount';
|
|
60
|
+
// TileMap mount API
|
|
61
|
+
export { createTileMap } from './tilemap-mount';
|
|
59
62
|
export type { TooltipManager } from './tooltip';
|
|
60
63
|
// Tooltip
|
|
61
64
|
export { createTooltipManager } from './tooltip';
|
package/src/mount.ts
CHANGED
|
@@ -260,6 +260,7 @@ function wireVoronoiTooltipEvents(
|
|
|
260
260
|
const voronoiPoints = collectVoronoiPoints(layout);
|
|
261
261
|
if (voronoiPoints.length === 0) return () => {};
|
|
262
262
|
|
|
263
|
+
const crosshair = svg.querySelector('[data-crosshair]') as SVGLineElement | null;
|
|
263
264
|
const cleanups: Array<() => void> = [];
|
|
264
265
|
|
|
265
266
|
const handleMouseMove = (e: Event) => {
|
|
@@ -277,6 +278,13 @@ function wireVoronoiTooltipEvents(
|
|
|
277
278
|
const nearest = findNearestPoint(voronoiPoints, svgX, svgY);
|
|
278
279
|
if (!nearest?.tooltip) return;
|
|
279
280
|
|
|
281
|
+
// Update crosshair position to match the nearest data point's x
|
|
282
|
+
if (crosshair) {
|
|
283
|
+
crosshair.setAttribute('x1', String(nearest.x));
|
|
284
|
+
crosshair.setAttribute('x2', String(nearest.x));
|
|
285
|
+
crosshair.style.display = '';
|
|
286
|
+
}
|
|
287
|
+
|
|
280
288
|
// Show tooltip at the mouse position (relative to container, not SVG viewBox)
|
|
281
289
|
const containerX = mouseEvent.clientX - svgRect.left;
|
|
282
290
|
const containerY = mouseEvent.clientY - svgRect.top;
|
|
@@ -284,6 +292,7 @@ function wireVoronoiTooltipEvents(
|
|
|
284
292
|
};
|
|
285
293
|
|
|
286
294
|
const handleMouseLeave = () => {
|
|
295
|
+
if (crosshair) crosshair.style.display = 'none';
|
|
287
296
|
tooltipManager.hide();
|
|
288
297
|
};
|
|
289
298
|
|
|
@@ -304,6 +313,13 @@ function wireVoronoiTooltipEvents(
|
|
|
304
313
|
const nearest = findNearestPoint(voronoiPoints, svgX, svgY);
|
|
305
314
|
if (!nearest?.tooltip) return;
|
|
306
315
|
|
|
316
|
+
// Update crosshair position on touch
|
|
317
|
+
if (crosshair) {
|
|
318
|
+
crosshair.setAttribute('x1', String(nearest.x));
|
|
319
|
+
crosshair.setAttribute('x2', String(nearest.x));
|
|
320
|
+
crosshair.style.display = '';
|
|
321
|
+
}
|
|
322
|
+
|
|
307
323
|
const containerX = touch.clientX - svgRect.left;
|
|
308
324
|
const containerY = touch.clientY - svgRect.top;
|
|
309
325
|
tooltipManager.show(nearest.tooltip, containerX, containerY);
|
|
@@ -1181,8 +1197,9 @@ function wireSeriesLabelDrag(
|
|
|
1181
1197
|
const labels = svg.querySelectorAll('.oc-mark-label');
|
|
1182
1198
|
const cleanups: Array<() => void> = [];
|
|
1183
1199
|
|
|
1184
|
-
// Read existing label offsets from the spec
|
|
1185
|
-
const
|
|
1200
|
+
// Read existing label offsets from the spec (skip boolean shorthand)
|
|
1201
|
+
const rawLabels = 'labels' in spec ? spec.labels : undefined;
|
|
1202
|
+
const labelsConfig = typeof rawLabels === 'object' ? rawLabels : undefined;
|
|
1186
1203
|
|
|
1187
1204
|
for (const label of labels) {
|
|
1188
1205
|
const labelEl = label as SVGTextElement;
|
|
@@ -1624,7 +1641,7 @@ function getEditableElements(
|
|
|
1624
1641
|
}
|
|
1625
1642
|
|
|
1626
1643
|
// Legend
|
|
1627
|
-
if (layout.legend.entries.length > 0) {
|
|
1644
|
+
if ('entries' in layout.legend && layout.legend.entries.length > 0) {
|
|
1628
1645
|
refs.push(elementRef.legend());
|
|
1629
1646
|
}
|
|
1630
1647
|
|
|
@@ -2145,7 +2162,11 @@ export function createChart(
|
|
|
2145
2162
|
|
|
2146
2163
|
currentLayout = compile();
|
|
2147
2164
|
const shouldAnimate = isFirstRender && !!currentLayout.animation?.enabled;
|
|
2148
|
-
|
|
2165
|
+
const crosshair = 'crosshair' in currentSpec && !!(currentSpec as ChartSpec).crosshair;
|
|
2166
|
+
svgElement = renderChartSVG(currentLayout, container, {
|
|
2167
|
+
animate: shouldAnimate,
|
|
2168
|
+
crosshair,
|
|
2169
|
+
});
|
|
2149
2170
|
tooltipManager = createTooltipManager(container);
|
|
2150
2171
|
|
|
2151
2172
|
// Wire tooltip events on mark elements
|