Fluxo UIFluxo UIv0.4.1

Expressions

Report Builder ships a hand-written expression language. There is no eval, new Function, or string-form setTimeout — every expression goes through a typed tokenizer → recursive-descent parser → AST walker. All component props that can be dynamic accept an expression by prefixing a = sign; anything else is treated as a literal.

Most props panels already switch to the expression editor with a single fx toggle; what you see in the playground below is the same editor component used in the builder.

Playground

Expression Playground

Type an expression (no leading = needed). Syntax highlighting and completions mirror what the props panels give you inside the builder. Result updates on every keystroke.

Presets:
Result
$83,831
Evaluation context (the maps that Datasources.*, Parameters.*, Variables.*, and BuiltInFields.* draw from)
{
  "datasources": {
    "orders": "[80 orders]",
    "employees": "[48 employees]"
  },
  "parameters": {
    "region": "North",
    "minRevenue": 500,
    "statuses": [
      "paid",
      "shipped"
    ],
    "today": "2026-05-02"
  },
  "variables": {
    "selectedOrder": {
      "id": "ORD-1042",
      "revenue": 1850,
      "region": "North"
    },
    "selectedEmployee": null
  },
  "builtInFields": {
    "CurrentUser": {
      "id": "user-42",
      "name": "Ada Lovelace",
      "role": "Analyst"
    },
    "Tenant": {
      "id": "acme",
      "name": "Acme Corp"
    },
    "Env": "production"
  }
}

Namespaces

Only the following identifier roots are recognised. Anything else is an unknown identifier.

  • Datasources.<name> / Datasources.<name>.<field> — rows or a projected field array
  • Parameters.<name> (with optional .path.to.field) — host-owned input values
  • Variables.<name> (with optional .path) — in-report writable state, set by drill-through / click actions
  • BuiltInFields.<name> — read-only host constants supplied via initReportBuilder or the component builtInFields prop
  • Field.<name> — the current row being rendered (inside a table cell, chart formatter, row visibility expression, etc.)
  • RowGroup('name').key / .keys / .values / .Fields.<field> / .Variables.<name> — access a row-group bucket
  • ColGroup('name') — same shape as RowGroup, for column-grouping contexts

Worked Examples

Aggregates

Aggregating over a whole datasource, typically in head/footer rows and KPI text.

Total revenue
FormatCurrency(Sum(Datasources.orders, 'revenue'), '$', 0)
Sum accepts either an array of numbers or an array of rows + field name. Combine with FormatCurrency for display.
Average performance, one decimal
Round(Avg(Datasources.employees, 'performance'), 1)
Avg returns 0 for an empty set — remember to guard with IsEmpty if you want a distinct no-data state.
Count of non-empty emails
Count(Datasources.employees, 'email')
Count with a field name only counts rows where the field is not null / undefined — pass no field to count all rows.

Conditionals

Branching output from a single expression. IIf is the workhorse; Switch is cleaner for many-way dispatch.

Status badge text
IIf(Field.status == 'paid', '✓ Paid', IIf(Field.status == 'pending', '⏳ Pending', '✗ Other'))
IIf nests to form if/else-if chains. For three or more branches, prefer Switch.
Switch-based label
Switch(Field.status, 'paid', 'Completed', 'pending', 'In review', 'cancelled', 'Dropped', 'Other')
Switch(value, case1, result1, ..., fallback). If the arg count is even, the last arg is used as the default.
Parameter-driven filter clause
IsEmpty(Parameters.region) || Field.region == Parameters.region
The optional-filter idiom: IsEmpty short-circuits so a blank parameter disables the clause entirely.

String / Number formatting

Turn raw values into display strings. All formatters are null-safe and return empty strings on garbage input.

Currency
FormatCurrency(Field.revenue, '€', 2)
symbol and decimals default to "$" and 2. Thousands separators are always commas in the default output.
Percent from ratio
FormatPercent(Field.revenue / Field.target, 1)
Input is a 0..1 ratio; the formatter multiplies by 100 and appends the % symbol.
Zero-padded invoice id
Concat('INV-', PadLeft(Field.id, 6, '0'))
PadLeft / PadRight take a target length and a pad character (default space).

Dates

Date math and formatting. Dates are ISO strings internally — the functions accept any Date-parseable string.

Today
Today()
Returns "YYYY-MM-DD" in local time. Use Now() for the full ISO timestamp.
Days since order
DateDiff('day', Field.orderDate, Today())
Unit may be day/hour/minute. Negative when the second arg is earlier.
Pretty date
FormatDate(Field.orderDate, 'YYYY-MM-DD')
Only YYYY, MM, DD tokens today — ship a richer format string if you need other units.

Variable paths

Variables are writable state. Anything drilled from a table or chart lands in a Variable and can be re-read by expressions elsewhere.

Nested field after drill-through
Variables.selectedOrder.revenue
Drill writes the whole row, so you navigate with .field.nested to pull out what you want.
Gate a section on a selection
!IsEmpty(Variables.selectedOrder)
Typical pattern for styles.visible on sub-reports / panels — appear only when a row has been drilled.

Built-in fields

Host-supplied constants (current user, tenant, feature flags). Combined from initReportBuilder registrations and per-instance props.

Current user name
BuiltInFields.CurrentUser.name
Access like any object path. Getters are resolved lazily on each evaluation, so clock-style values stay fresh.
Environment gate
BuiltInFields.Env == 'production'
Returns a boolean you can feed into styles.visible or Hidden (advanced).

Row-group scope

Table row groups expose a per-bucket frame in expressions. Inside a group-level formatter (for example a group header text or footer cell), you address the enclosing group with RowGroup('name').

  • RowGroup('region').key — the single grouping key (e.g. "North") when the row group has a single key.
  • RowGroup('region').keys — array form when the group is keyed by multiple fields.
  • RowGroup('region').values — the rows inside this bucket. Feed this directly into Sum / Count / Avg.
  • RowGroup('region').Fields.revenue — shortcut for the most-recent row's field value in that bucket.
  • RowGroup('region').Variables.myVar — a per-bucket variable computed by the group engine.

Rules & gotchas

  • No unsafe eval. Every expression is parsed into an AST; there is no string execution.
  • Missing property paths return null. Forbidden path segments (__proto__, prototype, constructor) also return null.
  • Type mismatches surface as warnings. If a field expects boolean and your expression returns string, the editor shows an error.
  • Optional-filter pattern. Always write IsEmpty(Parameters.x) || <clause> so a blank parameter disables the clause instead of excluding every row.
  • Parameters are read-only inside the viewer. Click actions, drills, and chart clicks write to Variables. Parameters only change via the parameter panel's Apply button.
  • Async built-in fields. A built-in field may be a Promise-returning getter. The evaluator awaits it, so the expression simply resolves when the value is ready — no special syntax required.