time series component (input) (#498)

This commit is contained in:
pngwn 2022-02-01 08:39:17 +00:00 committed by GitHub
parent aab74369af
commit 7a67fa09bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 623 additions and 16 deletions

View File

@ -11,6 +11,9 @@
"@rollup/plugin-replace": "^3.0.1",
"autoprefixer": "^9.8.8",
"cropperjs": "^1.5.12",
"d3-dsv": "^3.0.1",
"d3-scale": "^4.0.2",
"d3-shape": "^3.1.0",
"lazy-brush": "^1.0.1",
"mime-types": "^2.1.34",
"node-sass": "^7.0.1",
@ -35,7 +38,7 @@
"rollup-plugin-livereload": "^2.0.0",
"rollup-plugin-svelte": "^7.0.0",
"rollup-plugin-terser": "^7.0.0",
"svelte": "^3.0.0"
"svelte": "^3.46.3"
}
},
"node_modules/@babel/code-frame": {
@ -1287,6 +1290,132 @@
"integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==",
"optional": true
},
"node_modules/d3-array": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.1.1.tgz",
"integrity": "sha512-33qQ+ZoZlli19IFiQx4QEpf2CBEayMRzhlisJHSCsSUbDXv6ZishqS1x7uFVClKG4Wr7rZVHvaAttoLow6GqdQ==",
"dependencies": {
"internmap": "1 - 2"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-color": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.0.1.tgz",
"integrity": "sha512-6/SlHkDOBLyQSJ1j1Ghs82OIUXpKWlR0hCsw0XrLSQhuUPuCSmLQ1QPH98vpnQxMUQM2/gfAkUEWsupVpd9JGw==",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-dsv": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz",
"integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==",
"dependencies": {
"commander": "7",
"iconv-lite": "0.6",
"rw": "1"
},
"bin": {
"csv2json": "bin/dsv2json.js",
"csv2tsv": "bin/dsv2dsv.js",
"dsv2dsv": "bin/dsv2dsv.js",
"dsv2json": "bin/dsv2json.js",
"json2csv": "bin/json2dsv.js",
"json2dsv": "bin/json2dsv.js",
"json2tsv": "bin/json2dsv.js",
"tsv2csv": "bin/dsv2dsv.js",
"tsv2json": "bin/dsv2json.js"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-dsv/node_modules/commander": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
"engines": {
"node": ">= 10"
}
},
"node_modules/d3-format": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
"integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-interpolate": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
"integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
"dependencies": {
"d3-color": "1 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-path": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.0.1.tgz",
"integrity": "sha512-gq6gZom9AFZby0YLduxT1qmrp4xpBA1YZr19OI717WIdKE2OM5ETq5qrHLb301IgxhLwcuxvGZVLeeWc/k1I6w==",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-scale": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
"integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
"dependencies": {
"d3-array": "2.10.0 - 3",
"d3-format": "1 - 3",
"d3-interpolate": "1.2.0 - 3",
"d3-time": "2.1.1 - 3",
"d3-time-format": "2 - 4"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-shape": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.1.0.tgz",
"integrity": "sha512-tGDh1Muf8kWjEDT/LswZJ8WF85yDZLvVJpYU9Nq+8+yW1Z5enxrmXOhTArlkaElU+CTn0OTVNli+/i+HP45QEQ==",
"dependencies": {
"d3-path": "1 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-time": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.0.0.tgz",
"integrity": "sha512-zmV3lRnlaLI08y9IMRXSDshQb5Nj77smnfpnd2LrBa/2K281Jijactokeak14QacHs/kKq0AQ121nidNYlarbQ==",
"dependencies": {
"d3-array": "2 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-time-format": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
"integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
"dependencies": {
"d3-time": "1 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/dashdash": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
@ -2136,7 +2265,6 @@
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"optional": true,
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
@ -2248,6 +2376,14 @@
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"node_modules/internmap": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
"integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
"engines": {
"node": ">=12"
}
},
"node_modules/ip": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
@ -4833,6 +4969,11 @@
"queue-microtask": "^1.2.2"
}
},
"node_modules/rw": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
"integrity": "sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q="
},
"node_modules/sade": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/sade/-/sade-1.8.0.tgz",
@ -5371,9 +5512,9 @@
}
},
"node_modules/svelte": {
"version": "3.44.3",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.44.3.tgz",
"integrity": "sha512-aGgrNCip5PQFNfq9e9tmm7EYxWLVHoFsEsmKrtOeRD8dmoGDdyTQ+21xd7qgFd8MNdKGSYvg7F9dr+Tc0yDymg==",
"version": "3.46.3",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.46.3.tgz",
"integrity": "sha512-mTOXvv74CVQqJHqoIZDprVfRKVVmYNadXP0VKnOEA54223kLGCr1nMrifS4Zx29qMt/xRB38Eq1D7dDH/fM8fQ==",
"engines": {
"node": ">= 8"
}
@ -7121,6 +7262,90 @@
}
}
},
"d3-array": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.1.1.tgz",
"integrity": "sha512-33qQ+ZoZlli19IFiQx4QEpf2CBEayMRzhlisJHSCsSUbDXv6ZishqS1x7uFVClKG4Wr7rZVHvaAttoLow6GqdQ==",
"requires": {
"internmap": "1 - 2"
}
},
"d3-color": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.0.1.tgz",
"integrity": "sha512-6/SlHkDOBLyQSJ1j1Ghs82OIUXpKWlR0hCsw0XrLSQhuUPuCSmLQ1QPH98vpnQxMUQM2/gfAkUEWsupVpd9JGw=="
},
"d3-dsv": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz",
"integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==",
"requires": {
"commander": "7",
"iconv-lite": "0.6",
"rw": "1"
},
"dependencies": {
"commander": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="
}
}
},
"d3-format": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
"integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA=="
},
"d3-interpolate": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
"integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
"requires": {
"d3-color": "1 - 3"
}
},
"d3-path": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.0.1.tgz",
"integrity": "sha512-gq6gZom9AFZby0YLduxT1qmrp4xpBA1YZr19OI717WIdKE2OM5ETq5qrHLb301IgxhLwcuxvGZVLeeWc/k1I6w=="
},
"d3-scale": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
"integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
"requires": {
"d3-array": "2.10.0 - 3",
"d3-format": "1 - 3",
"d3-interpolate": "1.2.0 - 3",
"d3-time": "2.1.1 - 3",
"d3-time-format": "2 - 4"
}
},
"d3-shape": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.1.0.tgz",
"integrity": "sha512-tGDh1Muf8kWjEDT/LswZJ8WF85yDZLvVJpYU9Nq+8+yW1Z5enxrmXOhTArlkaElU+CTn0OTVNli+/i+HP45QEQ==",
"requires": {
"d3-path": "1 - 3"
}
},
"d3-time": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.0.0.tgz",
"integrity": "sha512-zmV3lRnlaLI08y9IMRXSDshQb5Nj77smnfpnd2LrBa/2K281Jijactokeak14QacHs/kKq0AQ121nidNYlarbQ==",
"requires": {
"d3-array": "2 - 3"
}
},
"d3-time-format": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
"integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
"requires": {
"d3-time": "1 - 3"
}
},
"dashdash": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
@ -7769,7 +7994,6 @@
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"optional": true,
"requires": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
}
@ -7851,6 +8075,11 @@
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"internmap": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
"integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="
},
"ip": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
@ -9709,6 +9938,11 @@
"queue-microtask": "^1.2.2"
}
},
"rw": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
"integrity": "sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q="
},
"sade": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/sade/-/sade-1.8.0.tgz",
@ -10124,9 +10358,9 @@
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="
},
"svelte": {
"version": "3.44.3",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.44.3.tgz",
"integrity": "sha512-aGgrNCip5PQFNfq9e9tmm7EYxWLVHoFsEsmKrtOeRD8dmoGDdyTQ+21xd7qgFd8MNdKGSYvg7F9dr+Tc0yDymg=="
"version": "3.46.3",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.46.3.tgz",
"integrity": "sha512-mTOXvv74CVQqJHqoIZDprVfRKVVmYNadXP0VKnOEA54223kLGCr1nMrifS4Zx29qMt/xRB38Eq1D7dDH/fM8fQ=="
},
"svelte-preprocess": {
"version": "4.10.1",

View File

@ -19,12 +19,15 @@
"rollup-plugin-livereload": "^2.0.0",
"rollup-plugin-svelte": "^7.0.0",
"rollup-plugin-terser": "^7.0.0",
"svelte": "^3.0.0"
"svelte": "^3.46.3"
},
"dependencies": {
"@rollup/plugin-replace": "^3.0.1",
"autoprefixer": "^9.8.8",
"cropperjs": "^1.5.12",
"d3-dsv": "^3.0.1",
"d3-scale": "^4.0.2",
"d3-shape": "^3.1.0",
"lazy-brush": "^1.0.1",
"mime-types": "^2.1.34",
"node-sass": "^7.0.1",

View File

@ -10,6 +10,7 @@ import InputSlider from "./input/Slider/config.js";
import InputTextbox from "./input/Textbox/config.js";
import InputVideo from "./input/Video/config.js";
import InputDataFrame from "./input/DataFrame/config.js";
import InputTimeSeries from './input/TimeSeries/config.js';
import OutputAudio from "./output/Audio/config.js";
import OutputCarousel from "./output/Carousel/config.js";
@ -22,6 +23,7 @@ import OutputJson from "./output/Json/config.js";
import OutputLabel from "./output/Label/config.js";
import OutputTextbox from "./output/Textbox/config.js";
import OutputVideo from "./output/Video/config.js";
import OutputTimeSeries from './output/TimeSeries/config.js'
import Dummy from "./Dummy.svelte"
@ -37,7 +39,7 @@ export const input_component_map = {
"radio": InputRadio,
"slider": InputSlider,
"textbox": InputTextbox,
"timeseries": {"component": Dummy, "example": Dummy},
"timeseries": InputTimeSeries,
"video": InputVideo,
}
@ -52,6 +54,6 @@ export const output_component_map = {
"json": OutputJson,
"label": OutputLabel,
"textbox": OutputTextbox,
"timeseries": {"component": Dummy, "example": Dummy},
"timeseries": OutputTimeSeries,
"video": OutputVideo,
}

View File

@ -60,7 +60,6 @@
onDestroy(() => {
if (recorder) {
console.log(recorder);
recorder.stop();
}
});

View File

@ -0,0 +1,76 @@
<script>
import Upload from "../../utils/Upload.svelte";
import Chart from "../../utils/Chart.svelte";
export let value, setValue, theme, y, x;
let _value;
function data_uri_to_blob(data_uri) {
var byte_str = atob(data_uri.split(",")[1]);
var mime_str = data_uri.split(",")[0].split(":")[1].split(";")[0];
var ab = new ArrayBuffer(byte_str.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < byte_str.length; i++) {
ia[i] = byte_str.charCodeAt(i);
}
return new Blob([ab], { type: mime_str });
}
function blob_to_string(blb) {
const reader = new FileReader();
reader.addEventListener("loadend", (e) => {
_value = e.srcElement.result;
});
reader.readAsText(blb);
}
$: {
if (value && value.data && typeof value.data === "string") {
if (!value) _value = null;
else blob_to_string(data_uri_to_blob(value.data));
}
}
function make_dict(x, y) {
const headers = [];
const data = [];
headers.push(x.name);
y.forEach(({ name }) => headers.push(name));
for (let i = 0; i < x.values.length; i++) {
let _data = [];
_data.push(x.values[i]);
y.forEach(({ values }) => _data.push(values[i].y));
data.push(_data);
}
return { headers, data };
}
</script>
{#if _value}
<Chart
value={_value}
{y}
{x}
on:process={({ detail: { x, y } }) => setValue(make_dict(x, y))}
/>
{/if}
{#if !value}
<Upload
filetype="text/csv"
load={(v) => setValue({ data: v })}
include_file_metadata={false}
{theme}
>
Drop CSV Here
<br />- or -<br />
Click to Upload
</Upload>
{/if}

View File

@ -0,0 +1,5 @@
import Component from "./Component.svelte";
export default {
"component": Component,
}

View File

@ -5,8 +5,6 @@
value,
theme,
setValue = () => {};
$: console.log(headers, value);
</script>
<DataFrame

View File

@ -21,7 +21,6 @@
}
}
}
console.log(color_map);
</script>
<div class="output-highlightedtext" {theme}>

