From 902c660a10e654ebc96e40cfab7d09d36b7a77ac Mon Sep 17 00:00:00 2001
From: Paul Oliver <contact@pauloliver.dev>
Date: Sat, 21 Mar 2026 01:36:24 +0100
Subject: Adds data server

---
 data/vue/Plot.vue | 133 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 133 insertions(+)
 create mode 100644 data/vue/Plot.vue

(limited to 'data/vue/Plot.vue')

diff --git a/data/vue/Plot.vue b/data/vue/Plot.vue
new file mode 100644
index 0000000..3c08d73
--- /dev/null
+++ b/data/vue/Plot.vue
@@ -0,0 +1,133 @@
+<template>
+  <div class="plot_container" :class="{ plot_maximized: maximized, plot_minimized: !maximized }" ref="plot_container">
+    <div class="plot" ref="plot_ref">
+      <button class="plot_button" @click="plot_toggle_maximize">
+        {{ maximized ? '-' : '+' }}
+      </button>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { defineProps, inject, onMounted, ref, useTemplateRef, watch } from 'vue'
+
+const props = defineProps({ section: String, name: String })
+
+const maximized = ref(false)
+
+const plot_ref = useTemplateRef('plot_ref')
+const plot_container = useTemplateRef('plot_container')
+
+const plots = inject('plots')
+const entries = inject('entries')
+const x_axis = inject('x_axis')
+const data = inject('data')
+
+const plot_toggle_maximize = () => {
+  maximized.value = !maximized.value
+  Plotly.Plots.resize(plot_ref.value)
+  document.body.style.overflow = maximized.value ? 'hidden' : 'visible'
+}
+
+const prevent_plotly_buttons_tab_focus = () => {
+  const focusableElements = plot_container.value.querySelectorAll('a, button, input, select')
+  focusableElements.forEach(elem => elem.setAttribute('tabindex', '-1'))
+}
+
+onMounted(() => {
+  const plot_config = plots.value[props.section][props.name]
+
+  switch (plot_config.type) {
+  case 'lines':
+    var data_defs = { mode: 'lines', line: { width: 1 }}
+    break
+  case 'stack':
+    var data_defs = { mode: 'lines', line: { width: 1 }, stackgroup: 'stackgroup' }
+    break
+  case 'stack_percent':
+    var data_defs = { mode: 'lines', line: { width: 1 }, stackgroup: 'stackgroup', groupnorm: 'percent' }
+    break
+  }
+
+  const columns = plot_config.cols
+  const data = Array.from(columns, column => ({ ...data_defs, x: [], y: [], name: column }))
+
+  Plotly.newPlot(plot_ref.value, data, {
+    legend: { font: { color: '#586e75', family: 'monospace' }, maxheight: 100, orientation: 'h' },
+    margin: { b: 32, l: 32, r: 32, t: 32 },
+    paper_bgcolor: '#002b36',
+    plot_bgcolor: '#002b36',
+    title: { font: { color: '#586e75' }, text: props.name, x: 0, xref: 'paper' },
+    xaxis: { gridcolor: '#073642', tickfont: { color: '#586e75' }, zerolinecolor: '#586e75' },
+    yaxis: { gridcolor: '#073642', tickfont: { color: '#586e75' }, zerolinecolor: '#586e75' },
+  }, {
+    displayModeBar: true,
+    responsive: true,
+  })
+
+  prevent_plotly_buttons_tab_focus()
+})
+
+watch(data, new_data => {
+  const plot_config = plots.value[props.section][props.name]
+  const columns = plot_config.cols
+  const column_count = columns.length
+  const table_data = new_data.values[plot_config.table]
+  const traces = [...Array(column_count).keys()]
+  const xs = Array(column_count).fill(table_data.map(elem => elem[x_axis.value]))
+  const ys = columns.map(column => table_data.map(elem => elem[column]))
+
+  // Clear traces
+  if (new_data.redraw) {
+    const restyle = {
+      x: Array.from(columns, () => []),
+      y: Array.from(columns, () => []),
+    }
+
+    Plotly.restyle(plot_ref.value, restyle)
+  }
+
+  Plotly.extendTraces(plot_ref.value, { x: xs, y: ys }, traces, entries.value)
+})
+</script>
+
+<style>
+.plot_container {
+  background-color: #002b36;
+  display: inline-block;
+  width: 100%;
+}
+
+.plot_maximized {
+  height: 100%;
+  left: 0;
+  position: fixed;
+  top: 0;
+  z-index: 999;
+}
+
+.plot_minimized {
+  height: 400px;
+  position: relative;
+  z-index: 0;
+}
+
+.plot_button {
+  background-color: #002b36;
+  border: 1.5px solid #586e75;
+  color: #586e75;
+  cursor: pointer;
+  font-family: monospace;
+  font-size: 18px;
+  height: 26px;
+  padding: 0;
+  position: absolute;
+  right: 0;
+  top: 0;
+  width: 26px;
+}
+
+.plot {
+  height: 100%;
+}
+</style>
-- 
cgit v1.3

