homeos/resources/js/app.js

88 lines
3.5 KiB
JavaScript

import Echo from 'laravel-echo';
import Pusher from 'pusher-js';
import { Chart, registerables } from 'chart.js';
import Sortable from 'sortablejs';
Chart.register(...registerables);
// Drag-to-reorder tile grid → persists order via $wire.reorder([uuid, …]).
document.addEventListener('alpine:init', () => {
window.Alpine.data('sortableGrid', () => ({
init() {
Sortable.create(this.$refs.grid, {
handle: '.drag-handle',
animation: 150,
ghostClass: 'opacity-40',
onEnd: () => {
const order = [...this.$refs.grid.children].map((c) => c.dataset.uuid).filter(Boolean);
this.$wire.reorder(order);
},
});
},
}));
});
// Themed line-chart island (Livewire bundles Alpine). Usage:
// <div x-data="lineChart(@js($config))"><canvas x-ref="canvas"></canvas></div>
// where $config = { labels: [...], series: [{ key, label, color, data, axis }], units: {...} }
document.addEventListener('alpine:init', () => {
window.Alpine.data('lineChart', (config) => ({
chart: null,
init() {
const css = getComputedStyle(document.documentElement);
const token = (name, fallback) => (css.getPropertyValue(name).trim() || fallback);
const grid = token('--color-line', '#1C2846');
const ink = token('--color-ink-3', '#5E6C8A');
this.chart = new Chart(this.$refs.canvas, {
type: 'line',
data: {
labels: config.labels,
datasets: config.series.map((s) => ({
label: s.label,
data: s.data,
borderColor: token(s.color, '#4FC1FF'),
backgroundColor: 'transparent',
borderWidth: 2,
tension: 0.35,
pointRadius: 0,
yAxisID: s.axis || 'y',
})),
},
options: {
responsive: true,
maintainAspectRatio: false,
interaction: { mode: 'index', intersect: false },
plugins: { legend: { labels: { color: ink, boxWidth: 10, boxHeight: 10, usePointStyle: true } } },
scales: {
x: { ticks: { color: ink, maxTicksLimit: 6 }, grid: { color: grid } },
y: { position: 'left', ticks: { color: ink }, grid: { color: grid }, beginAtZero: true },
y1: { position: 'right', ticks: { color: ink }, grid: { drawOnChartArea: false }, beginAtZero: true, max: 100 },
},
},
});
},
destroy() {
this.chart?.destroy();
},
}));
});
// Reverb (Pusher protocol), proxied same-origin through nginx (/app, /apps).
// The browser connects to the page's own host/port — works on localhost and over the LAN
// with no separate WS port and no host mismatch. Every channel is PRIVATE (/broadcasting/auth).
const loc = window.location;
const isHttps = loc.protocol === 'https:';
const port = loc.port ? Number(loc.port) : (isHttps ? 443 : 80);
window.Pusher = Pusher;
window.Echo = new Echo({
broadcaster: 'reverb',
key: import.meta.env.VITE_REVERB_APP_KEY,
wsHost: loc.hostname,
wsPort: port,
wssPort: port,
forceTLS: isHttps,
enabledTransports: ['ws', 'wss'],
});