@pulzr/perses-figure-panel 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +21 -0
  2. package/__mf/js/{PulzrFigurePanel.577b888ef4.js → PulzrFigurePanel.b29c14b507.js} +2 -2
  3. package/__mf/js/async/{0.325dac5c70.js → 0.ee9b9a050d.js} +2 -2
  4. package/__mf/js/async/{0p.68da90f323.js → 0p.810fbfea5e.js} +2 -2
  5. package/__mf/js/async/{2.18619a1037.js → 2.e0e77c51ac.js} +2 -2
  6. package/__mf/js/async/{6.02956de769.js → 6.1033156971.js} +1 -1
  7. package/__mf/js/async/{9.952436a6ee.js → 9.f20ad249a8.js} +1 -1
  8. package/__mf/js/async/__federation_expose_PulzrFigurePanel.9472172973.js +1 -0
  9. package/__mf/js/async/{f.daba8eec81.js → f.9b147c6255.js} +2 -2
  10. package/__mf/js/async/k.39678d65e5.js +1 -0
  11. package/__mf/js/async/kk.62383e2b13.js +1 -0
  12. package/__mf/js/async/kx.899711f01e.js +83 -0
  13. package/__mf/js/async/kx.899711f01e.js.LICENSE.txt +55 -0
  14. package/__mf/js/async/mt.918f7f9b24.js +92 -0
  15. package/__mf/js/async/{b.5f50cdb328.js.LICENSE.txt → mt.918f7f9b24.js.LICENSE.txt} +0 -25
  16. package/__mf/js/async/{o.0d1a3636bd.js → o.ec154e6ff6.js} +1 -1
  17. package/__mf/js/async/{os.7ddd6e9621.js → os.e5e40daae1.js} +2 -2
  18. package/__mf/js/async/{p.3fb8172ba3.js → p.4a73008e86.js} +2 -2
  19. package/__mf/js/async/{s.f71e79b6e1.js → s.658d6a4164.js} +1 -1
  20. package/__mf/js/{main.d9b3dd4bc6.js → main.386bdfecce.js} +2 -2
  21. package/lib/panels/pulzr-figure-panel/PulzrFigurePanelComponent.d.ts +4 -1
  22. package/lib/panels/pulzr-figure-panel/PulzrFigurePanelComponent.d.ts.map +1 -1
  23. package/lib/panels/pulzr-figure-panel/PulzrFigurePanelComponent.js +499 -101
  24. package/lib/panels/pulzr-figure-panel/PulzrFigurePanelComponent.js.map +1 -1
  25. package/lib/panels/pulzr-figure-panel/PulzrFigurePanelSettingsEditor.d.ts.map +1 -1
  26. package/lib/panels/pulzr-figure-panel/PulzrFigurePanelSettingsEditor.js +128 -51
  27. package/lib/panels/pulzr-figure-panel/PulzrFigurePanelSettingsEditor.js.map +1 -1
  28. package/lib/panels/pulzr-figure-panel/figure.d.ts +25 -6
  29. package/lib/panels/pulzr-figure-panel/figure.d.ts.map +1 -1
  30. package/lib/panels/pulzr-figure-panel/figure.js +43 -11
  31. package/lib/panels/pulzr-figure-panel/figure.js.map +1 -1
  32. package/lib/panels/pulzr-figure-panel/pulzr-figure-panel-types.d.ts +22 -2
  33. package/lib/panels/pulzr-figure-panel/pulzr-figure-panel-types.d.ts.map +1 -1
  34. package/lib/panels/pulzr-figure-panel/pulzr-figure-panel-types.js.map +1 -1
  35. package/mf-manifest.json +19 -19
  36. package/mf-stats.json +19 -19
  37. package/package.json +1 -1
  38. package/__mf/js/async/5.9d2ef52231.js +0 -83
  39. package/__mf/js/async/5.9d2ef52231.js.LICENSE.txt +0 -30
  40. package/__mf/js/async/__federation_expose_PulzrFigurePanel.e4e9ee3624.js +0 -1
  41. package/__mf/js/async/b.5f50cdb328.js +0 -92
  42. package/__mf/js/async/k.7c4213442a.js +0 -1
  43. package/__mf/js/async/kk.c246b3edb4.js +0 -1
  44. /package/__mf/css/async/{b.7481ca0159.css → mt.7481ca0159.css} +0 -0
  45. /package/__mf/js/async/{0.325dac5c70.js.LICENSE.txt → 0.ee9b9a050d.js.LICENSE.txt} +0 -0
  46. /package/__mf/js/async/{0p.68da90f323.js.LICENSE.txt → 0p.810fbfea5e.js.LICENSE.txt} +0 -0
  47. /package/__mf/js/async/{2.18619a1037.js.LICENSE.txt → 2.e0e77c51ac.js.LICENSE.txt} +0 -0
  48. /package/__mf/js/async/{f.daba8eec81.js.LICENSE.txt → f.9b147c6255.js.LICENSE.txt} +0 -0
  49. /package/__mf/js/async/{os.7ddd6e9621.js.LICENSE.txt → os.e5e40daae1.js.LICENSE.txt} +0 -0
  50. /package/__mf/js/async/{p.3fb8172ba3.js.LICENSE.txt → p.4a73008e86.js.LICENSE.txt} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/panels/pulzr-figure-panel/PulzrFigurePanelComponent.tsx"],"sourcesContent":["import { Box, Typography } from '@mui/material';\nimport { formatValue } from '@perses-dev/components';\nimport type { ReactElement } from 'react';\n\nimport { changeOf, changeText, figureOf, fill, sizeChange, timeInParts, valuesOf } from './figure';\nimport type { PulzrFigurePanelProps } from './pulzr-figure-panel-types';\n\nconst numbers = { fontFeatureSettings: \"'tnum' 1\", fontVariantNumeric: 'tabular-nums' } as const;\n\n/** The figure as text: a time in parts when asked, else by its format. */\nfunction figure(now: number | undefined, spec: PulzrFigurePanelProps['spec']): string {\n if (now === undefined) return '—';\n return (spec.timeInParts && timeInParts(now, spec.format?.unit)) || formatValue(now, spec.format);\n}\n\nconst TONE = { good: 'success.main', bad: 'error.main', flat: 'text.disabled' } as const;\n\n/**\n * The line under the figure: its change in the style asked, good news in the success color, bad in the error\n * color, flat quiet; or the caption, secondary, with nothing to compare with.\n */\nfunction changeLine(\n now: number | undefined,\n before: number | undefined,\n spec: PulzrFigurePanelProps['spec'],\n): { line?: string; lineColor: string } {\n const label = spec.comparedLabel ?? 'vs the period before';\n if (spec.changeStyle === 'size') {\n const change = sizeChange(now, before, spec.lowerIsBetter);\n return change\n ? { line: `${change.text} ${label}`, lineColor: TONE[change.tone] }\n : { line: spec.caption, lineColor: 'text.secondary' };\n }\n const change = changeOf(now, before);\n if (!change) return { line: spec.caption, lineColor: 'text.secondary' };\n if (change.direction === 'flat') return { line: changeText(change, label), lineColor: TONE.flat };\n const good = change.direction === (spec.lowerIsBetter ? 'down' : 'up');\n return { line: changeText(change, label), lineColor: good ? TONE.good : TONE.bad };\n}\n\n/** One figure, large and light, and under it its change against the period before (or a caption). */\nexport function PulzrFigurePanelComponent({ queryResults, spec }: PulzrFigurePanelProps): ReactElement {\n const { now, before, total } = figureOf(queryResults, spec.of?.series);\n const values = spec.subtitle || spec.detail ? valuesOf(queryResults) : undefined;\n const { line, lineColor } = changeLine(now, before, spec);\n return (\n <Box sx={{ px: '10px', pt: '2px', pb: '7px', minWidth: 0, overflow: 'hidden' }}>\n {spec.subtitle && values && (\n <Typography\n component=\"div\"\n sx={{\n fontSize: 9,\n letterSpacing: '0.04em',\n textTransform: 'uppercase',\n color: 'text.secondary',\n mt: '-4px',\n mb: '2px',\n whiteSpace: 'nowrap',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n }}\n >\n {fill(spec.subtitle, values)}\n </Typography>\n )}\n <Typography\n component=\"div\"\n sx={{\n ...numbers,\n fontSize: 22,\n fontWeight: 300,\n lineHeight: 1.05,\n letterSpacing: '-0.02em',\n mb: '4px',\n color: spec.color ?? 'text.primary',\n }}\n >\n {figure(now, spec)}\n {spec.of && total !== undefined && now !== undefined && (\n <Typography\n component=\"span\"\n sx={{\n ...numbers,\n fontSize: 13,\n fontWeight: 400,\n letterSpacing: '0.02em',\n color: 'text.secondary',\n ml: '8px',\n whiteSpace: 'nowrap',\n }}\n >\n {`${Math.round(now * total)}/${total}${spec.of.label ? ` ${spec.of.label}` : ''}`}\n </Typography>\n )}\n </Typography>\n {line && (\n <Typography\n component=\"div\"\n sx={{\n ...numbers,\n fontSize: 10,\n fontWeight: 500,\n lineHeight: 1.4,\n letterSpacing: '0.02em',\n textTransform: 'uppercase',\n whiteSpace: 'nowrap',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n color: lineColor,\n }}\n >\n {line}\n </Typography>\n )}\n {spec.detail && values && (\n <Typography\n component=\"div\"\n sx={{\n ...numbers,\n display: 'inline-block',\n fontSize: 9,\n fontWeight: 500,\n lineHeight: 1.4,\n letterSpacing: '0.04em',\n textTransform: 'uppercase',\n color: 'text.secondary',\n mt: '5px',\n px: '6px',\n py: '2px',\n bgcolor: 'action.hover',\n border: 1,\n borderColor: 'divider',\n }}\n >\n {fill(spec.detail, values)}\n </Typography>\n )}\n </Box>\n );\n}\n"],"names":["Box","Typography","formatValue","changeOf","changeText","figureOf","fill","sizeChange","timeInParts","valuesOf","numbers","fontFeatureSettings","fontVariantNumeric","figure","now","spec","undefined","format","unit","TONE","good","bad","flat","changeLine","before","label","comparedLabel","changeStyle","change","lowerIsBetter","line","text","lineColor","tone","caption","direction"],"mappings":";;AAAA,SAASA,GAAG,EAAEC,UAAU,QAAQ,gBAAgB;AAChD,SAASC,WAAW,QAAQ,yBAAyB;AAGrD,SAASC,QAAQ,EAAEC,UAAU,EAAEC,QAAQ,EAAEC,IAAI,EAAEC,UAAU,EAAEC,WAAW,EAAEC,QAAQ,QAAQ,cAAW;AAGnG,MAAMC,UAAU;IAAEC,qBAAqB;IAAYC,oBAAoB;AAAe;AAEtF,wEAAwE,GACxE,SAASC,OAAOC,GAAG,EAAsBC,IAAI;IAC3C,IAAID,QAAQE,WAAW,OAAO;IAC9B,OAAO,AAACD,KAAKP,WAAW,IAAIA,YAAYM,KAAKC,KAAKE,MAAM,EAAEC,SAAUhB,YAAYY,KAAKC,KAAKE,MAAM;AAClG;AAEA,MAAME,OAAO;IAAEC,MAAM;IAAgBC,KAAK;IAAcC,MAAM;AAAgB;AAE9E;;;CAGC,GACD,SAASC,WACPT,GAAG,EACHU,MAAM,EACNT,IAAI;IAEJ,MAAMU,QAAQV,KAAKW,aAAa,IAAI;IACpC,IAAIX,KAAKY,WAAW,KAAK,QAAQ;QAC/B,MAAMC,SAASrB,WAAWO,KAAKU,QAAQT,KAAKc,aAAa;QACzD,OAAOD,SACH;YAAEE,MAAM,GAAGF,OAAOG,IAAI,CAAC,CAAC,EAAEN,OAAO;YAAEO,WAAWb,IAAKS,CAAAA,OAAOK,IAAI,CAAA,AAAC;QAAC,IAChE;YAAEH,MAAMf,KAAKmB,OAAO;YAAEF,WAAW;QAAiB;IACxD;IACA,MAAMJ,SAASzB,SAASW,KAAKU;IAC7B,IAAI,CAACI,QAAQ,OAAO;QAAEE,MAAMf,KAAKmB,OAAO;QAAEF,WAAW;IAAiB;IACtE,IAAIJ,OAAOO,SAAS,KAAK,QAAQ,OAAO;QAAEL,MAAM1B,WAAWwB,QAAQH;QAAQO,WAAWb,KAAKG,IAAI;IAAC;IAChG,MAAMF,OAAOQ,OAAOO,SAAS,KAAMpB,CAAAA,KAAKc,aAAa,GAAG,SAAS,IAAG;IACpE,OAAO;QAAEC,MAAM1B,WAAWwB,QAAQH;QAAQO,WAAWZ,OAAOD,KAAKC,IAAI,GAAGD,KAAKE,GAAG;IAAC;AACnF;AAEA,mGAAmG,GACnG,OAAO"}
