@opendata-ai/openchart-core 6.25.4 → 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 +231 -14
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/helpers/spec-builders.ts +63 -0
- package/src/index.ts +2 -0
- package/src/styles/animation.css +7 -0
- package/src/styles/base.css +2 -1
- package/src/styles/tokens.css +2 -1
- package/src/types/index.ts +16 -0
- package/src/types/layout.ts +110 -5
- package/src/types/spec.ts +112 -8
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
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import type {
|
|
13
|
+
AnimationSpec,
|
|
13
14
|
Annotation,
|
|
14
15
|
ChartSpec,
|
|
15
16
|
Chrome,
|
|
@@ -18,10 +19,14 @@ import type {
|
|
|
18
19
|
Encoding,
|
|
19
20
|
EncodingChannel,
|
|
20
21
|
FieldType,
|
|
22
|
+
LegendConfig,
|
|
21
23
|
MarkDef,
|
|
22
24
|
MarkType,
|
|
23
25
|
TableSpec,
|
|
24
26
|
ThemeConfig,
|
|
27
|
+
TileMapEncoding,
|
|
28
|
+
TileMapPalette,
|
|
29
|
+
TileMapSpec,
|
|
25
30
|
} from '../types/spec';
|
|
26
31
|
import type { ColumnConfig } from '../types/table';
|
|
27
32
|
|
|
@@ -409,3 +414,61 @@ export function dataTable(data: DataRow[], options?: TableBuilderOptions): Table
|
|
|
409
414
|
|
|
410
415
|
return spec;
|
|
411
416
|
}
|
|
417
|
+
|
|
418
|
+
// ---------------------------------------------------------------------------
|
|
419
|
+
// Tile map builder
|
|
420
|
+
// ---------------------------------------------------------------------------
|
|
421
|
+
|
|
422
|
+
/** Options for the tileMap convenience builder. */
|
|
423
|
+
export interface TileMapBuilderOptions {
|
|
424
|
+
/** Encoding channels (required when data is DataRow[]). */
|
|
425
|
+
encoding?: TileMapEncoding;
|
|
426
|
+
/** Sequential color palette name. */
|
|
427
|
+
palette?: TileMapPalette;
|
|
428
|
+
/** Editorial chrome. */
|
|
429
|
+
chrome?: Chrome;
|
|
430
|
+
/** Legend display configuration. */
|
|
431
|
+
legend?: LegendConfig;
|
|
432
|
+
/** Theme overrides. */
|
|
433
|
+
theme?: ThemeConfig;
|
|
434
|
+
/** Dark mode setting. */
|
|
435
|
+
darkMode?: DarkMode;
|
|
436
|
+
/** Whether to show the watermark. */
|
|
437
|
+
watermark?: boolean;
|
|
438
|
+
/** Animation configuration. */
|
|
439
|
+
animation?: AnimationSpec;
|
|
440
|
+
/** d3-format string for value formatting. */
|
|
441
|
+
valueFormat?: string;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Build a TileMapSpec from a state-to-value record or tabular data.
|
|
446
|
+
*
|
|
447
|
+
* @example Record map (simplest):
|
|
448
|
+
* ```ts
|
|
449
|
+
* tileMap({ CA: 4.4, TX: 17.6, NY: 4.5 })
|
|
450
|
+
* ```
|
|
451
|
+
*
|
|
452
|
+
* @example Tabular data with encoding:
|
|
453
|
+
* ```ts
|
|
454
|
+
* tileMap(data, { encoding: { state: { field: 'code' }, value: { field: 'rate' } } })
|
|
455
|
+
* ```
|
|
456
|
+
*/
|
|
457
|
+
export function tileMap(
|
|
458
|
+
data: Record<string, number | null> | DataRow[],
|
|
459
|
+
options?: TileMapBuilderOptions,
|
|
460
|
+
): TileMapSpec {
|
|
461
|
+
return {
|
|
462
|
+
type: 'tilemap' as const,
|
|
463
|
+
data,
|
|
464
|
+
...(options?.encoding && { encoding: options.encoding }),
|
|
465
|
+
...(options?.palette && { palette: options.palette }),
|
|
466
|
+
...(options?.chrome && { chrome: options.chrome }),
|
|
467
|
+
...(options?.legend && { legend: options.legend }),
|
|
468
|
+
...(options?.theme && { theme: options.theme }),
|
|
469
|
+
...(options?.darkMode && { darkMode: options.darkMode }),
|
|
470
|
+
...(options?.watermark !== undefined && { watermark: options.watermark }),
|
|
471
|
+
...(options?.animation && { animation: options.animation }),
|
|
472
|
+
...(options?.valueFormat && { valueFormat: options.valueFormat }),
|
|
473
|
+
};
|
|
474
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -151,6 +151,7 @@ export type {
|
|
|
151
151
|
ChartBuilderOptions,
|
|
152
152
|
FieldRef,
|
|
153
153
|
TableBuilderOptions,
|
|
154
|
+
TileMapBuilderOptions,
|
|
154
155
|
} from './helpers/spec-builders';
|
|
155
156
|
export {
|
|
156
157
|
areaChart,
|
|
@@ -163,4 +164,5 @@ export {
|
|
|
163
164
|
lineChart,
|
|
164
165
|
pieChart,
|
|
165
166
|
scatterChart,
|
|
167
|
+
tileMap,
|
|
166
168
|
} from './helpers/spec-builders';
|
package/src/styles/animation.css
CHANGED
|
@@ -115,6 +115,13 @@
|
|
|
115
115
|
animation-delay: calc(var(--oc-animation-duration) + var(--oc-annotation-delay, 200ms));
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
+
/* Tilemap tiles: fade in with jittered stagger for organic feel.
|
|
119
|
+
Per-tile delay is computed in the renderer with pseudo-random variation. */
|
|
120
|
+
& .oc-tilemap-tile {
|
|
121
|
+
animation: oc-enter-fade-only calc(var(--oc-animation-duration) * 0.35) ease-in both;
|
|
122
|
+
animation-delay: var(--oc-tile-delay, 0ms);
|
|
123
|
+
}
|
|
124
|
+
|
|
118
125
|
/* Sankey nodes: fade in with stagger by column depth (left-to-right) */
|
|
119
126
|
& .oc-sankey-node rect {
|
|
120
127
|
animation: oc-enter-fade-only var(--oc-animation-duration)
|
package/src/styles/base.css
CHANGED
package/src/styles/tokens.css
CHANGED
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
.oc-table-root,
|
|
9
9
|
.oc-graph-wrapper,
|
|
10
10
|
.oc-graph-root,
|
|
11
|
-
.oc-sankey-root
|
|
11
|
+
.oc-sankey-root,
|
|
12
|
+
.oc-tilemap-root {
|
|
12
13
|
--oc-font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
13
14
|
--oc-font-mono: "JetBrains Mono", "Fira Code", "Cascadia Code", monospace;
|
|
14
15
|
|
package/src/types/index.ts
CHANGED
|
@@ -33,12 +33,16 @@ export type {
|
|
|
33
33
|
AxisLayout,
|
|
34
34
|
AxisTick,
|
|
35
35
|
BarTableCell,
|
|
36
|
+
BaseLegendLayout,
|
|
37
|
+
CategoricalLegendLayout,
|
|
36
38
|
CategoryTableCell,
|
|
37
39
|
CellStyle,
|
|
38
40
|
ChartLayout,
|
|
39
41
|
CompileOptions,
|
|
40
42
|
CompileTableOptions,
|
|
41
43
|
FlagTableCell,
|
|
44
|
+
GradientColorStop,
|
|
45
|
+
GradientLegendLayout,
|
|
42
46
|
GraphEdgeLayout,
|
|
43
47
|
GraphLayout,
|
|
44
48
|
GraphNodeLayout,
|
|
@@ -78,6 +82,8 @@ export type {
|
|
|
78
82
|
TextStyle,
|
|
79
83
|
TextTableCell,
|
|
80
84
|
TickMarkLayout,
|
|
85
|
+
TileMapLayout,
|
|
86
|
+
TileMapTileMark,
|
|
81
87
|
TooltipContent,
|
|
82
88
|
TooltipField,
|
|
83
89
|
} from './layout';
|
|
@@ -140,6 +146,7 @@ export type {
|
|
|
140
146
|
RadialGradient,
|
|
141
147
|
RangeAnnotation,
|
|
142
148
|
RefLineAnnotation,
|
|
149
|
+
RelativeTimeRef,
|
|
143
150
|
ResolveConfig,
|
|
144
151
|
ResolveMode,
|
|
145
152
|
SankeyEncoding,
|
|
@@ -155,10 +162,18 @@ export type {
|
|
|
155
162
|
TableSpecWithoutData,
|
|
156
163
|
TextAnnotation,
|
|
157
164
|
ThemeConfig,
|
|
165
|
+
TileMapEncoding,
|
|
166
|
+
TileMapPalette,
|
|
167
|
+
TileMapSpec,
|
|
168
|
+
TileMapSpecWithoutData,
|
|
158
169
|
TimeUnit,
|
|
159
170
|
TimeUnitTransform,
|
|
160
171
|
Transform,
|
|
161
172
|
VizSpec,
|
|
173
|
+
WindowFieldDef,
|
|
174
|
+
WindowOp,
|
|
175
|
+
WindowSortField,
|
|
176
|
+
WindowTransform,
|
|
162
177
|
} from './spec';
|
|
163
178
|
export {
|
|
164
179
|
CHART_TYPES,
|
|
@@ -174,6 +189,7 @@ export {
|
|
|
174
189
|
isSankeySpec,
|
|
175
190
|
isTableSpec,
|
|
176
191
|
isTextAnnotation,
|
|
192
|
+
isTileMapSpec,
|
|
177
193
|
MARK_DISPLAY_NAMES,
|
|
178
194
|
MARK_TYPES,
|
|
179
195
|
resolveMarkDef,
|
package/src/types/layout.ts
CHANGED
|
@@ -121,6 +121,8 @@ export interface AxisTick {
|
|
|
121
121
|
position: number;
|
|
122
122
|
/** Formatted label string for display. */
|
|
123
123
|
label: string;
|
|
124
|
+
/** Secondary label text from axis.labelField. */
|
|
125
|
+
subtitle?: string;
|
|
124
126
|
}
|
|
125
127
|
|
|
126
128
|
/** A single gridline with computed positions. */
|
|
@@ -557,7 +559,7 @@ export interface ResolvedAnnotation {
|
|
|
557
559
|
// Legend
|
|
558
560
|
// ---------------------------------------------------------------------------
|
|
559
561
|
|
|
560
|
-
/** A single entry in
|
|
562
|
+
/** A single entry in a categorical legend. */
|
|
561
563
|
export interface LegendEntry {
|
|
562
564
|
/** The label text (category name or range description). */
|
|
563
565
|
label: string;
|
|
@@ -571,16 +573,22 @@ export interface LegendEntry {
|
|
|
571
573
|
overflow?: boolean;
|
|
572
574
|
}
|
|
573
575
|
|
|
574
|
-
/**
|
|
575
|
-
export interface
|
|
576
|
+
/** Base legend layout fields shared by all legend types. */
|
|
577
|
+
export interface BaseLegendLayout {
|
|
576
578
|
/** Where the legend is positioned relative to the chart area. */
|
|
577
579
|
position: 'top' | 'right' | 'bottom' | 'bottom-right' | 'inline';
|
|
578
|
-
/** Legend entries. */
|
|
579
|
-
entries: LegendEntry[];
|
|
580
580
|
/** Bounding box for the legend (pixel coordinates). */
|
|
581
581
|
bounds: Rect;
|
|
582
582
|
/** Entry label style. */
|
|
583
583
|
labelStyle: TextStyle;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/** Categorical legend with discrete swatch entries. */
|
|
587
|
+
export interface CategoricalLegendLayout extends BaseLegendLayout {
|
|
588
|
+
/** Discriminant for legend type. Omitted means categorical (backwards compat). */
|
|
589
|
+
type?: 'categorical';
|
|
590
|
+
/** Legend entries. */
|
|
591
|
+
entries: LegendEntry[];
|
|
584
592
|
/** Swatch size in pixels. */
|
|
585
593
|
swatchSize: number;
|
|
586
594
|
/** Gap between swatch and label. */
|
|
@@ -589,6 +597,29 @@ export interface LegendLayout {
|
|
|
589
597
|
entryGap: number;
|
|
590
598
|
}
|
|
591
599
|
|
|
600
|
+
/** A color stop in a gradient legend. */
|
|
601
|
+
export interface GradientColorStop {
|
|
602
|
+
/** Position along the gradient (0 to 1). */
|
|
603
|
+
offset: number;
|
|
604
|
+
/** CSS color value. */
|
|
605
|
+
color: string;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
/** Gradient legend for continuous color scales. */
|
|
609
|
+
export interface GradientLegendLayout extends BaseLegendLayout {
|
|
610
|
+
/** Discriminant for legend type. */
|
|
611
|
+
type: 'gradient';
|
|
612
|
+
/** Color stops for the gradient bar. */
|
|
613
|
+
colorStops: GradientColorStop[];
|
|
614
|
+
/** Formatted minimum value label. */
|
|
615
|
+
minLabel: string;
|
|
616
|
+
/** Formatted maximum value label. */
|
|
617
|
+
maxLabel: string;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/** Resolved legend layout — either categorical (swatches) or gradient (continuous bar). */
|
|
621
|
+
export type LegendLayout = CategoricalLegendLayout | GradientLegendLayout;
|
|
622
|
+
|
|
592
623
|
// ---------------------------------------------------------------------------
|
|
593
624
|
// Tooltips
|
|
594
625
|
// ---------------------------------------------------------------------------
|
|
@@ -1052,6 +1083,80 @@ export interface SankeyLayout {
|
|
|
1052
1083
|
measureText?: MeasureTextFn;
|
|
1053
1084
|
}
|
|
1054
1085
|
|
|
1086
|
+
// ---------------------------------------------------------------------------
|
|
1087
|
+
// TileMapLayout (engine output for tile map visualizations)
|
|
1088
|
+
// ---------------------------------------------------------------------------
|
|
1089
|
+
|
|
1090
|
+
/** A resolved tile map tile with computed position and visual properties. */
|
|
1091
|
+
export interface TileMapTileMark {
|
|
1092
|
+
type: 'tile';
|
|
1093
|
+
/** Left edge x position. */
|
|
1094
|
+
x: number;
|
|
1095
|
+
/** Top edge y position. */
|
|
1096
|
+
y: number;
|
|
1097
|
+
/** Tile width and height (tiles are square). */
|
|
1098
|
+
size: number;
|
|
1099
|
+
/** Fill color from the sequential color scale. */
|
|
1100
|
+
fill: string;
|
|
1101
|
+
/** Tile border color. */
|
|
1102
|
+
stroke: string;
|
|
1103
|
+
/** Tile border width. */
|
|
1104
|
+
strokeWidth: number;
|
|
1105
|
+
/** Corner radius. */
|
|
1106
|
+
cornerRadius: number;
|
|
1107
|
+
/** US state abbreviation (e.g. "CA", "TX"). */
|
|
1108
|
+
stateCode: string;
|
|
1109
|
+
/** Numeric value (null if state has no data). */
|
|
1110
|
+
value: number | null;
|
|
1111
|
+
/** Formatted value string (e.g. "12,000" or "–" for missing). */
|
|
1112
|
+
formattedValue: string;
|
|
1113
|
+
/** Whether this state has data. False for missing states. */
|
|
1114
|
+
hasData: boolean;
|
|
1115
|
+
/** State code text label (positioned inside tile). */
|
|
1116
|
+
label: ResolvedLabel;
|
|
1117
|
+
/** Value text label (positioned inside tile, below state code). */
|
|
1118
|
+
valueLabel: ResolvedLabel;
|
|
1119
|
+
/** Accessibility attributes. */
|
|
1120
|
+
aria: MarkAria;
|
|
1121
|
+
/** Index for stagger animation ordering. */
|
|
1122
|
+
animationIndex?: number;
|
|
1123
|
+
/** Original data associated with this tile. */
|
|
1124
|
+
data: Record<string, unknown>;
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
/**
|
|
1128
|
+
* TileMapLayout: the complete engine output for tile map visualizations.
|
|
1129
|
+
*
|
|
1130
|
+
* Contains everything an adapter needs to render the tile map: dimensions,
|
|
1131
|
+
* chrome, tiles, gradient legend, tooltip descriptors, and accessibility metadata.
|
|
1132
|
+
*/
|
|
1133
|
+
export interface TileMapLayout {
|
|
1134
|
+
/** The tile map drawing area (after chrome and legend are subtracted). */
|
|
1135
|
+
area: Rect;
|
|
1136
|
+
/** Resolved chrome text elements with positions and styles. */
|
|
1137
|
+
chrome: ResolvedChrome;
|
|
1138
|
+
/** Resolved tile marks with positions, colors, and labels. */
|
|
1139
|
+
tiles: TileMapTileMark[];
|
|
1140
|
+
/** Gradient color legend (null if legend is hidden). */
|
|
1141
|
+
gradientLegend: GradientLegendLayout | null;
|
|
1142
|
+
/** Tooltip content descriptors keyed by state code. */
|
|
1143
|
+
tooltipDescriptors: Map<string, TooltipContent>;
|
|
1144
|
+
/** Accessibility metadata for the tile map. */
|
|
1145
|
+
a11y: A11yMetadata;
|
|
1146
|
+
/** Resolved theme. */
|
|
1147
|
+
theme: ResolvedTheme;
|
|
1148
|
+
/** Total SVG width in pixels. */
|
|
1149
|
+
width: number;
|
|
1150
|
+
/** Total SVG height in pixels. */
|
|
1151
|
+
height: number;
|
|
1152
|
+
/** Resolved animation config (undefined if animation is disabled). */
|
|
1153
|
+
animation: ResolvedAnimation | undefined;
|
|
1154
|
+
/** Whether to render the watermark. */
|
|
1155
|
+
watermark: boolean;
|
|
1156
|
+
/** Text measurement function for the rendering adapter. */
|
|
1157
|
+
measureText: MeasureTextFn;
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1055
1160
|
// ---------------------------------------------------------------------------
|
|
1056
1161
|
// Compile options (engine input)
|
|
1057
1162
|
// ---------------------------------------------------------------------------
|
package/src/types/spec.ts
CHANGED
|
@@ -240,6 +240,8 @@ export interface AxisConfig {
|
|
|
240
240
|
labelPadding?: number;
|
|
241
241
|
/** Color override for axis tick labels and title. Useful in dual-axis charts to match axis color to its series. */
|
|
242
242
|
labelColor?: string;
|
|
243
|
+
/** Secondary data field to display alongside each tick label. Renders in lighter weight/color. Only effective on categorical y-axis labels (horizontal bar charts). */
|
|
244
|
+
labelField?: string;
|
|
243
245
|
}
|
|
244
246
|
|
|
245
247
|
/** Scale configuration for an encoding channel. */
|
|
@@ -878,6 +880,12 @@ export interface ChartSpec {
|
|
|
878
880
|
* - AnimationConfig: full control via enter/update/exit phases
|
|
879
881
|
*/
|
|
880
882
|
animation?: AnimationSpec;
|
|
883
|
+
/**
|
|
884
|
+
* Show a vertical crosshair line that tracks the nearest data point on
|
|
885
|
+
* line and area charts. Only active when a voronoi overlay is present.
|
|
886
|
+
* Defaults to false.
|
|
887
|
+
*/
|
|
888
|
+
crosshair?: boolean;
|
|
881
889
|
/**
|
|
882
890
|
* Render order within a LayerSpec. Higher values render on top.
|
|
883
891
|
* When omitted, layers render in array order (later layers paint on top).
|
|
@@ -1120,6 +1128,58 @@ export interface SankeySpec {
|
|
|
1120
1128
|
valueFormat?: string;
|
|
1121
1129
|
}
|
|
1122
1130
|
|
|
1131
|
+
// ---------------------------------------------------------------------------
|
|
1132
|
+
// TileMap spec (US state tile grid map)
|
|
1133
|
+
// ---------------------------------------------------------------------------
|
|
1134
|
+
|
|
1135
|
+
/** Encoding channels specific to tile map visualizations. */
|
|
1136
|
+
export interface TileMapEncoding {
|
|
1137
|
+
/** State code field (required, nominal). Maps to US state abbreviations. */
|
|
1138
|
+
state: EncodingChannel;
|
|
1139
|
+
/** Value field (required, quantitative). Maps to the sequential color scale. */
|
|
1140
|
+
value: EncodingChannel;
|
|
1141
|
+
/** Tooltip encoding (optional). */
|
|
1142
|
+
tooltip?: EncodingChannel | EncodingChannel[];
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
/** Sequential color palette names available for tile maps. */
|
|
1146
|
+
export type TileMapPalette = 'blue' | 'green' | 'orange' | 'purple';
|
|
1147
|
+
|
|
1148
|
+
export interface TileMapSpec {
|
|
1149
|
+
/** Discriminant: always "tilemap". */
|
|
1150
|
+
type: 'tilemap';
|
|
1151
|
+
/**
|
|
1152
|
+
* Data for the tile map. Accepts either:
|
|
1153
|
+
* - A record mapping state codes to numeric values: `{ "CA": 12000, "TX": 8500 }`
|
|
1154
|
+
* - Tabular data rows with state and value fields (requires encoding)
|
|
1155
|
+
*/
|
|
1156
|
+
data: Record<string, number | null> | DataRow[];
|
|
1157
|
+
/**
|
|
1158
|
+
* Encoding channels mapping data fields to visual properties.
|
|
1159
|
+
* Required when data is DataRow[]. Auto-generated when data is a record map.
|
|
1160
|
+
*/
|
|
1161
|
+
encoding?: TileMapEncoding;
|
|
1162
|
+
/** Sequential color palette. Defaults to 'blue'. */
|
|
1163
|
+
palette?: TileMapPalette;
|
|
1164
|
+
/** Editorial chrome (title, subtitle, source, byline, footer). */
|
|
1165
|
+
chrome?: Chrome;
|
|
1166
|
+
/** Legend display configuration. */
|
|
1167
|
+
legend?: LegendConfig;
|
|
1168
|
+
/** Theme configuration overrides. */
|
|
1169
|
+
theme?: ThemeConfig;
|
|
1170
|
+
/** Dark mode behavior. Defaults to "off". */
|
|
1171
|
+
darkMode?: DarkMode;
|
|
1172
|
+
/** Whether to show the tryOpenData.ai watermark. Defaults to true. */
|
|
1173
|
+
watermark?: boolean;
|
|
1174
|
+
/** Animation configuration for entrance animations. */
|
|
1175
|
+
animation?: AnimationSpec;
|
|
1176
|
+
/**
|
|
1177
|
+
* d3-format string applied to tile values, legend labels, and tooltips.
|
|
1178
|
+
* Examples: ".1f" for one decimal, "$,.0f" for currency, "~s" for SI.
|
|
1179
|
+
*/
|
|
1180
|
+
valueFormat?: string;
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1123
1183
|
/**
|
|
1124
1184
|
* Top-level visualization spec: union discriminated by structural shape.
|
|
1125
1185
|
*
|
|
@@ -1128,8 +1188,9 @@ export interface SankeySpec {
|
|
|
1128
1188
|
* - TableSpec: has `type: 'table'`
|
|
1129
1189
|
* - GraphSpec: has `type: 'graph'`
|
|
1130
1190
|
* - SankeySpec: has `type: 'sankey'`
|
|
1191
|
+
* - TileMapSpec: has `type: 'tilemap'`
|
|
1131
1192
|
*/
|
|
1132
|
-
export type VizSpec = ChartSpec | LayerSpec | TableSpec | GraphSpec | SankeySpec;
|
|
1193
|
+
export type VizSpec = ChartSpec | LayerSpec | TableSpec | GraphSpec | SankeySpec | TileMapSpec;
|
|
1133
1194
|
|
|
1134
1195
|
/** Chart spec without runtime data, for persistence/storage. */
|
|
1135
1196
|
export type ChartSpecWithoutData = Omit<ChartSpec, 'data'>;
|
|
@@ -1139,12 +1200,15 @@ export type TableSpecWithoutData = Omit<TableSpec, 'data' | 'columns'>;
|
|
|
1139
1200
|
export type GraphSpecWithoutData = Omit<GraphSpec, 'nodes' | 'edges'>;
|
|
1140
1201
|
/** Sankey spec without runtime data, for persistence/storage. */
|
|
1141
1202
|
export type SankeySpecWithoutData = Omit<SankeySpec, 'data'>;
|
|
1203
|
+
/** TileMap spec without runtime data, for persistence/storage. */
|
|
1204
|
+
export type TileMapSpecWithoutData = Omit<TileMapSpec, 'data'>;
|
|
1142
1205
|
/** Union of data-stripped spec types for persistence/storage. */
|
|
1143
1206
|
export type StoredVizSpec =
|
|
1144
1207
|
| ChartSpecWithoutData
|
|
1145
1208
|
| TableSpecWithoutData
|
|
1146
1209
|
| GraphSpecWithoutData
|
|
1147
|
-
| SankeySpecWithoutData
|
|
1210
|
+
| SankeySpecWithoutData
|
|
1211
|
+
| TileMapSpecWithoutData;
|
|
1148
1212
|
|
|
1149
1213
|
// ---------------------------------------------------------------------------
|
|
1150
1214
|
// Transforms (Vega-Lite aligned)
|
|
@@ -1165,6 +1229,13 @@ export interface LogicalNot<T> {
|
|
|
1165
1229
|
not: T;
|
|
1166
1230
|
}
|
|
1167
1231
|
|
|
1232
|
+
/** A relative-time reference that resolves against the data extent. */
|
|
1233
|
+
export interface RelativeTimeRef {
|
|
1234
|
+
anchor: 'max' | 'min';
|
|
1235
|
+
offset: number;
|
|
1236
|
+
unit: 'day' | 'week' | 'month' | 'quarter' | 'year';
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1168
1239
|
/** A predicate that tests a field value against a condition. */
|
|
1169
1240
|
export interface FieldPredicate {
|
|
1170
1241
|
/** Data field to test. */
|
|
@@ -1172,15 +1243,15 @@ export interface FieldPredicate {
|
|
|
1172
1243
|
/** Equals comparison. */
|
|
1173
1244
|
equal?: unknown;
|
|
1174
1245
|
/** Less than. */
|
|
1175
|
-
lt?: number;
|
|
1246
|
+
lt?: number | RelativeTimeRef;
|
|
1176
1247
|
/** Less than or equal. */
|
|
1177
|
-
lte?: number;
|
|
1248
|
+
lte?: number | RelativeTimeRef;
|
|
1178
1249
|
/** Greater than. */
|
|
1179
|
-
gt?: number;
|
|
1250
|
+
gt?: number | RelativeTimeRef;
|
|
1180
1251
|
/** Greater than or equal. */
|
|
1181
|
-
gte?: number;
|
|
1252
|
+
gte?: number | RelativeTimeRef;
|
|
1182
1253
|
/** Inclusive range [min, max]. */
|
|
1183
|
-
range?: [number, number];
|
|
1254
|
+
range?: [number | RelativeTimeRef, number | RelativeTimeRef];
|
|
1184
1255
|
/** Value is one of these. */
|
|
1185
1256
|
oneOf?: unknown[];
|
|
1186
1257
|
/** Whether the value is valid (non-null, non-undefined, non-NaN). */
|
|
@@ -1286,6 +1357,33 @@ export interface FoldTransform {
|
|
|
1286
1357
|
as?: [string, string];
|
|
1287
1358
|
}
|
|
1288
1359
|
|
|
1360
|
+
/** Window operation types for computing values relative to other rows. */
|
|
1361
|
+
export type WindowOp = 'lag' | 'lead' | 'diff' | 'pct_change' | 'cumsum' | 'rank' | 'first_value';
|
|
1362
|
+
|
|
1363
|
+
/** Sort field definition for window transforms. */
|
|
1364
|
+
export interface WindowSortField {
|
|
1365
|
+
field: string;
|
|
1366
|
+
order?: 'ascending' | 'descending';
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
/** Window field definition specifying which operation to compute. */
|
|
1370
|
+
export interface WindowFieldDef {
|
|
1371
|
+
op: WindowOp;
|
|
1372
|
+
field: string;
|
|
1373
|
+
/** Row offset for lag/lead/diff/pct_change. Defaults to 1. */
|
|
1374
|
+
offset?: number;
|
|
1375
|
+
as: string;
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
/** Window transform: computes values relative to other rows in sort order within a partition. */
|
|
1379
|
+
export interface WindowTransform {
|
|
1380
|
+
window: WindowFieldDef[];
|
|
1381
|
+
/** Fields to sort by within each partition. */
|
|
1382
|
+
sort: WindowSortField[];
|
|
1383
|
+
/** Fields to partition (group) by. Each group is windowed independently. */
|
|
1384
|
+
groupby?: string[];
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1289
1387
|
/** Discriminated union of all transform types. */
|
|
1290
1388
|
export type Transform =
|
|
1291
1389
|
| FilterTransform
|
|
@@ -1293,7 +1391,8 @@ export type Transform =
|
|
|
1293
1391
|
| CalculateTransform
|
|
1294
1392
|
| TimeUnitTransform
|
|
1295
1393
|
| AggregateTransform
|
|
1296
|
-
| FoldTransform
|
|
1394
|
+
| FoldTransform
|
|
1395
|
+
| WindowTransform;
|
|
1297
1396
|
|
|
1298
1397
|
// ---------------------------------------------------------------------------
|
|
1299
1398
|
// Conditional encoding (Vega-Lite aligned)
|
|
@@ -1412,6 +1511,11 @@ export function isSankeySpec(spec: VizSpec | Record<string, unknown>): spec is S
|
|
|
1412
1511
|
return 'type' in spec && (spec as Record<string, unknown>).type === 'sankey';
|
|
1413
1512
|
}
|
|
1414
1513
|
|
|
1514
|
+
/** Check if a spec is a TileMapSpec. */
|
|
1515
|
+
export function isTileMapSpec(spec: VizSpec | Record<string, unknown>): spec is TileMapSpec {
|
|
1516
|
+
return 'type' in spec && (spec as Record<string, unknown>).type === 'tilemap';
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1415
1519
|
// ---------------------------------------------------------------------------
|
|
1416
1520
|
// Annotation type guards
|
|
1417
1521
|
// ---------------------------------------------------------------------------
|