Skip to content

Chart Dialog

The chart dialog edits a typed chart — column, bar, stacked column, stacked bar, line, area, stacked area, pie, donut, scatter, radar, or waterfall — with a data grid plus styling controls, and saves a versioned uhuu.chart.v2 payload.

A uhuu.chart.v2 value separates spec (how the chart looks) from data (the values it draws). Templates render live from those two objects with the same renderer the dialog previews with, so the dialog preview and the printed page are the same picture, and a later brand-color change reaches charts that were saved long ago.

Use chart when the field is a visualization the user should edit as a chart. For raw tabular rows the user pastes/imports, use the Table dialog; for the richer spreadsheet editor, use Spreadsheet.

Parameters

  • type (enum, required: "chart"): Opens the Chart dialog.
  • path (string, required): Payload path where the dialog response is stored.
  • state (object, optional): The current chart to reopen — a uhuu.chart.v2 value ({ schema, spec, data }). Pass this so the dialog opens with the existing chart instead of an empty default.
  • value (optional): Current chart value (same shape accepted as state).
  • config.brand (object, optional): Resolved brand colors, supplied by the host — { chart: string[], ink, line, muted, surface }, all plain hex. chart is the series palette; ink, line, muted, and surface theme the text, axes, gridlines, and background. The dialog resolves nothing itself: it draws with exactly the colors it is handed, which is what keeps its preview identical to the page. Omitted ⇒ a neutral fallback palette.
  • config.bound (object, optional): Who owns the values — { isBound: boolean, sourcePath?: string, allowOverride?: boolean }. See Bound and unbound charts. Omitted ⇒ treated as unbound.

The host supplies more than five series colors when a chart needs them; the extra stops are derived from the five brand anchors by the same rule everywhere, so series #6 gets the same color in the dialog and on the page.

Example Usage

javascript
export default ({ payload }) => {
  const { sales_chart } = payload; // { schema: 'uhuu.chart.v2', spec: {...}, data: {...} }

  return (
    <div
      onClick={() =>
        $uhuu.editDialog({
          path: 'sales_chart',
          type: 'chart',
          state: sales_chart,
          config: {
            brand: {
              chart: ['#2f6db4', '#6ea3a1', '#c9a227', '#8a6f9e', '#b46a55'],
              ink: '#18181b',
              line: '#e4e4e7',
              muted: '#71717a',
              surface: '#ffffff',
            },
            bound: { isBound: false },
          },
        })
      }
    >
      {/* render live from spec + data */}
    </div>
  );
};

Example Response

After saving, the payload at path receives a versioned chart document:

json
{
  "sales_chart": {
    "schema": "uhuu.chart.v2",
    "spec": {
      "version": 2,
      "type": "column",
      "title": "ARR by quarter",
      "subtitle": "Group total",
      "options": {
        "showGrid": true,
        "showXAxis": true,
        "showYAxis": true,
        "showLegend": true,
        "showLabels": true,
        "smoothLine": false,
        "innerRadiusRatio": 0
      },
      "format": { "kind": "currency", "currency": "CHF", "precision": 0 },
      "axis": { "valueTitle": "ARR", "categoryTitle": "Quarter" }
    },
    "data": {
      "categories": ["Q1", "Q2", "Q3", "Q4"],
      "series": [
        { "name": "New business", "values": [120000, 148000, 151000, 190000] },
        { "name": "Renewals", "values": [86000, 91000, 99000, 104000] }
      ]
    }
  }
}

What the two halves hold:

  • spectype, title/subtitle, options, format (one locale-aware number format used by both axis ticks and data labels), axis (titles plus optional min/max), and optional color overrides.
    • options always carries showGrid, showXAxis, showYAxis, showLegend, showLabels, smoothLine, and innerRadiusRatio (0 for a pie, ~0.6 for a donut). Type-dependent extras appear when relevant: stacked and horizontal override what the type implies, plus legendPosition, barPadding, showPoints, pointRadius, areaOpacity, and waterfallTotals.
    • palette (whole-chart) and seriesColors (keyed by series name, or by category for pie/donut/radar) hold resolved color strings. Omit them and the chart follows config.brand, which is what lets a later brand change repaint it; a literal hex freezes that series.
  • datacategories: string[] plus series: [{ name, values }], one value per category. A pie or donut is a single-series data. This is the only data shape the dialog reads or writes.

There is no pre-rendered SVG in the response. Render from spec + data.

Bound and unbound charts

A chart's values can come from a bound data source or be typed into the dialog. config.bound tells the dialog which, so the two editors never fight over the same numbers.

Slot stateData gridPresentation controlsWhat save writes
Bound (isBound: true)Read-only, with a note naming the source: "Values come from finance.quarters — edit the data source"Livespec only; the values stay owned by the data source
Unbound (isBound: false or omitted)Editable — this is the calibration surface for the chartLivespec + data
Bound with allowOverride: trueEditableLivespec + data, written as a document-level override; the source is untouched

sourcePath is display text for that note — pass the path the user would recognize, not an internal identifier.

Schema-bound fields (Chart / DataTable)

A template can bind a field to a uhuu schema primitive (Chart or DataTable from uhuu-schemas/common) instead of storing the dialog's own document. The schema Chart is meaning-shaped — { type, categories, series: [{ name, data }], valueAxis } — and a schema DataTable is { columns, rows } of strings.

For charts this is now a rename, not a bridge to a third shape: the schema Chart's categories and series[].data map straight onto ChartData's categories and series[].values, and type maps onto spec.type. The editor host still converts, but nothing is reshaped in between.

  • On opencategories/series become the dialog's data, type seeds spec.type, and the value is supplied as the dialog state. A schema DataTable is converted to the Table dialog's state.data cell matrix.
  • On save — the dialog's output is merged onto the original value, never substituted for it, so anything the dialog did not set survives the round trip: valueAxis.min/max left to the template on a Chart, a caption on a DataTable. Only the keys the dialog actually owns are replaced.

So both patterns are valid, and you choose per template:

PatternPayload storesRender readsRound-trip
Wire-shapeduhuu.chart.v2 / { data } matrix.spec + .data · .datanative — no conversion
Schema-boundChart / DataTable primitive.categories/.series · .columns/.rowshost renames fields to/from the dialog

Notes

  • Source data does not have to be ChartData already. A [{ label, value }] array, a [{ name, value }] array, or a record array plus a spec.encoding field mapping are all normalized to categories + series on the way in — malformed input yields an empty chart rather than an error. ChartData is what gets stored, though; the other shapes are inputs only.
  • spec.format is the single number format for the chart. Axis ticks and data labels always agree, and both follow the document's locale.
  • Charts are print output, not dashboards: there are no tooltips, hover states, or drill-downs.

Public developer documentation for Uhuu.