1
+ {"version":3,"sources":["../../../../src/panels/pulzr-figure-panel/PulzrFigurePanelComponent.tsx"],"sourcesContent":["import { Box, Typography, useTheme } from '@mui/material';\nimport type { FormatOptions } from '@perses-dev/components';\nimport { EChart, formatValue, useChartsTheme } from '@perses-dev/components';\nimport { LineChart } from 'echarts/charts';\nimport { GridComponent } from 'echarts/components';\nimport { color as colors, use as registerECharts } from 'echarts/core';\nimport { CanvasRenderer } from 'echarts/renderers';\nimport type { ReactElement } from 'react';\nimport { useMemo } from 'react';\n\nimport {\n BADGE_ARROWS,\n changeOf,\n changeText,\n figureOf,\n fill,\n pairOf,\n sizeChange,\n timeInParts,\n valuesOf,\n valuesOver,\n} from './figure';\nimport type { PulzrFigurePanelProps } from './pulzr-figure-panel-types';\n\nregisterECharts([LineChart, GridComponent, CanvasRenderer]);\n\nconst numbers = { fontFeatureSettings: \"'tnum' 1\", fontVariantNumeric: 'tabular-nums' } as const;\n\n/** A value as text: a time in parts when asked, else by its format. */\nfunction figure(now: number | undefined, format?: FormatOptions, inParts?: boolean): string {\n if (now === undefined) return '—';\n return (inParts && timeInParts(now, format?.unit)) || formatValue(now, format);\n}\n\nconst TONE = { good: 'success.main', bad: 'error.main', flat: 'text.disabled' } as const;\n\n/** The change, small, as a pillar has it beside a figure: `↗ 133%`, good or bad news. Nothing with nothing before. */\nfunction Badge({\n now,\n before,\n lowerIsBetter,\n}: {\n now?: number;\n before?: number;\n lowerIsBetter?: boolean;\n}): ReactElement | null {\n const change = sizeChange(now, before, lowerIsBetter, BADGE_ARROWS);\n if (!change) return null;\n return (\n <Typography\n component=\"span\"\n sx={{ ...numbers, fontSize: 11, fontWeight: 600, whiteSpace: 'nowrap', color: TONE[change.tone] }}\n >\n {change.text}\n </Typography>\n );\n}\n\n/** An area, its line 1.5 wide, fading from the color down: the series' shape over the period. */\nfunction Sparkline({ values, color }: { values: number[]; color: string }): ReactElement {\n const chartsTheme = useChartsTheme();\n const option = useMemo(\n () => ({\n animation: false,\n grid: { top: 2, right: 0, bottom: 0, left: 0 },\n xAxis: { type: 'category', show: false, boundaryGap: false },\n yAxis: { type: 'value', show: false },\n series: [\n {\n type: 'line',\n data: values,\n smooth: true,\n smoothMonotone: 'x',\n symbol: 'none',\n silent: true,\n lineStyle: { color, width: 1.5 },\n // The console's area: a gradient 0.3 to 0, at the 0.6 opacity a Recharts area has.\n areaStyle: {\n opacity: 0.6,\n color: {\n type: 'linear',\n x: 0,\n y: 0,\n x2: 0,\n y2: 1,\n colorStops: [\n { offset: 0, color: colors.modifyAlpha(color, 0.3) },\n { offset: 1, color: colors.modifyAlpha(color, 0) },\n ],\n },\n },\n },\n ],\n }),\n [values, color],\n );\n return (\n <Box sx={{ height: 32, m: '4px -2px 6px' }}>\n {values.length > 1 && (\n <EChart option={option} theme={chartsTheme.echartsTheme} sx={{ width: '100%', height: '100%' }} />\n )}\n </Box>\n );\n}\n\n/**\n * The line under the figure: its change in the style asked, good news in the success color, bad in the error\n * color, flat quiet; or the caption, secondary, with nothing to compare with.\n */\nfunction changeLine(\n now: number | undefined,\n before: number | undefined,\n spec: PulzrFigurePanelProps['spec'],\n): { line?: string; lineColor: string } {\n const label = spec.comparedLabel ?? 'vs the period before';\n if (spec.changeStyle === 'badge') {\n return { line: sizeChange(now, before) ? undefined : spec.caption, lineColor: 'text.secondary' };\n }\n if (spec.changeStyle === 'size') {\n const change = sizeChange(now, before, spec.lowerIsBetter);\n return change\n ? { line: `${change.text} ${label}`, lineColor: TONE[change.tone] }\n : { line: spec.caption, lineColor: 'text.secondary' };\n }\n const change = changeOf(now, before);\n if (!change) return { line: spec.caption, lineColor: 'text.secondary' };\n if (change.direction === 'flat') return { line: changeText(change, label), lineColor: TONE.flat };\n const good = change.direction === (spec.lowerIsBetter ? 'down' : 'up');\n return { line: changeText(change, label), lineColor: good ? TONE.good : TONE.bad };\n}\n\n/**\n * One figure, large and light, and under it its change against the period before (or a caption); as a pillar, bold\n * with its change beside it, a sparkline and a list of figures under it.\n */\nexport function PulzrFigurePanelComponent({ queryResults, spec }: PulzrFigurePanelProps): ReactElement {\n const theme = useTheme();\n const others = [...(spec.sparkline ? [spec.sparkline.series] : []), ...(spec.rows ?? []).map((r) => r.series)];\n const { now, before, total } = figureOf(queryResults, spec.of?.series, others);\n const values = spec.subtitle || spec.detail ? valuesOf(queryResults) : undefined;\n const { line, lineColor } = changeLine(now, before, spec);\n const badge = spec.changeStyle === 'badge';\n return (\n <Box sx={{ px: '10px', pt: '2px', pb: '7px', minWidth: 0, overflow: 'hidden' }}>\n {spec.subtitle && values && (\n <Typography\n component=\"div\"\n sx={{\n fontSize: 9,\n letterSpacing: '0.04em',\n textTransform: 'uppercase',\n color: 'text.secondary',\n mt: '-4px',\n mb: '2px',\n whiteSpace: 'nowrap',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n }}\n >\n {fill(spec.subtitle, values)}\n </Typography>\n )}\n <Typography\n component=\"div\"\n sx={{\n ...numbers,\n fontSize: 22,\n ...(badge\n ? { fontWeight: 650, lineHeight: 1.1, display: 'flex', alignItems: 'baseline', gap: '8px' }\n : { fontWeight: 300, lineHeight: 1.05, letterSpacing: '-0.02em' }),\n mb: '4px',\n color: spec.color ?? 'text.primary',\n }}\n >\n {figure(now, spec.format, spec.timeInParts)}\n {spec.of && total !== undefined && now !== undefined && (\n <Typography\n component=\"span\"\n sx={{\n ...numbers,\n fontSize: 13,\n fontWeight: 400,\n letterSpacing: '0.02em',\n color: 'text.secondary',\n ml: badge ? 0 : '8px',\n whiteSpace: 'nowrap',\n }}\n >\n {`${Math.round(now * total)}/${total}${spec.of.label ? ` ${spec.of.label}` : ''}`}\n </Typography>\n )}\n {badge && <Badge now={now} before={before} lowerIsBetter={spec.lowerIsBetter} />}\n </Typography>\n {line && (\n <Typography\n component=\"div\"\n sx={{\n ...numbers,\n fontSize: 10,\n fontWeight: 500,\n lineHeight: 1.4,\n letterSpacing: '0.02em',\n textTransform: 'uppercase',\n whiteSpace: 'nowrap',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n color: lineColor,\n }}\n >\n {line}\n </Typography>\n )}\n {spec.detail && values && (\n <Typography\n component=\"div\"\n sx={{\n ...numbers,\n display: 'inline-block',\n fontSize: 9,\n fontWeight: 500,\n lineHeight: 1.4,\n letterSpacing: '0.04em',\n textTransform: 'uppercase',\n color: 'text.secondary',\n mt: '5px',\n px: '6px',\n py: '2px',\n bgcolor: 'action.hover',\n border: 1,\n borderColor: 'divider',\n }}\n >\n {fill(spec.detail, values)}\n </Typography>\n )}\n {spec.sparkline && (\n <Sparkline\n values={valuesOver(queryResults, spec.sparkline.series)}\n color={spec.sparkline.color ?? spec.color ?? theme.palette.text.primary}\n />\n )}\n {spec.rows && spec.rows.length > 0 && (\n <Box component=\"dl\" sx={{ display: 'flex', flexDirection: 'column', gap: '1px', m: 0 }}>\n {spec.rows.map((row) => {\n const pair = pairOf(queryResults, row.series);\n return (\n <Box\n key={row.label}\n sx={{\n display: 'flex',\n alignItems: 'baseline',\n justifyContent: 'space-between',\n gap: '10px',\n py: '3px',\n borderTop: 1,\n borderColor: 'divider',\n fontSize: 12,\n }}\n >\n <Box component=\"dt\" sx={{ color: 'text.secondary', minWidth: 0 }}>\n {row.label}\n </Box>\n <Box\n component=\"dd\"\n sx={{\n ...numbers,\n display: 'inline-flex',\n alignItems: 'baseline',\n gap: '6px',\n m: 0,\n color: 'text.primary',\n whiteSpace: 'nowrap',\n }}\n >\n {figure(pair.now, row.format, row.timeInParts)}\n {row.suffix && pair.now !== undefined && ` ${row.suffix}`}\n <Badge now={pair.now} before={pair.before} lowerIsBetter={row.lowerIsBetter} />\n </Box>\n </Box>\n );\n })}\n </Box>\n )}\n </Box>\n );\n}\n"],"names":["Box","Typography","useTheme","EChart","formatValue","useChartsTheme","LineChart","GridComponent","color","colors","use","registerECharts","CanvasRenderer","BADGE_ARROWS","changeOf","changeText","figureOf","fill","pairOf","sizeChange","timeInParts","valuesOf","valuesOver","numbers","fontFeatureSettings","fontVariantNumeric","figure","now","format","inParts","undefined","unit","TONE","good","bad","flat","changeLine","before","spec","label","comparedLabel","changeStyle","line","caption","lineColor","change","lowerIsBetter","text","tone","direction"],"mappings":";;AAAA,SAASA,GAAG,EAAEC,UAAU,EAAEC,QAAQ,QAAQ,gBAAgB;AAE1D,SAASC,MAAM,EAAEC,WAAW,EAAEC,cAAc,QAAQ,yBAAyB;AAC7E,SAASC,SAAS,QAAQ,iBAAiB;AAC3C,SAASC,aAAa,QAAQ,qBAAqB;AACnD,SAASC,SAASC,MAAM,EAAEC,OAAOC,eAAe,QAAQ,eAAe;AACvE,SAASC,cAAc,QAAQ,oBAAoB;AAInD,SACEC,YAAY,EACZC,QAAQ,EACRC,UAAU,EACVC,QAAQ,EACRC,IAAI,EACJC,MAAM,EACNC,UAAU,EACVC,WAAW,EACXC,QAAQ,EACRC,UAAU,QACL,cAAW;AAGlBX,gBAAgB;IAACL;IAAWC;IAAeK;CAAe;AAE1D,MAAMW,UAAU;IAAEC,qBAAqB;IAAYC,oBAAoB;AAAe;AAEtF,qEAAqE,GACrE,SAASC,OAAOC,GAAG,EAAsBC,MAAM,EAAkBC,OAAO;IACtE,IAAIF,QAAQG,WAAW,OAAO;IAC9B,OAAO,AAACD,WAAWT,YAAYO,KAAKC,QAAQG,SAAU3B,YAAYuB,KAAKC;AACzE;AAEA,MAAMI,OAAO;IAAEC,MAAM;IAAgBC,KAAK;IAAcC,MAAM;AAAgB;AAE9E,oHAAoH,GACpH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqBA,+FAA+F,GAC/F;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8CA;;;CAGC,GACD,SAASC,WACPT,GAAG,EACHU,MAAM,EACNC,IAAI;IAEJ,MAAMC,QAAQD,KAAKE,aAAa,IAAI;IACpC,IAAIF,KAAKG,WAAW,KAAK,SAAS;QAChC,OAAO;YAAEC,MAAMvB,WAAWQ,KAAKU,UAAUP,YAAYQ,KAAKK,OAAO;YAAEC,WAAW;QAAiB;IACjG;IACA,IAAIN,KAAKG,WAAW,KAAK,QAAQ;QAC/B,MAAMI,SAAS1B,WAAWQ,KAAKU,QAAQC,KAAKQ,aAAa;QACzD,OAAOD,SACH;YAAEH,MAAM,GAAGG,OAAOE,IAAI,CAAC,CAAC,EAAER,OAAO;YAAEK,WAAWZ,IAAKa,CAAAA,OAAOG,IAAI,CAAA,AAAC;QAAC,IAChE;YAAEN,MAAMJ,KAAKK,OAAO;YAAEC,WAAW;QAAiB;IACxD;IACA,MAAMC,SAAS/B,SAASa,KAAKU;IAC7B,IAAI,CAACQ,QAAQ,OAAO;QAAEH,MAAMJ,KAAKK,OAAO;QAAEC,WAAW;IAAiB;IACtE,IAAIC,OAAOI,SAAS,KAAK,QAAQ,OAAO;QAAEP,MAAM3B,WAAW8B,QAAQN;QAAQK,WAAWZ,KAAKG,IAAI;IAAC;IAChG,MAAMF,OAAOY,OAAOI,SAAS,KAAMX,CAAAA,KAAKQ,aAAa,GAAG,SAAS,IAAG;IACpE,OAAO;QAAEJ,MAAM3B,WAAW8B,QAAQN;QAAQK,WAAWX,OAAOD,KAAKC,IAAI,GAAGD,KAAKE,GAAG;IAAC;AACnF;AAEA;;;CAGC,GACD,OAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"PulzrFigurePanelSettingsEditor.d.ts","sourceRoot":"","sources":["../../../../src/panels/pulzr-figure-panel/PulzrFigurePanelSettingsEditor.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAE1C,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAI1E,wBAAgB,8BAA8B,CAAC,EAC7C,KAAK,EACL,QAAQ,GACT,EAAE,kBAAkB,CAAC,uBAAuB,CAAC,GAAG,YAAY,CAuE5D"}
1
+ {"version":3,"file":"PulzrFigurePanelSettingsEditor.d.ts","sourceRoot":"","sources":["../../../../src/panels/pulzr-figure-panel/PulzrFigurePanelSettingsEditor.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAE1C,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAI1E,wBAAgB,8BAA8B,CAAC,EAC7C,KAAK,EACL,QAAQ,GACT,EAAE,kBAAkB,CAAC,uBAAuB,CAAC,GAAG,YAAY,CAmG5D"}
@@ -4,7 +4,7 @@ import { FormControlLabel, MenuItem, Switch, TextField } from "@mui/material";
4
4
  import { FormatControls, OptionsEditorColumn, OptionsEditorGrid, OptionsEditorGroup } from "@perses-dev/components";
5
5
  const text = (v)=>v.trim() ? v : undefined;
6
6
  export function PulzrFigurePanelSettingsEditor(t0) {
7
- const $ = _c(63);
7
+ const $ = _c(79);
8
8
  const { value, onChange } = t0;
9
9
  let t1;
10
10
  if ($[0] !== value.format) {
@@ -190,7 +190,7 @@ export function PulzrFigurePanelSettingsEditor(t0) {
190
190
  if ($[38] !== onChange || $[39] !== value) {
191
191
  t20 = (e_4)=>onChange({
192
192
  ...value,
193
- changeStyle: e_4.target.value === "size" ? "size" : undefined
193
+ changeStyle: e_4.target.value === "size" || e_4.target.value === "badge" ? e_4.target.value : undefined
194
194
  });
195
195
  $[38] = onChange;
196
196
  $[39] = value;
@@ -200,6 +200,7 @@ export function PulzrFigurePanelSettingsEditor(t0) {
200
200
  }
201
201
  let t21;
202
202
  let t22;
203
+ let t23;
203
204
  if ($[41] === Symbol.for("react.memo_cache_sentinel")) {
204
205
  t21 = /*#__PURE__*/ _jsx(MenuItem, {
205
206
  value: "signed",
@@ -209,15 +210,21 @@ export function PulzrFigurePanelSettingsEditor(t0) {
209
210
  value: "size",
210
211
  children: "↑ 0.7%"
211
212
  });
213
+ t23 = /*#__PURE__*/ _jsx(MenuItem, {
214
+ value: "badge",
215
+ children: "↗ 0.7% beside the figure"
216
+ });
212
217
  $[41] = t21;
213
218
  $[42] = t22;
219
+ $[43] = t23;
214
220
  } else {
215
221
  t21 = $[41];
216
222
  t22 = $[42];
223
+ t23 = $[43];
217
224
  }
218
- let t23;
219
- if ($[43] !== t19 || $[44] !== t20) {
220
- t23 = /*#__PURE__*/ _jsxs(TextField, {
225
+ let t24;
226
+ if ($[44] !== t19 || $[45] !== t20) {
227
+ t24 = /*#__PURE__*/ _jsxs(TextField, {
221
228
  select: true,
222
229
  size: "small",
223
230
  label: "Change reads",
@@ -225,46 +232,112 @@ export function PulzrFigurePanelSettingsEditor(t0) {
225
232
  onChange: t20,
226
233
  children: [
227
234
  t21,
228
- t22
235
+ t22,
236
+ t23
229
237
  ]
230
238
  });
231
- $[43] = t19;
232
- $[44] = t20;
233
- $[45] = t23;
239
+ $[44] = t19;
240
+ $[45] = t20;
241
+ $[46] = t24;
234
242
  } else {
235
- t23 = $[45];
243
+ t24 = $[46];
236
244
  }
237
- const t24 = Boolean(value.lowerIsBetter);
238
- let t25;
239
- if ($[46] !== onChange || $[47] !== value) {
240
- t25 = (e_5)=>onChange({
245
+ const t25 = value.sparkline?.series ?? "";
246
+ let t26;
247
+ if ($[47] !== onChange || $[48] !== value) {
248
+ t26 = (e_5)=>onChange({
241
249
  ...value,
242
- lowerIsBetter: e_5.target.checked || undefined
250
+ sparkline: text(e_5.target.value) ? {
251
+ ...value.sparkline,
252
+ series: e_5.target.value
253
+ } : undefined
243
254
  });
244
- $[46] = onChange;
245
- $[47] = value;
246
- $[48] = t25;
255
+ $[47] = onChange;
256
+ $[48] = value;
257
+ $[49] = t26;
247
258
  } else {
248
- t25 = $[48];
259
+ t26 = $[49];
249
260
  }
250
- let t26;
251
- if ($[49] !== t24 || $[50] !== t25) {
252
- t26 = /*#__PURE__*/ _jsx(FormControlLabel, {
261
+ let t27;
262
+ if ($[50] !== t25 || $[51] !== t26) {
263
+ t27 = /*#__PURE__*/ _jsx(TextField, {
264
+ size: "small",
265
+ label: "Sparkline series",
266
+ helperText: "An area under the figure, of this series over time.",
267
+ value: t25,
268
+ onChange: t26
269
+ });
270
+ $[50] = t25;
271
+ $[51] = t26;
272
+ $[52] = t27;
273
+ } else {
274
+ t27 = $[52];
275
+ }
276
+ const t28 = !value.sparkline;
277
+ const t29 = value.sparkline?.color ?? "";
278
+ let t30;
279
+ if ($[53] !== onChange || $[54] !== value) {
280
+ t30 = (e_6)=>value.sparkline && onChange({
281
+ ...value,
282
+ sparkline: {
283
+ ...value.sparkline,
284
+ color: text(e_6.target.value)
285
+ }
286
+ });
287
+ $[53] = onChange;
288
+ $[54] = value;
289
+ $[55] = t30;
290
+ } else {
291
+ t30 = $[55];
292
+ }
293
+ let t31;
294
+ if ($[56] !== t28 || $[57] !== t29 || $[58] !== t30) {
295
+ t31 = /*#__PURE__*/ _jsx(TextField, {
296
+ size: "small",
297
+ label: "Sparkline color",
298
+ helperText: "Empty: the figure's color.",
299
+ disabled: t28,
300
+ value: t29,
301
+ onChange: t30
302
+ });
303
+ $[56] = t28;
304
+ $[57] = t29;
305
+ $[58] = t30;
306
+ $[59] = t31;
307
+ } else {
308
+ t31 = $[59];
309
+ }
310
+ const t32 = Boolean(value.lowerIsBetter);
311
+ let t33;
312
+ if ($[60] !== onChange || $[61] !== value) {
313
+ t33 = (e_7)=>onChange({
314
+ ...value,
315
+ lowerIsBetter: e_7.target.checked || undefined
316
+ });
317
+ $[60] = onChange;
318
+ $[61] = value;
319
+ $[62] = t33;
320
+ } else {
321
+ t33 = $[62];
322
+ }
323
+ let t34;
324
+ if ($[63] !== t32 || $[64] !== t33) {
325
+ t34 = /*#__PURE__*/ _jsx(FormControlLabel, {
253
326
  control: /*#__PURE__*/ _jsx(Switch, {
254
- checked: t24,
255
- onChange: t25
327
+ checked: t32,
328
+ onChange: t33
256
329
  }),
257
330
  label: "Lower is better"
258
331
  });
259
- $[49] = t24;
260
- $[50] = t25;
261
- $[51] = t26;
332
+ $[63] = t32;
333
+ $[64] = t33;
334
+ $[65] = t34;
262
335
  } else {
263
- t26 = $[51];
336
+ t34 = $[65];
264
337
  }
265
- let t27;
266
- if ($[52] !== t12 || $[53] !== t15 || $[54] !== t18 || $[55] !== t23 || $[56] !== t26 || $[57] !== t6 || $[58] !== t9) {
267
- t27 = /*#__PURE__*/ _jsx(OptionsEditorColumn, {
338
+ let t35;
339
+ if ($[66] !== t12 || $[67] !== t15 || $[68] !== t18 || $[69] !== t24 || $[70] !== t27 || $[71] !== t31 || $[72] !== t34 || $[73] !== t6 || $[74] !== t9) {
340
+ t35 = /*#__PURE__*/ _jsx(OptionsEditorColumn, {
268
341
  children: /*#__PURE__*/ _jsxs(OptionsEditorGroup, {
269
342
  title: "Figure",
270
343
  children: [
@@ -273,37 +346,41 @@ export function PulzrFigurePanelSettingsEditor(t0) {
273
346
  t12,
274
347
  t15,
275
348
  t18,
276
- t23,
277
- t26
349
+ t24,
350
+ t27,
351
+ t31,
352
+ t34
278
353
  ]
279
354
  })
280
355
  });
281
- $[52] = t12;
282
- $[53] = t15;
283
- $[54] = t18;
284
- $[55] = t23;
285
- $[56] = t26;
286
- $[57] = t6;
287
- $[58] = t9;
288
- $[59] = t27;
356
+ $[66] = t12;
357
+ $[67] = t15;
358
+ $[68] = t18;
359
+ $[69] = t24;
360
+ $[70] = t27;
361
+ $[71] = t31;
362
+ $[72] = t34;
363
+ $[73] = t6;
364
+ $[74] = t9;
365
+ $[75] = t35;
289
366
  } else {
290
- t27 = $[59];
367
+ t35 = $[75];
291
368
  }
292
- let t28;
293
- if ($[60] !== t27 || $[61] !== t3) {
294
- t28 = /*#__PURE__*/ _jsxs(OptionsEditorGrid, {
369
+ let t36;
370
+ if ($[76] !== t3 || $[77] !== t35) {
371
+ t36 = /*#__PURE__*/ _jsxs(OptionsEditorGrid, {
295
372
  children: [
296
373
  t3,
297
- t27
374
+ t35
298
375
  ]
299
376
  });
300
- $[60] = t27;
301
- $[61] = t3;
302
- $[62] = t28;
377
+ $[76] = t3;
378
+ $[77] = t35;
379
+ $[78] = t36;
303
380
  } else {
304
- t28 = $[62];
381
+ t36 = $[78];
305
382
  }
306
- return t28;
383
+ return t36;
307
384
  }
308
385
 
309
386
  //# sourceMappingURL=PulzrFigurePanelSettingsEditor.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/panels/pulzr-figure-panel/PulzrFigurePanelSettingsEditor.tsx"],"sourcesContent":["import { FormControlLabel, MenuItem, Switch, TextField } from '@mui/material';\nimport { FormatControls, OptionsEditorColumn, OptionsEditorGrid, OptionsEditorGroup } from '@perses-dev/components';\nimport type { OptionsEditorProps } from '@perses-dev/plugin-system';\nimport type { ReactElement } from 'react';\n\nimport type { PulzrFigurePanelOptions } from './pulzr-figure-panel-types';\n\nconst text = (v: string): string | undefined => (v.trim() ? v : undefined);\n\nexport function PulzrFigurePanelSettingsEditor({\n value,\n onChange,\n}: OptionsEditorProps<PulzrFigurePanelOptions>): ReactElement {\n return (\n <OptionsEditorGrid>\n <OptionsEditorColumn>\n <OptionsEditorGroup title=\"Format\">\n <FormatControls\n value={value.format ?? { unit: 'decimal' }}\n onChange={(format) => onChange({ ...value, format })}\n />\n </OptionsEditorGroup>\n </OptionsEditorColumn>\n <OptionsEditorColumn>\n <OptionsEditorGroup title=\"Figure\">\n <TextField\n size=\"small\"\n label=\"Color\"\n helperText=\"Any CSS color. Empty: the text color.\"\n value={value.color ?? ''}\n onChange={(e) => onChange({ ...value, color: text(e.target.value) })}\n />\n <TextField\n size=\"small\"\n label=\"Compared label\"\n helperText=\"After the change: vs last week.\"\n value={value.comparedLabel ?? ''}\n onChange={(e) => onChange({ ...value, comparedLabel: text(e.target.value) })}\n />\n <TextField\n size=\"small\"\n label=\"Caption\"\n helperText=\"When there is nothing to compare with.\"\n value={value.caption ?? ''}\n onChange={(e) => onChange({ ...value, caption: text(e.target.value) })}\n />\n <TextField\n size=\"small\"\n label=\"Subtitle\"\n helperText=\"Under the title. {{series}}, {{series|%}}.\"\n value={value.subtitle ?? ''}\n onChange={(e) => onChange({ ...value, subtitle: text(e.target.value) })}\n />\n <TextField\n size=\"small\"\n label=\"Detail\"\n helperText=\"A box under the figure, as the subtitle.\"\n value={value.detail ?? ''}\n onChange={(e) => onChange({ ...value, detail: text(e.target.value) })}\n />\n <TextField\n select\n size=\"small\"\n label=\"Change reads\"\n value={value.changeStyle ?? 'signed'}\n onChange={(e) => onChange({ ...value, changeStyle: e.target.value === 'size' ? 'size' : undefined })}\n >\n <MenuItem value=\"signed\">↑ +0.7%</MenuItem>\n <MenuItem value=\"size\">↑ 0.7%</MenuItem>\n </TextField>\n <FormControlLabel\n control={\n <Switch\n checked={Boolean(value.lowerIsBetter)}\n onChange={(e) => onChange({ ...value, lowerIsBetter: e.target.checked || undefined })}\n />\n }\n label=\"Lower is better\"\n />\n </OptionsEditorGroup>\n </OptionsEditorColumn>\n </OptionsEditorGrid>\n );\n}\n"],"names":["FormControlLabel","MenuItem","Switch","TextField","FormatControls","OptionsEditorColumn","OptionsEditorGrid","OptionsEditorGroup","text","v","trim","undefined"],"mappings":";;AAAA,SAASA,gBAAgB,EAAEC,QAAQ,EAAEC,MAAM,EAAEC,SAAS,QAAQ,gBAAgB;AAC9E,SAASC,cAAc,EAAEC,mBAAmB,EAAEC,iBAAiB,EAAEC,kBAAkB,QAAQ,yBAAyB;AAMpH,MAAMC,OAAO,CAACC,IAAmCA,EAAEC,IAAI,KAAKD,IAAIE;AAEhE,OAAO"}
1
+ {"version":3,"sources":["../../../../src/panels/pulzr-figure-panel/PulzrFigurePanelSettingsEditor.tsx"],"sourcesContent":["import { FormControlLabel, MenuItem, Switch, TextField } from '@mui/material';\nimport { FormatControls, OptionsEditorColumn, OptionsEditorGrid, OptionsEditorGroup } from '@perses-dev/components';\nimport type { OptionsEditorProps } from '@perses-dev/plugin-system';\nimport type { ReactElement } from 'react';\n\nimport type { PulzrFigurePanelOptions } from './pulzr-figure-panel-types';\n\nconst text = (v: string): string | undefined => (v.trim() ? v : undefined);\n\nexport function PulzrFigurePanelSettingsEditor({\n value,\n onChange,\n}: OptionsEditorProps<PulzrFigurePanelOptions>): ReactElement {\n return (\n <OptionsEditorGrid>\n <OptionsEditorColumn>\n <OptionsEditorGroup title=\"Format\">\n <FormatControls\n value={value.format ?? { unit: 'decimal' }}\n onChange={(format) => onChange({ ...value, format })}\n />\n </OptionsEditorGroup>\n </OptionsEditorColumn>\n <OptionsEditorColumn>\n <OptionsEditorGroup title=\"Figure\">\n <TextField\n size=\"small\"\n label=\"Color\"\n helperText=\"Any CSS color. Empty: the text color.\"\n value={value.color ?? ''}\n onChange={(e) => onChange({ ...value, color: text(e.target.value) })}\n />\n <TextField\n size=\"small\"\n label=\"Compared label\"\n helperText=\"After the change: vs last week.\"\n value={value.comparedLabel ?? ''}\n onChange={(e) => onChange({ ...value, comparedLabel: text(e.target.value) })}\n />\n <TextField\n size=\"small\"\n label=\"Caption\"\n helperText=\"When there is nothing to compare with.\"\n value={value.caption ?? ''}\n onChange={(e) => onChange({ ...value, caption: text(e.target.value) })}\n />\n <TextField\n size=\"small\"\n label=\"Subtitle\"\n helperText=\"Under the title. {{series}}, {{series|%}}.\"\n value={value.subtitle ?? ''}\n onChange={(e) => onChange({ ...value, subtitle: text(e.target.value) })}\n />\n <TextField\n size=\"small\"\n label=\"Detail\"\n helperText=\"A box under the figure, as the subtitle.\"\n value={value.detail ?? ''}\n onChange={(e) => onChange({ ...value, detail: text(e.target.value) })}\n />\n <TextField\n select\n size=\"small\"\n label=\"Change reads\"\n value={value.changeStyle ?? 'signed'}\n onChange={(e) =>\n onChange({\n ...value,\n changeStyle: e.target.value === 'size' || e.target.value === 'badge' ? e.target.value : undefined,\n })\n }\n >\n <MenuItem value=\"signed\">↑ +0.7%</MenuItem>\n <MenuItem value=\"size\">↑ 0.7%</MenuItem>\n <MenuItem value=\"badge\">↗ 0.7% beside the figure</MenuItem>\n </TextField>\n <TextField\n size=\"small\"\n label=\"Sparkline series\"\n helperText=\"An area under the figure, of this series over time.\"\n value={value.sparkline?.series ?? ''}\n onChange={(e) =>\n onChange({\n ...value,\n sparkline: text(e.target.value) ? { ...value.sparkline, series: e.target.value } : undefined,\n })\n }\n />\n <TextField\n size=\"small\"\n label=\"Sparkline color\"\n helperText=\"Empty: the figure's color.\"\n disabled={!value.sparkline}\n value={value.sparkline?.color ?? ''}\n onChange={(e) =>\n value.sparkline && onChange({ ...value, sparkline: { ...value.sparkline, color: text(e.target.value) } })\n }\n />\n <FormControlLabel\n control={\n <Switch\n checked={Boolean(value.lowerIsBetter)}\n onChange={(e) => onChange({ ...value, lowerIsBetter: e.target.checked || undefined })}\n />\n }\n label=\"Lower is better\"\n />\n </OptionsEditorGroup>\n </OptionsEditorColumn>\n </OptionsEditorGrid>\n );\n}\n"],"names":["FormControlLabel","MenuItem","Switch","TextField","FormatControls","OptionsEditorColumn","OptionsEditorGrid","OptionsEditorGroup","text","v","trim","undefined"],"mappings":";;AAAA,SAASA,gBAAgB,EAAEC,QAAQ,EAAEC,MAAM,EAAEC,SAAS,QAAQ,gBAAgB;AAC9E,SAASC,cAAc,EAAEC,mBAAmB,EAAEC,iBAAiB,EAAEC,kBAAkB,QAAQ,yBAAyB;AAMpH,MAAMC,OAAO,CAACC,IAAmCA,EAAEC,IAAI,KAAKD,IAAIE;AAEhE,OAAO"}
@@ -1,18 +1,32 @@
1
1
  import type { TimeSeries, TimeSeriesData } from '@perses-dev/spec';
2
2
  /** A series' last value that is a number. */
3
3
  export declare function lastValue(series: TimeSeries | undefined): number | undefined;
4
+ /** The series compared with the one named: `<name> · previous period`, `<name> · last year`. */
5
+ export declare function twinOf(series: TimeSeries[], name: string): TimeSeries | undefined;
4
6
  /**
5
- * The figure and the one it is set against. A series labelled `compare` (the
6
- * period before, as the Pulzr and Parkeze datasources label it) is the one
7
- * set against; the first other series is the figure.
7
+ * The figure and the one it is set against. A series labelled `compare` (the period before, as the Pulzr and
8
+ * Parkeze datasources label it) is the one set against; the first other series is the figure, leaving out `of` and
9
+ * the series named in `others` (a sparkline's, the rows'). The period before is the figure's twin by name, or else
10
+ * the first compared series that is not theirs.
8
11
  */
9
12
  export declare function figureOf(results: Array<{
10
13
  data?: TimeSeriesData;
11
- }>, of?: string): {
14
+ }>, of?: string, others?: string[]): {
12
15
  now?: number;
13
16
  before?: number;
14
17
  total?: number;
15
18
  };
19
+ /** The last value of the series named and of its twin, the period before. */
20
+ export declare function pairOf(results: Array<{
21
+ data?: TimeSeriesData;
22
+ }>, name: string): {
23
+ now?: number;
24
+ before?: number;
25
+ };
26
+ /** Every number over time of the series named, for a sparkline. */
27
+ export declare function valuesOver(results: Array<{
28
+ data?: TimeSeriesData;
29
+ }>, name: string): number[];
16
30
  /**
17
31
  * A time in parts, as people say it: 45m, 6h 46m, 7h, and past two days 2d 3h, the minutes being noise there.
18
32
  * Undefined for a unit that is not a time.
@@ -36,8 +50,13 @@ export declare function valuesOf(results: Array<{
36
50
  /** A template's `{{name}}` and `{{name|%}}` filled from the series' values; `—` for one with none. */
37
51
  export declare function fill(template: string, values: Map<string, number>): string;
38
52
  export type Tone = 'good' | 'bad' | 'flat';
39
- /** The change as its size, good or bad news: `↑ 35.8%`, whole past 100%, `0%` within a twentieth of one. */
40
- export declare function sizeChange(now: number | undefined, before: number | undefined, lowerIsBetter?: boolean): {
53
+ /** A pillar's arrows, for the change as a badge: `↗ 133%`. */
54
+ export declare const BADGE_ARROWS: [string, string];
55
+ /**
56
+ * The change as its size, good or bad news: `↑ 35.8%`, whole past 100%, `0%` within a twentieth of one. `arrows`
57
+ * are the rise's and the fall's.
58
+ */
59
+ export declare function sizeChange(now: number | undefined, before: number | undefined, lowerIsBetter?: boolean, arrows?: [string, string]): {
41
60
  text: string;
42
61
  tone: Tone;
43
62
  } | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"figure.d.ts","sourceRoot":"","sources":["../../../../src/panels/pulzr-figure-panel/figure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEnE,6CAA6C;AAC7C,wBAAgB,SAAS,CAAC,MAAM,EAAE,UAAU,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAO5E;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CACtB,OAAO,EAAE,KAAK,CAAC;IAAE,IAAI,CAAC,EAAE,cAAc,CAAA;CAAE,CAAC,EACzC,EAAE,CAAC,EAAE,MAAM,GACV;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAWnD;AAID;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAavF;AAED,MAAM,MAAM,SAAS,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC;AAE/C,4FAA4F;AAC5F,wBAAgB,QAAQ,CACtB,GAAG,EAAE,MAAM,GAAG,SAAS,EACvB,MAAM,EAAE,MAAM,GAAG,SAAS,GACzB;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,SAAS,CAAA;CAAE,GAAG,SAAS,CAOvD;AAID,+BAA+B;AAC/B,wBAAgB,UAAU,CACxB,MAAM,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,SAAS,CAAA;CAAE,EACjD,aAAa,SAAyB,GACrC,MAAM,CAGR;AAED,mFAAmF;AACnF,wBAAgB,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC;IAAE,IAAI,CAAC,EAAE,cAAc,CAAA;CAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAQvF;AAED,sGAAsG;AACtG,wBAAgB,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAM1E;AAED,MAAM,MAAM,IAAI,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;AAE3C,4GAA4G;AAC5G,wBAAgB,UAAU,CACxB,GAAG,EAAE,MAAM,GAAG,SAAS,EACvB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,aAAa,UAAQ,GACpB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,IAAI,CAAA;CAAE,GAAG,SAAS,CAO1C"}
1
+ {"version":3,"file":"figure.d.ts","sourceRoot":"","sources":["../../../../src/panels/pulzr-figure-panel/figure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEnE,6CAA6C;AAC7C,wBAAgB,SAAS,CAAC,MAAM,EAAE,UAAU,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAO5E;AAKD,gGAAgG;AAChG,wBAAgB,MAAM,CAAC,MAAM,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAEjF;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CACtB,OAAO,EAAE,KAAK,CAAC;IAAE,IAAI,CAAC,EAAE,cAAc,CAAA;CAAE,CAAC,EACzC,EAAE,CAAC,EAAE,MAAM,EACX,MAAM,GAAE,MAAM,EAAO,GACpB;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAYnD;AAED,6EAA6E;AAC7E,wBAAgB,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC;IAAE,IAAI,CAAC,EAAE,cAAc,CAAA;CAAE,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAMjH;AAED,mEAAmE;AACnE,wBAAgB,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC;IAAE,IAAI,CAAC,EAAE,cAAc,CAAA;CAAE,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAG5F;AAID;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAavF;AAED,MAAM,MAAM,SAAS,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC;AAE/C,4FAA4F;AAC5F,wBAAgB,QAAQ,CACtB,GAAG,EAAE,MAAM,GAAG,SAAS,EACvB,MAAM,EAAE,MAAM,GAAG,SAAS,GACzB;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,SAAS,CAAA;CAAE,GAAG,SAAS,CAOvD;AAID,+BAA+B;AAC/B,wBAAgB,UAAU,CACxB,MAAM,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,SAAS,CAAA;CAAE,EACjD,aAAa,SAAyB,GACrC,MAAM,CAGR;AAED,mFAAmF;AACnF,wBAAgB,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC;IAAE,IAAI,CAAC,EAAE,cAAc,CAAA;CAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAQvF;AAED,sGAAsG;AACtG,wBAAgB,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAM1E;AAED,MAAM,MAAM,IAAI,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;AAE3C,8DAA8D;AAC9D,eAAO,MAAM,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,CAAc,CAAC;AAEzD;;;GAGG;AACH,wBAAgB,UAAU,CACxB,GAAG,EAAE,MAAM,GAAG,SAAS,EACvB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,aAAa,UAAQ,EACrB,MAAM,GAAE,CAAC,MAAM,EAAE,MAAM,CAAc,GACpC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,IAAI,CAAA;CAAE,GAAG,SAAS,CAO1C"}
@@ -6,20 +6,42 @@
6
6
  }
7
7
  return undefined;
8
8
  }
9
+ const nameOf = (s)=>s.formattedName ?? s.name;
10
+ const compared = (s)=>Boolean(s.labels?.compare);
11
+ /** The series compared with the one named: `<name> · previous period`, `<name> · last year`. */ export function twinOf(series, name) {
12
+ return series.find((s)=>compared(s) && nameOf(s).startsWith(`${name} · `));
13
+ }
9
14
  /**
10
- * The figure and the one it is set against. A series labelled `compare` (the
11
- * period before, as the Pulzr and Parkeze datasources label it) is the one
12
- * set against; the first other series is the figure.
13
- */ export function figureOf(results, of) {
15
+ * The figure and the one it is set against. A series labelled `compare` (the period before, as the Pulzr and
16
+ * Parkeze datasources label it) is the one set against; the first other series is the figure, leaving out `of` and
17
+ * the series named in `others` (a sparkline's, the rows'). The period before is the figure's twin by name, or else
18
+ * the first compared series that is not theirs.
19
+ */ export function figureOf(results, of, others = []) {
14
20
  const series = results.flatMap((r)=>r.data?.series ?? []);
15
- const compared = (s)=>Boolean(s.labels?.compare);
16
- const named = (s)=>of !== undefined && (s.formattedName ?? s.name) === of;
21
+ const taken = of === undefined ? others : [
22
+ of,
23
+ ...others
24
+ ];
25
+ const figure = series.find((s)=>!compared(s) && !taken.includes(nameOf(s)));
17
26
  return {
18
- now: lastValue(series.find((s)=>!compared(s) && !named(s))),
19
- before: lastValue(series.find((s)=>compared(s) && !(of !== undefined && (s.formattedName ?? s.name).startsWith(of)))),
20
- total: of === undefined ? undefined : lastValue(series.find((s)=>!compared(s) && named(s)))
27
+ now: lastValue(figure),
28
+ before: lastValue((figure && twinOf(series, nameOf(figure))) ?? series.find((s)=>compared(s) && !taken.some((name)=>nameOf(s).startsWith(name)))),
29
+ total: of === undefined ? undefined : lastValue(series.find((s)=>!compared(s) && nameOf(s) === of))
21
30
  };
22
31
  }
32
+ /** The last value of the series named and of its twin, the period before. */ export function pairOf(results, name) {
33
+ const series = results.flatMap((r)=>r.data?.series ?? []);
34
+ return {
35
+ now: lastValue(series.find((s)=>!compared(s) && nameOf(s) === name)),
36
+ before: lastValue(twinOf(series, name))
37
+ };
38
+ }
39
+ /** Every number over time of the series named, for a sparkline. */ export function valuesOver(results, name) {
40
+ const series = results.flatMap((r)=>r.data?.series ?? []).find((s)=>!compared(s) && nameOf(s) === name);
41
+ return (series?.values ?? []).flatMap(([, v])=>v !== null && Number.isFinite(v) ? [
42
+ v
43
+ ] : []);
44
+ }
23
45
  const TO_MINUTES = {
24
46
  seconds: 1 / 60,
25
47
  minutes: 1,
@@ -80,7 +102,17 @@ const ARROW = {
80
102
  });
81
103
  });
82
104
  }
83
- /** The change as its size, good or bad news: `↑ 35.8%`, whole past 100%, `0%` within a twentieth of one. */ export function sizeChange(now, before, lowerIsBetter = false) {
105
+ /** A pillar's arrows, for the change as a badge: `↗ 133%`. */ export const BADGE_ARROWS = [
106
+ '↗',
107
+ '↘'
108
+ ];
109
+ /**
110
+ * The change as its size, good or bad news: `↑ 35.8%`, whole past 100%, `0%` within a twentieth of one. `arrows`
111
+ * are the rise's and the fall's.
112
+ */ export function sizeChange(now, before, lowerIsBetter = false, arrows = [
113
+ '↑',
114
+ '↓'
115
+ ]) {
84
116
  if (now === undefined || before === undefined || before === 0) return undefined;
85
117
  const percent = (now - before) / Math.abs(before) * 100;
86
118
  if (Math.abs(percent) < 0.05) return {
@@ -90,7 +122,7 @@ const ARROW = {
90
122
  const size = Math.abs(percent) >= 100 ? String(Math.round(Math.abs(percent))) : Math.abs(percent).toFixed(1);
91
123
  const good = lowerIsBetter ? percent < 0 : percent > 0;
92
124
  return {
93
- text: `${percent > 0 ? '↑' : '↓'} ${size}%`,
125
+ text: `${percent > 0 ? arrows[0] : arrows[1]} ${size}%`,
94
126
  tone: good ? 'good' : 'bad'
95
127
  };
96
128
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/panels/pulzr-figure-panel/figure.ts"],"sourcesContent":["import type { TimeSeries, TimeSeriesData } from '@perses-dev/spec';\n\n/** A series' last value that is a number. */\nexport function lastValue(series: TimeSeries | undefined): number | undefined {\n const values = series?.values ?? [];\n for (let i = values.length - 1; i >= 0; i--) {\n const v = values[i]![1];\n if (v !== null && Number.isFinite(v)) return v;\n }\n return undefined;\n}\n\n/**\n * The figure and the one it is set against. A series labelled `compare` (the\n * period before, as the Pulzr and Parkeze datasources label it) is the one\n * set against; the first other series is the figure.\n */\nexport function figureOf(\n results: Array<{ data?: TimeSeriesData }>,\n of?: string,\n): { now?: number; before?: number; total?: number } {\n const series = results.flatMap((r) => r.data?.series ?? []);\n const compared = (s: TimeSeries): boolean => Boolean(s.labels?.compare);\n const named = (s: TimeSeries): boolean => of !== undefined && (s.formattedName ?? s.name) === of;\n return {\n now: lastValue(series.find((s) => !compared(s) && !named(s))),\n before: lastValue(\n series.find((s) => compared(s) && !(of !== undefined && (s.formattedName ?? s.name).startsWith(of))),\n ),\n total: of === undefined ? undefined : lastValue(series.find((s) => !compared(s) && named(s))),\n };\n}\n\nconst TO_MINUTES: Record<string, number> = { seconds: 1 / 60, minutes: 1, hours: 60 };\n\n/**\n * A time in parts, as people say it: 45m, 6h 46m, 7h, and past two days 2d 3h, the minutes being noise there.\n * Undefined for a unit that is not a time.\n */\nexport function timeInParts(value: number, unit: string | undefined): string | undefined {\n const per = unit ? TO_MINUTES[unit] : undefined;\n if (per === undefined) return undefined;\n const minutes = Math.round(value * per);\n if (minutes < 60) return `${minutes}m`;\n if (minutes < 48 * 60) {\n const h = Math.floor(minutes / 60);\n const m = minutes % 60;\n return m ? `${h}h ${m}m` : `${h}h`;\n }\n const d = Math.floor(minutes / (24 * 60));\n const h = Math.round((minutes % (24 * 60)) / 60);\n return h ? `${d}d ${h}h` : `${d}d`;\n}\n\nexport type Direction = 'up' | 'down' | 'flat';\n\n/** The change from before to now, in percent, and which way: flat within half a percent. */\nexport function changeOf(\n now: number | undefined,\n before: number | undefined,\n): { percent: number; direction: Direction } | undefined {\n if (now === undefined || before === undefined || before <= 0) return undefined;\n const percent = ((now - before) / before) * 100;\n let direction: Direction = 'flat';\n if (percent > 0.5) direction = 'up';\n else if (percent < -0.5) direction = 'down';\n return { percent, direction };\n}\n\nconst ARROW: Record<Direction, string> = { up: '↑', down: '↓', flat: '→' };\n\n/** `↓ -47.3% vs last week`. */\nexport function changeText(\n change: { percent: number; direction: Direction },\n comparedLabel = 'vs the period before',\n): string {\n const sign = change.percent > 0 ? '+' : '';\n return `${ARROW[change.direction]} ${sign}${change.percent.toFixed(1)}% ${comparedLabel}`;\n}\n\n/** The last value of each series not labelled `compare`, by its formatted name. */\nexport function valuesOf(results: Array<{ data?: TimeSeriesData }>): Map<string, number> {\n const values = new Map<string, number>();\n for (const s of results.flatMap((r) => r.data?.series ?? [])) {\n if (s.labels?.compare) continue;\n const v = lastValue(s);\n if (v !== undefined) values.set(s.formattedName ?? s.name, v);\n }\n return values;\n}\n\n/** A template's `{{name}}` and `{{name|%}}` filled from the series' values; `—` for one with none. */\nexport function fill(template: string, values: Map<string, number>): string {\n return template.replace(/\\{\\{\\s*([^}|]+?)\\s*(\\|\\s*%\\s*)?\\}\\}/g, (_, name: string, percent?: string) => {\n const v = values.get(name);\n if (v === undefined) return '—';\n return percent ? `${v.toFixed(1)}%` : v.toLocaleString(undefined, { maximumFractionDigits: 1 });\n });\n}\n\nexport type Tone = 'good' | 'bad' | 'flat';\n\n/** The change as its size, good or bad news: `↑ 35.8%`, whole past 100%, `0%` within a twentieth of one. */\nexport function sizeChange(\n now: number | undefined,\n before: number | undefined,\n lowerIsBetter = false,\n): { text: string; tone: Tone } | undefined {\n if (now === undefined || before === undefined || before === 0) return undefined;\n const percent = ((now - before) / Math.abs(before)) * 100;\n if (Math.abs(percent) < 0.05) return { text: '0%', tone: 'flat' };\n const size = Math.abs(percent) >= 100 ? String(Math.round(Math.abs(percent))) : Math.abs(percent).toFixed(1);\n const good = lowerIsBetter ? percent < 0 : percent > 0;\n return { text: `${percent > 0 ? '↑' : '↓'} ${size}%`, tone: good ? 'good' : 'bad' };\n}\n"],"names":["lastValue","series","values","i","length","v","Number","isFinite","undefined","figureOf","results","of","flatMap","r","data","compared","s","Boolean","labels","compare","named","formattedName","name","now","find","before","startsWith","total","TO_MINUTES","seconds","minutes","hours","timeInParts","value","unit","per","Math","round","h","floor","m","d","changeOf","percent","direction","ARROW","up","down","flat","changeText","change","comparedLabel","sign","toFixed","valuesOf","Map","set","fill","template","replace","_","get","toLocaleString","maximumFractionDigits","sizeChange","lowerIsBetter","abs","text","tone","size","String","good"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../src/panels/pulzr-figure-panel/figure.ts"],"sourcesContent":["import type { TimeSeries, TimeSeriesData } from '@perses-dev/spec';\n\n/** A series' last value that is a number. */\nexport function lastValue(series: TimeSeries | undefined): number | undefined {\n const values = series?.values ?? [];\n for (let i = values.length - 1; i >= 0; i--) {\n const v = values[i]![1];\n if (v !== null && Number.isFinite(v)) return v;\n }\n return undefined;\n}\n\nconst nameOf = (s: TimeSeries): string => s.formattedName ?? s.name;\nconst compared = (s: TimeSeries): boolean => Boolean(s.labels?.compare);\n\n/** The series compared with the one named: `<name> · previous period`, `<name> · last year`. */\nexport function twinOf(series: TimeSeries[], name: string): TimeSeries | undefined {\n return series.find((s) => compared(s) && nameOf(s).startsWith(`${name} · `));\n}\n\n/**\n * The figure and the one it is set against. A series labelled `compare` (the period before, as the Pulzr and\n * Parkeze datasources label it) is the one set against; the first other series is the figure, leaving out `of` and\n * the series named in `others` (a sparkline's, the rows'). The period before is the figure's twin by name, or else\n * the first compared series that is not theirs.\n */\nexport function figureOf(\n results: Array<{ data?: TimeSeriesData }>,\n of?: string,\n others: string[] = [],\n): { now?: number; before?: number; total?: number } {\n const series = results.flatMap((r) => r.data?.series ?? []);\n const taken = of === undefined ? others : [of, ...others];\n const figure = series.find((s) => !compared(s) && !taken.includes(nameOf(s)));\n return {\n now: lastValue(figure),\n before: lastValue(\n (figure && twinOf(series, nameOf(figure))) ??\n series.find((s) => compared(s) && !taken.some((name) => nameOf(s).startsWith(name))),\n ),\n total: of === undefined ? undefined : lastValue(series.find((s) => !compared(s) && nameOf(s) === of)),\n };\n}\n\n/** The last value of the series named and of its twin, the period before. */\nexport function pairOf(results: Array<{ data?: TimeSeriesData }>, name: string): { now?: number; before?: number } {\n const series = results.flatMap((r) => r.data?.series ?? []);\n return {\n now: lastValue(series.find((s) => !compared(s) && nameOf(s) === name)),\n before: lastValue(twinOf(series, name)),\n };\n}\n\n/** Every number over time of the series named, for a sparkline. */\nexport function valuesOver(results: Array<{ data?: TimeSeriesData }>, name: string): number[] {\n const series = results.flatMap((r) => r.data?.series ?? []).find((s) => !compared(s) && nameOf(s) === name);\n return (series?.values ?? []).flatMap(([, v]) => (v !== null && Number.isFinite(v) ? [v] : []));\n}\n\nconst TO_MINUTES: Record<string, number> = { seconds: 1 / 60, minutes: 1, hours: 60 };\n\n/**\n * A time in parts, as people say it: 45m, 6h 46m, 7h, and past two days 2d 3h, the minutes being noise there.\n * Undefined for a unit that is not a time.\n */\nexport function timeInParts(value: number, unit: string | undefined): string | undefined {\n const per = unit ? TO_MINUTES[unit] : undefined;\n if (per === undefined) return undefined;\n const minutes = Math.round(value * per);\n if (minutes < 60) return `${minutes}m`;\n if (minutes < 48 * 60) {\n const h = Math.floor(minutes / 60);\n const m = minutes % 60;\n return m ? `${h}h ${m}m` : `${h}h`;\n }\n const d = Math.floor(minutes / (24 * 60));\n const h = Math.round((minutes % (24 * 60)) / 60);\n return h ? `${d}d ${h}h` : `${d}d`;\n}\n\nexport type Direction = 'up' | 'down' | 'flat';\n\n/** The change from before to now, in percent, and which way: flat within half a percent. */\nexport function changeOf(\n now: number | undefined,\n before: number | undefined,\n): { percent: number; direction: Direction } | undefined {\n if (now === undefined || before === undefined || before <= 0) return undefined;\n const percent = ((now - before) / before) * 100;\n let direction: Direction = 'flat';\n if (percent > 0.5) direction = 'up';\n else if (percent < -0.5) direction = 'down';\n return { percent, direction };\n}\n\nconst ARROW: Record<Direction, string> = { up: '↑', down: '↓', flat: '→' };\n\n/** `↓ -47.3% vs last week`. */\nexport function changeText(\n change: { percent: number; direction: Direction },\n comparedLabel = 'vs the period before',\n): string {\n const sign = change.percent > 0 ? '+' : '';\n return `${ARROW[change.direction]} ${sign}${change.percent.toFixed(1)}% ${comparedLabel}`;\n}\n\n/** The last value of each series not labelled `compare`, by its formatted name. */\nexport function valuesOf(results: Array<{ data?: TimeSeriesData }>): Map<string, number> {\n const values = new Map<string, number>();\n for (const s of results.flatMap((r) => r.data?.series ?? [])) {\n if (s.labels?.compare) continue;\n const v = lastValue(s);\n if (v !== undefined) values.set(s.formattedName ?? s.name, v);\n }\n return values;\n}\n\n/** A template's `{{name}}` and `{{name|%}}` filled from the series' values; `—` for one with none. */\nexport function fill(template: string, values: Map<string, number>): string {\n return template.replace(/\\{\\{\\s*([^}|]+?)\\s*(\\|\\s*%\\s*)?\\}\\}/g, (_, name: string, percent?: string) => {\n const v = values.get(name);\n if (v === undefined) return '—';\n return percent ? `${v.toFixed(1)}%` : v.toLocaleString(undefined, { maximumFractionDigits: 1 });\n });\n}\n\nexport type Tone = 'good' | 'bad' | 'flat';\n\n/** A pillar's arrows, for the change as a badge: `↗ 133%`. */\nexport const BADGE_ARROWS: [string, string] = ['↗', '↘'];\n\n/**\n * The change as its size, good or bad news: `↑ 35.8%`, whole past 100%, `0%` within a twentieth of one. `arrows`\n * are the rise's and the fall's.\n */\nexport function sizeChange(\n now: number | undefined,\n before: number | undefined,\n lowerIsBetter = false,\n arrows: [string, string] = ['↑', '↓'],\n): { text: string; tone: Tone } | undefined {\n if (now === undefined || before === undefined || before === 0) return undefined;\n const percent = ((now - before) / Math.abs(before)) * 100;\n if (Math.abs(percent) < 0.05) return { text: '0%', tone: 'flat' };\n const size = Math.abs(percent) >= 100 ? String(Math.round(Math.abs(percent))) : Math.abs(percent).toFixed(1);\n const good = lowerIsBetter ? percent < 0 : percent > 0;\n return { text: `${percent > 0 ? arrows[0] : arrows[1]} ${size}%`, tone: good ? 'good' : 'bad' };\n}\n"],"names":["lastValue","series","values","i","length","v","Number","isFinite","undefined","nameOf","s","formattedName","name","compared","Boolean","labels","compare","twinOf","find","startsWith","figureOf","results","of","others","flatMap","r","data","taken","figure","includes","now","before","some","total","pairOf","valuesOver","TO_MINUTES","seconds","minutes","hours","timeInParts","value","unit","per","Math","round","h","floor","m","d","changeOf","percent","direction","ARROW","up","down","flat","changeText","change","comparedLabel","sign","toFixed","valuesOf","Map","set","fill","template","replace","_","get","toLocaleString","maximumFractionDigits","BADGE_ARROWS","sizeChange","lowerIsBetter","arrows","abs","text","tone","size","String","good"],"mappings":"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"}
@@ -30,9 +30,29 @@ export interface PulzrFigurePanelOptions {
30
30
  detail?: string;
31
31
  /**
32
32
  * How the change reads. `signed` (the default): `↑ +0.7% vs last week`, flat within half a percent. `size`: `↑ 35.8%
33
- * vs last week`, whole past 100%, `0%` within a twentieth of one.
33
+ * vs last week`, whole past 100%, `0%` within a twentieth of one. `badge`: `↗ 133%`, small beside the figure, sized
34
+ * as `size`, without the compared label, the figure bold as a pillar's.
34
35
  */
35
- changeStyle?: 'signed' | 'size';
36
+ changeStyle?: 'signed' | 'size' | 'badge';
37
+ /** An area under the figure, of every value over time of the series named. `color` is the figure's unless set. */
38
+ sparkline?: {
39
+ series: string;
40
+ color?: string;
41
+ };
42
+ /**
43
+ * A list of figures under the figure (and its sparkline): each the last value of the series named, and its change
44
+ * as a badge against that series' twin, `<series> · previous period` or `· last year`.
45
+ */
46
+ rows?: PulzrFigureRow[];
47
+ }
48
+ export interface PulzrFigureRow {
49
+ label: string;
50
+ series: string;
51
+ format?: FormatOptions;
52
+ lowerIsBetter?: boolean;
53
+ timeInParts?: boolean;
54
+ /** Words after the value, `/ space / day`. */
55
+ suffix?: string;
36
56
  }
37
57
  export type PulzrFigurePanelProps = PanelProps<PulzrFigurePanelOptions, TimeSeriesData>;
38
58
  //# sourceMappingURL=pulzr-figure-panel-types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pulzr-figure-panel-types.d.ts","sourceRoot":"","sources":["../../../../src/panels/pulzr-figure-panel/pulzr-figure-panel-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,MAAM,WAAW,uBAAuB;IACtC,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,2GAA2G;IAC3G,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+FAA+F;IAC/F,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,sGAAsG;IACtG,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;OAGG;IACH,EAAE,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACxC;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qHAAqH;IACrH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,WAAW,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;CACjC;AAED,MAAM,MAAM,qBAAqB,GAAG,UAAU,CAAC,uBAAuB,EAAE,cAAc,CAAC,CAAC"}
1
+ {"version":3,"file":"pulzr-figure-panel-types.d.ts","sourceRoot":"","sources":["../../../../src/panels/pulzr-figure-panel/pulzr-figure-panel-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,MAAM,WAAW,uBAAuB;IACtC,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,2GAA2G;IAC3G,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+FAA+F;IAC/F,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,sGAAsG;IACtG,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;OAGG;IACH,EAAE,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACxC;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qHAAqH;IACrH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,WAAW,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;IAC1C,kHAAkH;IAClH,SAAS,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C;;;OAGG;IACH,IAAI,CAAC,EAAE,cAAc,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,8CAA8C;IAC9C,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,qBAAqB,GAAG,UAAU,CAAC,uBAAuB,EAAE,cAAc,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/panels/pulzr-figure-panel/pulzr-figure-panel-types.ts"],"sourcesContent":["import type { FormatOptions } from '@perses-dev/components';\nimport type { PanelProps } from '@perses-dev/plugin-system';\nimport type { TimeSeriesData } from '@perses-dev/spec';\n\nexport interface PulzrFigurePanelOptions {\n format?: FormatOptions;\n /** The figure's color, any CSS color. The theme's text color unless set. */\n color?: string;\n /** What the period compared is called, after the change: `vs last week`. */\n comparedLabel?: string;\n /** Shown in place of the change when there is nothing to compare with: `Average for the selected week`. */\n caption?: string;\n /** A fall is good news: a fall is drawn in the success color and a rise in the error color. */\n lowerIsBetter?: boolean;\n /** A time in parts, as people say it: 6h 46m, not 6.8h. For a format in seconds, minutes or hours. */\n timeInParts?: boolean;\n /**\n * The figure, a rate, as a count of a total beside it: `1690/2639 Spaces Occupied`. `series` names the\n * series holding the total (the query asks for both); `label` follows.\n */\n of?: { series: string; label?: string };\n /**\n * A line under the title, from the query's series: `of {{booked_space_hours}} booked hours, occupied`. `{{name}}` is\n * the series' value, `{{name|%}}` the value as a percent (16.4%).\n */\n subtitle?: string;\n /** A small box under the figure, from the query's series as `subtitle` is: `{{new}} new · {{renewals}} renewals`. */\n detail?: string;\n /**\n * How the change reads. `signed` (the default): `↑ +0.7% vs last week`, flat within half a percent. `size`: `↑ 35.8%\n * vs last week`, whole past 100%, `0%` within a twentieth of one.\n */\n changeStyle?: 'signed' | 'size';\n}\n\nexport type PulzrFigurePanelProps = PanelProps<PulzrFigurePanelOptions, TimeSeriesData>;\n"],"names":[],"mappings":"AAmCA,WAAwF"}
1
+ {"version":3,"sources":["../../../../src/panels/pulzr-figure-panel/pulzr-figure-panel-types.ts"],"sourcesContent":["import type { FormatOptions } from '@perses-dev/components';\nimport type { PanelProps } from '@perses-dev/plugin-system';\nimport type { TimeSeriesData } from '@perses-dev/spec';\n\nexport interface PulzrFigurePanelOptions {\n format?: FormatOptions;\n /** The figure's color, any CSS color. The theme's text color unless set. */\n color?: string;\n /** What the period compared is called, after the change: `vs last week`. */\n comparedLabel?: string;\n /** Shown in place of the change when there is nothing to compare with: `Average for the selected week`. */\n caption?: string;\n /** A fall is good news: a fall is drawn in the success color and a rise in the error color. */\n lowerIsBetter?: boolean;\n /** A time in parts, as people say it: 6h 46m, not 6.8h. For a format in seconds, minutes or hours. */\n timeInParts?: boolean;\n /**\n * The figure, a rate, as a count of a total beside it: `1690/2639 Spaces Occupied`. `series` names the\n * series holding the total (the query asks for both); `label` follows.\n */\n of?: { series: string; label?: string };\n /**\n * A line under the title, from the query's series: `of {{booked_space_hours}} booked hours, occupied`. `{{name}}` is\n * the series' value, `{{name|%}}` the value as a percent (16.4%).\n */\n subtitle?: string;\n /** A small box under the figure, from the query's series as `subtitle` is: `{{new}} new · {{renewals}} renewals`. */\n detail?: string;\n /**\n * How the change reads. `signed` (the default): `↑ +0.7% vs last week`, flat within half a percent. `size`: `↑ 35.8%\n * vs last week`, whole past 100%, `0%` within a twentieth of one. `badge`: `↗ 133%`, small beside the figure, sized\n * as `size`, without the compared label, the figure bold as a pillar's.\n */\n changeStyle?: 'signed' | 'size' | 'badge';\n /** An area under the figure, of every value over time of the series named. `color` is the figure's unless set. */\n sparkline?: { series: string; color?: string };\n /**\n * A list of figures under the figure (and its sparkline): each the last value of the series named, and its change\n * as a badge against that series' twin, `<series> · previous period` or `· last year`.\n */\n rows?: PulzrFigureRow[];\n}\n\nexport interface PulzrFigureRow {\n label: string;\n series: string;\n format?: FormatOptions;\n lowerIsBetter?: boolean;\n timeInParts?: boolean;\n /** Words after the value, `/ space / day`. */\n suffix?: string;\n}\n\nexport type PulzrFigurePanelProps = PanelProps<PulzrFigurePanelOptions, TimeSeriesData>;\n"],"names":[],"mappings":"AAqDA,WAAwF"}