View File

@ -0,0 +1,11 @@
<script>
import Upload from "../../utils/Upload.svelte";
import Chart from "../../utils/Chart.svelte";
export let value;
$: formatted_value = value.data.map((r) =>
r.reduce((acc, next, i) => ({ ...acc, [value.headers[i]]: next }), {})
);
</script>
<Chart value={formatted_value} type="data" />

View File

@ -0,0 +1,5 @@
import Component from "./Component.svelte";
export default {
"component": Component,
}

View File

@ -0,0 +1,164 @@
<script>
import { csvParse } from "d3-dsv";
import { scaleLinear } from "d3-scale";
import { line as _line, curveLinear } from "d3-shape";
import { createEventDispatcher, onMount } from "svelte";
import { get_domains, transform_values, get_color } from "./utils.js";
import { tooltip } from "./tooltip.js";
export let value;
export let theme;
export let x;
export let y;
export let type = "csv";
const dispatch = createEventDispatcher();
$: ({ x, y } =
type === "csv"
? transform_values(csvParse(value), x, y)
: transform_values(value, x, y));
$: x_domain = get_domains(x);
$: y_domain = get_domains(y);
$: scale_x = scaleLinear(x_domain, [0, 600]).nice();
$: scale_y = scaleLinear(y_domain, [350, 0]).nice();
$: x_ticks = scale_x.ticks(8);
$: y_ticks = scale_y.ticks(y_domain[1] < 10 ? y_domain[1] : 8);
$: colors = y.reduce(
(acc, next) => ({ ...acc, [next.name]: get_color() }),
{}
);
onMount(() => {
dispatch("process", { x, y });
});
</script>
<div>
<div class="flex justify-center align-items-center text-sm">
{#each y as { name }}
<div class="mx-2">
<span
class="inline-block w-[10px] h-[10px]"
style="background-color: {colors[name]}"
/>
{name}
</div>
{/each}
</div>
<svg class="w-full" viewbox="-70 -20 700 420">
<g>
{#each x_ticks as tick}
<line
stroke-width="0.5"
x1={scale_x(tick)}
x2={scale_x(tick)}
y1={scale_y(y_ticks[0] < y_domain[0] ? y_ticks[0] : y_domain[0]) + 10}
y2={scale_y(
y_domain[1] > y_ticks[y_ticks.length - 1]
? y_domain[1]
: y_ticks[y_ticks.length - 1]
)}
stroke="#aaa"
/>
<text
class="font-mono text-xs"
text-anchor="middle"
x={scale_x(tick)}
y={scale_y(y_ticks[0]) + 30}
>
{tick}
</text>
{/each}
{#each y_ticks as tick}
<line
stroke-width="0.5"
y1={scale_y(tick)}
y2={scale_y(tick)}
x1={scale_x(x_ticks[0] < x_domain[0] ? x_ticks[0] : x_domain[0]) - 10}
x2={scale_x(
x_domain[1] > x_ticks[x_ticks.length - 1]
? x_domain[1]
: x_ticks[x_ticks.length - 1]
)}
stroke="#aaa"
/>
<text
class="font-mono text-xs"
text-anchor="end"
y={scale_y(tick) + 4}
x={scale_x(x_ticks[0]) - 20}
>
{tick}
</text>
{/each}
{#if y_domain[1] > y_ticks[y_ticks.length - 1]}
<line
stroke-width="0.5"
y1={scale_y(y_domain[1])}
y2={scale_y(y_domain[1])}
x1={scale_x(x_ticks[0])}
x2={scale_x(x_domain[1])}
stroke="#aaa"
/>
<text
class="font-mono text-xs"
text-anchor="end"
y={scale_y(y_domain[1]) + 4}
x={scale_x(x_ticks[0]) - 20}
>
{y_domain[1]}
</text>
{/if}
</g>
{#each y as { name, values }}
{@const color = colors[name]}
{#each values as { x, y }}
<circle
r="3.5"
cx={scale_x(x)}
cy={scale_y(y)}
stroke-width="1.5"
stroke={color}
fill="none"
/>
{/each}
<path
d={_line().curve(curveLinear)(
values.map(({ x, y }) => [scale_x(x), scale_y(y)])
)}
fill="none"
stroke={color}
stroke-width="3"
line-cap="round"
/>
{/each}
{#each y as { name, values }}
{@const color = colors[name]}
{#each values as { x, y }}
<circle
use:tooltip={{ color, text: `(${x}, ${y})` }}
r="7"
cx={scale_x(x)}
cy={scale_y(y)}
stroke="black"
fill="black"
style="cursor: pointer; opacity: 0"
/>
{/each}
{/each}
</svg>
<div class="flex justify-center align-items-center text-sm">
{x.name}
</div>
</div>

View File

@ -0,0 +1,19 @@
<script>
export let text;
export let x;
export let y;
export let color;
let w, h;
</script>
<div
class="bg-black bg-opacity-80 text-white border-r-2 p-1 absolute text-xs flex items-center justify-center"
bind:offsetWidth={w}
bind:offsetHeight={h}
style="
top: {y - h / 2}px;
left: {x - w - 7}px;"
>
<span class="inline-block w-3 h-3 mr-1" style="background: {color}" />{text}
</div>

View File

@ -0,0 +1,38 @@
import Tooltip from "./Tooltip.svelte";
export function tooltip(element, { color, text }) {
let div;
let tooltipComponent;
function mouseOver(event) {
tooltipComponent = new Tooltip({
props: {
text,
x: event.pageX,
y: event.pageY,
color,
},
target: document.body,
});
}
function mouseMove(event) {
tooltipComponent.$set({
x: event.pageX,
y: event.pageY,
});
}
function mouseLeave() {
tooltipComponent.$destroy();
}
element.addEventListener("mouseover", mouseOver);
element.addEventListener("mouseleave", mouseLeave);
element.addEventListener("mousemove", mouseMove);
return {
destroy() {
element.removeEventListener("mouseover", mouseOver);
element.removeEventListener("mouseleave", mouseLeave);
element.removeEventListener("mousemove", mouseMove);
},
};
}

View File

@ -0,0 +1,54 @@
export function get_domains(values) {
let _vs;
if (Array.isArray(values)) {
_vs = values.reduce((acc, { values }) => {
return [...acc, ...values.map(({ x, y }) => y)];
}, []);
} else {
_vs = values.values;
}
return [Math.min(..._vs), Math.max(..._vs)];
}
export function transform_values(values, x, y) {
const transformed_values = Object.entries(values[0]).reduce((acc, next, i) => {
if ((!x && i === 0) || (x && next[0] === x)) {
acc.x.name = next[0];
} else if (!y || y && y.includes(next[0]) ) {
acc.y.push({name: next[0], values: []});
}
return acc;
}, {x: {name: '', values: []}, y: []});
for (let i = 0; i < values.length; i++) {
const _a = Object.entries(values[i]);
for (let j = 0; j < _a.length; j++) {
let [name, x] = _a[j];
if (name === transformed_values.x.name) {
transformed_values.x.values.push(x);
} else {
transformed_values.y[j - 1].values.push({ y: _a[j][1], x: _a[0][1] });
}
}
}
return transformed_values;
}
let c = 0;
export function get_color() {
let default_colors = [
[255, 99, 132],
[54, 162, 235],
[240, 176, 26],
[153, 102, 255],
[75, 192, 192],
[255, 159, 64],
];
if (c >= default_colors.length) c = 0;
const [r, g, b] = default_colors[c++];
return `rgb(${r},${g},${b})`;
}