Buscar K
Aparência
Aparência
Um Widget Svelte importa de uma lista fechada de pacotes, entre eles Highcharts, Chart.js, ApexCharts, d3, date-fns, lodash e xlsx. O servidor de compilação do Lumo resolve todo import entre os pacotes que tem instalados e embute no Widget as bibliotecas que ele importa; nada vem como variável global na página. Por isso o import é obrigatório e só funciona com os pacotes desta lista.
| Para | Pacote | Versão | Import |
|---|---|---|---|
| Componente | svelte | 4.2.19 | import { onMount, onDestroy, tick } from 'svelte' |
| Gráfico (recomendado) | highcharts, @highcharts/svelte | 11.4.8, 1.1.1 | import Highcharts from 'highcharts' e import { Chart } from '@highcharts/svelte' |
| Gráfico | chart.js | 4.4.5 | import Chart from 'chart.js/auto' |
| Gráfico | apexcharts | 3.54.1 | import ApexCharts from 'apexcharts' |
| Gráfico leve, série longa | uplot | 1.6.31 | import uPlot from 'uplot' e import 'uplot/dist/uPlot.min.css' |
| Visualização livre | d3 | 7.9.0 | import * as d3 from 'd3' |
| Data | date-fns | 4.1.0 | import { format } from 'date-fns' e import { ptBR } from 'date-fns/locale' |
| Data | dayjs | 1.11.13 | import dayjs from 'dayjs' |
| Data | moment | 2.30.1 | import moment from 'moment' e import 'moment/locale/pt-br' |
| Dados | lodash | 4.17.21 | import _ from 'lodash' |
| Estatística | simple-statistics | 7.8.7 | import * as ss from 'simple-statistics' |
| Planilha | xlsx | 0.18.5 | import * as XLSX from 'xlsx' |
| Cor | chroma-js | 3.1.2 | import chroma from 'chroma-js' |
| Ícone | @iconify/svelte | 4.2.0 | import Icon from '@iconify/svelte' |
| Medida de texto | @chenglou/pretext | 0.0.6 | import { prepareWithSegments, measureNaturalWidth } from '@chenglou/pretext' |
Import de um pacote que não está na lista faz a compilação falhar, e o erro do compilador aparece no lugar do Widget. echarts, plotly.js, vega e numeral não estão instalados.
O caminho que o assistente de IA do editor segue, e o que dá menos trabalho: o componente <Chart> cria, atualiza e destrói o gráfico junto com o Widget.
<script>
import Highcharts from 'highcharts';
import { Chart } from '@highcharts/svelte';
export let app;
let options = null;
app.fetchMatrix({ columns: ['[Vendas]."MES":YEARMONTH', '[Vendas]."VALOR":SUM'] })
.then((res) => {
options = {
chart: { type: 'column', backgroundColor: 'transparent' },
title: { text: null },
xAxis: { categories: res.Result.map((r) => app.formatValue(r[0], res.Columns[0])) },
series: [{ name: res.Columns[1].label, data: res.Result.map((r) => Number(r[1]) || 0) }],
credits: { enabled: false },
};
});
</script>
{#if options}<Chart {Highcharts} {options} />{/if}Módulos extras do Highcharts vêm do próprio pacote e se ligam passando o Highcharts:
import Highcharts from 'highcharts';
import HighchartsMore from 'highcharts/highcharts-more'; // gauge, arearange, bubble
import Exporting from 'highcharts/modules/exporting';
HighchartsMore(Highcharts);
Exporting(Highcharts);Estas três desenham num elemento que você entrega. Crie no onMount e destrua no retorno dele, senão cada recarga do Dashboard deixa um gráfico órfão.
<script>
import { onMount } from 'svelte';
import Chart from 'chart.js/auto';
let canvas;
onMount(() => {
const grafico = new Chart(canvas, { type: 'line', data: { labels: ['Jan', 'Fev'], datasets: [{ data: [10, 14] }] } });
return () => grafico.destroy();
});
</script>
<canvas bind:this={canvas}></canvas>import * as d3 from 'd3';
const escala = d3.scaleLinear().domain([0, d3.max(valores)]).range([0, largura]);import { format, startOfMonth } from 'date-fns';
import { ptBR } from 'date-fns/locale';
format(startOfMonth(new Date()), "MMMM 'de' yyyy", { locale: ptBR }); // "novembro de 2026"Para formatar um valor que veio da consulta, use app.formatValue, que já respeita a máscara da coluna.
import _ from 'lodash';
import * as ss from 'simple-statistics';
const porLoja = _.groupBy(linhas, (l) => l.loja);
const mediana = ss.median(linhas.map((l) => l.valor));import * as XLSX from 'xlsx';
function baixar() {
const planilha = XLSX.utils.aoa_to_sheet([res.Columns.map((c) => c.label), ...res.Result]);
const livro = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(livro, planilha, 'Dados');
XLSX.writeFile(livro, 'vendas.xlsx');
}import chroma from 'chroma-js';
const escala = chroma.scale(['#fee2e2', '#dc2626']).domain([min, max]);
const cor = escala(valor).hex();<script>import Icon from '@iconify/svelte';</script>
<Icon icon="mdi:alert-circle" width="20" class="text-amber-500 dark:text-amber-400" />O nome é conjunto:ícone (mdi, lucide, tabler, ph, entre outros), e a busca fica em icon-sets.iconify.design. A cor segue currentColor, então a classe de texto do Tailwind pinta o ícone. O desenho de cada ícone é baixado da API pública do Iconify quando o Widget aparece; numa rede que bloqueia api.iconify.design, o ícone fica em branco.
Mede texto sem esperar o navegador desenhar, para ajustar o tamanho da fonte ao espaço do Widget. É o que o KPI do Lumo usa para caber o número no cartão.
import { prepareWithSegments, measureNaturalWidth } from '@chenglou/pretext';
const medido = prepareWithSegments('R$ 1.234.567', '700 100px Poppins');
const largura = measureNaturalWidth(medido); // largura em px na fonte de 100px
const tamanho = Math.min(48, (larguraDisponivel / largura) * 100);As classes do Tailwind 3.4 funcionam direto, inclusive dark: (o modo escuro segue a classe dark da página). O compilador gera só as classes que aparecem escritas no código, então a classe precisa estar inteira no texto.
<!-- certo: as duas classes aparecem inteiras -->
<div class={alerta ? 'bg-red-100 text-red-700' : 'bg-slate-100 text-slate-700'}>
<!-- errado: grid-cols-3 nunca aparece escrita e não é gerada -->
<div class="grid grid-cols-{n}">Além das cores padrão, o tema tem as do Lumo: navy-50 a navy-900 (a paleta do modo escuro), primary, accent, secondary, info, success, warning, error, slate-150; tamanhos de fonte text-tiny, text-tiny+, text-xs+, text-sm+; e as sombras shadow-soft e shadow-soft-dark. Quais classes usar para fundo, borda e texto está em Boas práticas.
O código pode ter mais de um arquivo. O principal se chama Main.svelte, e os outros entram por import relativo:
import Linha from './Linha.svelte';
import { somar } from './calculos.js';Sem Main.svelte, a compilação falha com "Main.svelte must be included".
onclick) não funciona; veja Boas práticas.