Rahul Ravikumar | 9c70b8f | 2023-06-15 13:29:04 -0700 | [diff] [blame] | 1 | <script lang="ts"> |
Rahul Ravikumar | a5805d1 | 2023-08-22 12:38:40 -0700 | [diff] [blame] | 2 | import type { ChartType, LegendItem, Point, TooltipItem } from "chart.js"; |
Rahul Ravikumar | 9c70b8f | 2023-06-15 13:29:04 -0700 | [diff] [blame] | 3 | import { Chart } from "chart.js/auto"; |
Rahul Ravikumar | 4b6dfd1 | 2023-08-18 16:51:41 -0700 | [diff] [blame] | 4 | import { createEventDispatcher, onMount } from "svelte"; |
Rahul Ravikumar | 333932c | 2023-07-20 14:49:49 -0700 | [diff] [blame] | 5 | import { writable, type Writable } from "svelte/store"; |
Rahul Ravikumar | 333932c | 2023-07-20 14:49:49 -0700 | [diff] [blame] | 6 | import { saveToClipboard as save } from "../clipboard.js"; |
Rahul Ravikumar | a5805d1 | 2023-08-22 12:38:40 -0700 | [diff] [blame] | 7 | import { LegendPlugin } from "../plugins.js"; |
| 8 | import type { Data } from "../types/chart.js"; |
Rahul Ravikumar | 4b6dfd1 | 2023-08-18 16:51:41 -0700 | [diff] [blame] | 9 | import type { Controls, ControlsEvent } from "../types/events.js"; |
Rahul Ravikumar | a5805d1 | 2023-08-22 12:38:40 -0700 | [diff] [blame] | 10 | import Legend from "./Legend.svelte"; |
| 11 | import { isSampled } from "../transforms/standard-mappers.js"; |
Rahul Ravikumar | 9c70b8f | 2023-06-15 13:29:04 -0700 | [diff] [blame] | 12 | |
Rahul Ravikumar | 333932c | 2023-07-20 14:49:49 -0700 | [diff] [blame] | 13 | export let data: Data; |
| 14 | export let chartType: ChartType = "line"; |
Rahul Ravikumar | e39246d | 2023-07-26 16:49:05 -0700 | [diff] [blame] | 15 | export let isExperimental: boolean = false; |
Rahul Ravikumar | 4b6dfd1 | 2023-08-18 16:51:41 -0700 | [diff] [blame] | 16 | export let showHistogramControls: boolean = false; |
Rahul Ravikumar | 9c70b8f | 2023-06-15 13:29:04 -0700 | [diff] [blame] | 17 | |
Rahul Ravikumar | 333932c | 2023-07-20 14:49:49 -0700 | [diff] [blame] | 18 | // State |
Rahul Ravikumar | 4b6dfd1 | 2023-08-18 16:51:41 -0700 | [diff] [blame] | 19 | let controlsDispatcher = createEventDispatcher<ControlsEvent>(); |
Rahul Ravikumar | 333932c | 2023-07-20 14:49:49 -0700 | [diff] [blame] | 20 | let element: HTMLCanvasElement; |
Rahul Ravikumar | 4b6dfd1 | 2023-08-18 16:51:41 -0700 | [diff] [blame] | 21 | let buckets: Writable<number> = writable(100); |
Rahul Ravikumar | 333932c | 2023-07-20 14:49:49 -0700 | [diff] [blame] | 22 | let chart: Writable<Chart | null> = writable(null); |
| 23 | let items: Writable<LegendItem[] | null> = writable(null); |
| 24 | |
Rahul Ravikumar | 16ffa9e | 2024-11-07 15:09:35 -0800 | [diff] [blame] | 25 | $: { |
| 26 | if ($chart) { |
| 27 | $chart.data = data; |
| 28 | $chart.update(); |
| 29 | } |
| 30 | } |
| 31 | |
Rahul Ravikumar | 333932c | 2023-07-20 14:49:49 -0700 | [diff] [blame] | 32 | // Effects |
Rahul Ravikumar | 9c70b8f | 2023-06-15 13:29:04 -0700 | [diff] [blame] | 33 | onMount(() => { |
Rahul Ravikumar | 16ffa9e | 2024-11-07 15:09:35 -0800 | [diff] [blame] | 34 | const onUpdate = (updated: Chart) => { |
| 35 | $chart = updated; |
Rahul Ravikumar | e39246d | 2023-07-26 16:49:05 -0700 | [diff] [blame] | 36 | // Bad typings. |
Rahul Ravikumar | 16ffa9e | 2024-11-07 15:09:35 -0800 | [diff] [blame] | 37 | const legend = updated.options.plugins?.legend as any; |
| 38 | $items = legend.labels.generateLabels($chart); |
Rahul Ravikumar | 9c70b8f | 2023-06-15 13:29:04 -0700 | [diff] [blame] | 39 | }; |
| 40 | const plugins = { |
Rahul Ravikumar | a5805d1 | 2023-08-22 12:38:40 -0700 | [diff] [blame] | 41 | tooltip: { |
| 42 | callbacks: { |
Rahul Ravikumar | ab5ea66 | 2024-06-06 16:05:41 -0700 | [diff] [blame] | 43 | label: (context: TooltipItem<typeof chartType>): string | void | string[] => { |
Rahul Ravikumar | a5805d1 | 2023-08-22 12:38:40 -0700 | [diff] [blame] | 44 | // TODO: Configure Tooltips |
| 45 | // https://www.chartjs.org/docs/latest/configuration/tooltip.html |
| 46 | const label = context.dataset.label; |
| 47 | const rp = context.raw as Point; |
| 48 | const frequency = context.parsed.y; |
| 49 | if (isSampled(label)) { |
| 50 | const fx = rp.x.toFixed(2); |
| 51 | return `${label}: ${fx} F(${frequency})`; |
| 52 | } else { |
| 53 | // Fallback to default behavior |
Rahul Ravikumar | ab5ea66 | 2024-06-06 16:05:41 -0700 | [diff] [blame] | 54 | return; |
Rahul Ravikumar | a5805d1 | 2023-08-22 12:38:40 -0700 | [diff] [blame] | 55 | } |
| 56 | }, |
| 57 | }, |
| 58 | }, |
Rahul Ravikumar | 9c70b8f | 2023-06-15 13:29:04 -0700 | [diff] [blame] | 59 | legend: { |
| 60 | display: false, |
| 61 | }, |
| 62 | benchmark: { |
| 63 | onUpdate: onUpdate, |
| 64 | }, |
| 65 | }; |
Rahul Ravikumar | 333932c | 2023-07-20 14:49:49 -0700 | [diff] [blame] | 66 | $chart = new Chart(element, { |
| 67 | data: data, |
| 68 | type: chartType, |
| 69 | plugins: [LegendPlugin], |
| 70 | options: { |
| 71 | plugins: plugins, |
| 72 | }, |
| 73 | }); |
Rahul Ravikumar | 9c70b8f | 2023-06-15 13:29:04 -0700 | [diff] [blame] | 74 | }); |
| 75 | |
Rahul Ravikumar | 333932c | 2023-07-20 14:49:49 -0700 | [diff] [blame] | 76 | // Copy to clip board |
| 77 | async function copy(event: Event) { |
Rahul Ravikumar | 9c70b8f | 2023-06-15 13:29:04 -0700 | [diff] [blame] | 78 | if ($chart) { |
Rahul Ravikumar | 333932c | 2023-07-20 14:49:49 -0700 | [diff] [blame] | 79 | await save($chart); |
Rahul Ravikumar | 9c70b8f | 2023-06-15 13:29:04 -0700 | [diff] [blame] | 80 | } |
| 81 | } |
Rahul Ravikumar | 4b6dfd1 | 2023-08-18 16:51:41 -0700 | [diff] [blame] | 82 | |
| 83 | function onHistogramChanged(event: Event) { |
| 84 | const element = event.target as EventTarget & HTMLInputElement; |
| 85 | const oldValue = $buckets; |
| 86 | $buckets = parseInt(element.value, 10); |
| 87 | if (oldValue != $buckets) { |
| 88 | let controls: Controls = { |
| 89 | buckets: $buckets, |
| 90 | }; |
| 91 | controlsDispatcher("controls", controls); |
| 92 | } |
| 93 | } |
Rahul Ravikumar | 9c70b8f | 2023-06-15 13:29:04 -0700 | [diff] [blame] | 94 | </script> |
| 95 | |
| 96 | <article> |
Rahul Ravikumar | 333932c | 2023-07-20 14:49:49 -0700 | [diff] [blame] | 97 | <div class="toolbar"> |
| 98 | <button |
| 99 | class="btn outline" |
| 100 | data-tooltip="Copy chart to clipboard." |
| 101 | on:click={copy} |
| 102 | > |
| 103 | ⎘ |
| 104 | </button> |
| 105 | </div> |
Rahul Ravikumar | 16ffa9e | 2024-11-07 15:09:35 -0800 | [diff] [blame] | 106 | <canvas class="chart" bind:this={element}> </canvas> |
Rahul Ravikumar | 4b6dfd1 | 2023-08-18 16:51:41 -0700 | [diff] [blame] | 107 | {#if showHistogramControls} |
| 108 | <div class="controls"> |
| 109 | <label for="buckets"> |
| 110 | Histogram |
| 111 | <input |
| 112 | type="range" |
| 113 | data-tooltip={$buckets} |
| 114 | data-placement="right" |
| 115 | min="10" |
| 116 | max="250" |
| 117 | value={$buckets} |
| 118 | id="buckets" |
| 119 | name="buckets" |
| 120 | on:change={onHistogramChanged} |
| 121 | /> |
| 122 | </label> |
| 123 | </div> |
| 124 | {/if} |
Rahul Ravikumar | e39246d | 2023-07-26 16:49:05 -0700 | [diff] [blame] | 125 | {#if isExperimental} |
| 126 | <footer class="slim"> |
| 127 | <section class="experimental"> |
| 128 | <kbd>Experimental</kbd> |
| 129 | </section> |
| 130 | </footer> |
| 131 | {/if} |
Rahul Ravikumar | 9c70b8f | 2023-06-15 13:29:04 -0700 | [diff] [blame] | 132 | </article> |
| 133 | |
Rahul Ravikumar | ab5ea66 | 2024-06-06 16:05:41 -0700 | [diff] [blame] | 134 | {#if $chart && $items} |
Rahul Ravikumar | 333932c | 2023-07-20 14:49:49 -0700 | [diff] [blame] | 135 | <Legend chart={$chart} items={$items} /> |
Rahul Ravikumar | 9c70b8f | 2023-06-15 13:29:04 -0700 | [diff] [blame] | 136 | {/if} |
| 137 | |
| 138 | <style> |
Rahul Ravikumar | 9c70b8f | 2023-06-15 13:29:04 -0700 | [diff] [blame] | 139 | .chart { |
| 140 | width: 100%; |
| 141 | } |
Rahul Ravikumar | 333932c | 2023-07-20 14:49:49 -0700 | [diff] [blame] | 142 | .toolbar { |
| 143 | padding: 0; |
Rahul Ravikumar | 9c70b8f | 2023-06-15 13:29:04 -0700 | [diff] [blame] | 144 | display: flex; |
| 145 | flex-direction: row; |
Rahul Ravikumar | 333932c | 2023-07-20 14:49:49 -0700 | [diff] [blame] | 146 | justify-content: flex-end; |
Rahul Ravikumar | 9c70b8f | 2023-06-15 13:29:04 -0700 | [diff] [blame] | 147 | } |
Rahul Ravikumar | 333932c | 2023-07-20 14:49:49 -0700 | [diff] [blame] | 148 | .toolbar .btn { |
| 149 | width: auto; |
| 150 | height: auto; |
| 151 | border: none; |
| 152 | padding: 5px; |
Rahul Ravikumar | 9c70b8f | 2023-06-15 13:29:04 -0700 | [diff] [blame] | 153 | } |
Rahul Ravikumar | 4b6dfd1 | 2023-08-18 16:51:41 -0700 | [diff] [blame] | 154 | .controls { |
| 155 | margin-top: 20px; |
| 156 | width: 100%; |
| 157 | } |
Rahul Ravikumar | e39246d | 2023-07-26 16:49:05 -0700 | [diff] [blame] | 158 | .slim { |
| 159 | margin-bottom: 0px; |
| 160 | padding: 0; |
| 161 | } |
Rahul Ravikumar | e39246d | 2023-07-26 16:49:05 -0700 | [diff] [blame] | 162 | .experimental { |
| 163 | display: flex; |
| 164 | flex-direction: row; |
| 165 | flex-wrap: nowrap; |
| 166 | justify-content: center; |
| 167 | margin-bottom: 0px; |
| 168 | } |
Rahul Ravikumar | 9c70b8f | 2023-06-15 13:29:04 -0700 | [diff] [blame] | 169 | </style> |