Files
MyFastHtml/docs/DataGrid Formatting - User Guide.md

36 KiB
Raw Blame History

DataGrid Formatting - User Guide

Introduction

The DataGrid Formatting system provides a powerful way to apply conditional styling and value formatting to your data grids. It allows you to create rich, readable data presentations with minimal code.

Key features:

  • Apply styles (colors, fonts, decorations) based on cell values
  • Format values for display (currencies, dates, percentages, text transformations)
  • Create conditional rules that respond to data
  • Reference other cells for cross-column/row comparisons
  • Define formatting at multiple levels (cell, row, column, table, or globally)

Common use cases:

  • Highlight negative values in financial reports
  • Format currency and percentage values
  • Color-code status indicators
  • Display custom labels for enumerated values
  • Apply different styles based on data thresholds
  • Create visual hierarchies with conditional formatting

Quick Start

Here's a simple example to highlight negative values in red:

column amount:
    style("error") if value < 0
    format("EUR")

This creates a formatting rule that:

  • Applies to all cells in the "amount" column
  • Colors negative values with the "error" style (red background)
  • Formats all values as Euro currency

Understanding Formatting

Formatting Levels

Formatting can be applied at five different levels, each with its own specificity:

Level Cells Targeted Condition Evaluated On Specificity
Cell 1 specific cell The cell value Highest (1)
Row All cells in the row Each cell value High (2)
Column All cells in the column Each cell value Medium (3)
Table All cells in a specific table Each cell value Low (4)
Tables All cells in all tables (global) Each cell value Lowest (5)

Visual hierarchy:

Cell (amount, 5)        ← Highest priority
    ↓
Row 5                   ← Overridden by cell
    ↓
Column amount           ← Overridden by row and cell
    ↓
Table "products"        ← Overridden by column, row, and cell
    ↓
Tables (global)         ← Lowest priority, applies everywhere

How Rules Are Evaluated

Rules are processed from highest to lowest specificity. When multiple rules match:

  1. Cell-level rules win over all others
  2. Row-level rules win over column, table, and global rules
  3. Column-level rules win over table and global rules
  4. Table-level rules win over global rules
  5. Global rules apply only if no other rules match

Conflict Resolution

When multiple rules at the same level match a cell, style and formatter are resolved independently:

Style rules compete among themselves:

  1. Specificity = number of conditions in the rule (0 or 1)
  2. Higher specificity wins
  3. At equal specificity, last rule wins

Formatter rules compete among themselves:

  1. Same specificity logic as styles
  2. Resolved independently from styles

Style and formatter can fuse:

  • If one rule provides the style and another provides the formatter, both are applied
  • This allows separating concerns (e.g., unconditional formatting + conditional styling)

Example 1: Style competition

column amount:
    style(color="gray")                       # Specificity: 0 (no condition)
    style("error") if value < 0               # Specificity: 1
    style("success", bold=True) if value > 0  # Specificity: 1
  • For value = -10: "error" style wins (specificity 1 > 0)
  • For value = 50: "success" style wins (specificity 1 > 0)
  • For value = 0: gray color applies (no other rules match)

Example 2: Style and formatter fusion

column amount:
    format("EUR")                                     # Unconditional formatter
    style("error") if value < 0                       # Conditional style
  • For value = -100:

    • Style "error" applied (from conditional rule)
    • Format "EUR" applied (from unconditional rule)
    • Result: -100,00 € with red background
  • For value = 100:

    • No style (condition not met)
    • Format "EUR" applied
    • Result: 100,00 € with default style

Example 3: Multiple styles and formatters

column amount:
    style("neutral")                                  # Unconditional style
    format("EUR")                                     # Unconditional formatter
    style("error") if value < 0                       # Conditional style (higher specificity)
    format.number(precision=0, suffix=" €") if value < 0  # Conditional formatter (higher specificity)
  • For value = -5.67:

    • Style: "error" wins (specificity 1 > 0)
    • Formatter: precision=0 wins (specificity 1 > 0)
    • Result: -6 € with red background
  • For value = 1234.56:

    • Style: "neutral" applies (no conditional matches)
    • Formatter: "EUR" applies (no conditional matches)
    • Result: 1 234,56 € with neutral background

How Rules Work

A formatting rule consists of three optional parts:

[style(...)] [format(...)] [if <condition>]

Rules:

  • At least one of style() or format() must be present
  • condition is optional
  • If a condition is present, the rule applies only when the condition is met
  • Multiple rules can be defined for the same scope

Examples:

# Style only
style("error")

# Format only
format("EUR")

# Style + Format
style("error") format("EUR")

# With condition
style("error") if value < 0

# All combined
style("error") format("EUR") if value < 0

Writing Formatting Rules

Basic Syntax

A DSL document consists of scopes, each containing one or more rules:

<scope>:
    <rule>
    <rule>
    ...

<scope>:
    <rule>
    ...

Scopes define which cells a rule applies to:

Scope Syntax Applies To Example
column <name>: All cells in the column column amount:
column "<name>": Column with spaces column "total amount":
row <index>: All cells in the row row 0:
cell (<col>, <row>): Single cell by coordinates cell (amount, 3):
cell <cell_id>: Single cell by ID cell tcell_grid1-3-2:
table "<name>": All cells in a specific table table "products":
tables: All cells in all tables tables:

Indentation and comments:

# This is a comment

column amount:
    # Rules must be indented under their scope
    style("error") if value < 0
    format("EUR")

# Column names with spaces need quotes
column "total amount":
    style("neutral", bold=True)

Styling Cells

The style() function applies visual formatting to cells.

Syntax:

style(<preset>)
style(<preset>, <options>)
style(<options>)

Available style presets (DaisyUI 5):

Preset Background Text Use Case
primary Primary theme color Primary content Main highlights
secondary Secondary theme color Secondary content Secondary highlights
accent Accent theme color Accent content Accent highlights
neutral Neutral theme color Neutral content Headers, totals
info Info (blue) Info content Information
success Success (green) Success content Positive values
warning Warning (yellow) Warning content Warnings
error Error (red) Error content Negative values, errors

Style properties:

Property Type Description Example
background_color string Background color "#ffeeee", "red", "var(--color-primary)"
color string Text color "#cc0000", "white"
bold boolean Bold text True, False
italic boolean Italic text True, False
underline boolean Underlined text True, False
strikethrough boolean Strikethrough text True, False
font_size string Font size "12px", "0.9em"

Examples:

# Preset only
column status:
    style("error")

# Preset with overrides
column total:
    style("neutral", bold=True)

# Multiple properties
column name:
    style("success", italic=True, underline=True)

# No preset, direct properties
column notes:
    style(color="red", bold=True)
    style(background_color="#ffeeee", color="#cc0000")

Formatting Values

The format() function transforms cell values for display without changing the underlying data.

Using presets:

# Preset only
format("EUR")

# Preset with overrides
format("EUR", precision=3)

Available formatter presets:

Preset Type Description Example Output
EUR number Euro currency 1 234,56 €
USD number US Dollar $1,234.56
percentage number Percentage (×100) 45.2%
short_date date DD/MM/YYYY 29/01/2026
iso_date date YYYY-MM-DD 2026-01-29
yes_no boolean Yes/No Yes

Using explicit types:

When not using a preset, specify the type explicitly:

format.number(precision=2, suffix=" €", thousands_sep=" ")
format.date(format="%d/%m/%Y")
format.boolean(true_value="Oui", false_value="Non")
format.text(max_length=50, ellipsis="...")
format.enum(source={"draft": "Draft", "published": "Published"})
format.constant(value="N/A")

Number Formatter

Formats numbers, currencies, and percentages.

Parameters:

Parameter Type Default Description
prefix string "" Text before value (e.g., "$")
suffix string "" Text after value (e.g., " €", "%")
thousands_sep string "" Thousands separator (e.g., ",", " ")
decimal_sep string "." Decimal separator (e.g., ".", ",")
precision int 0 Number of decimal places
multiplier number 1 Multiply value before display (e.g., 100 for %)
absolute boolean False Display absolute value (no sign)

Examples:

# Euro currency
column amount:
    format.number(suffix=" €", thousands_sep=" ", decimal_sep=",", precision=2)

# US Dollar
column price:
    format.number(prefix="$", thousands_sep=",", precision=2)

# Percentage
column rate:
    format.number(suffix="%", multiplier=100, precision=1)

# Large numbers with thousands separator
column population:
    format.number(thousands_sep=",", precision=0)

# Absolute value - display variance without sign
column variance:
    format.number(absolute=True, suffix=" €", precision=2)

Date Formatter

Formats dates and datetimes using strftime patterns.

Parameters:

Parameter Type Default Description
format string "%Y-%m-%d" strftime format pattern

Common format patterns:

Pattern Description Example
"%Y-%m-%d" ISO format 2026-01-29
"%d/%m/%Y" European 29/01/2026
"%m/%d/%Y" US format 01/29/2026
"%d %b %Y" Short month 29 Jan 2026
"%d %B %Y" Full month 29 January 2026
"%Y-%m-%d %H:%M" With time 2026-01-29 14:30

Examples:

# European format
column created_at:
    format.date(format="%d/%m/%Y")

# Full date with month name
column updated_at:
    format.date(format="%d %B %Y")

# ISO format with time
column timestamp:
    format.date(format="%Y-%m-%d %H:%M:%S")

Boolean Formatter

Formats boolean and binary values.

Parameters:

Parameter Type Default Description
true_value string "true" Display for true/1
false_value string "false" Display for false/0
null_value string "" Display for null/None

Examples:

# Yes/No
column active:
    format.boolean(true_value="Yes", false_value="No")

# Checked/Unchecked
column completed:
    format.boolean(true_value="✓", false_value="✗")

# Custom labels
column status:
    format.boolean(true_value="Enabled", false_value="Disabled", null_value="Unknown")

Text Formatter

Formats and transforms text values.

Parameters:

Parameter Type Default Description
transform string - "uppercase", "lowercase", "capitalize"
max_length int - Truncate if exceeded
ellipsis string "..." Suffix when truncated

Examples:

# Uppercase
column code:
    format.text(transform="uppercase")

# Truncate long text
column description:
    format.text(max_length=50, ellipsis="...")

# Capitalize first letter
column name:
    format.text(transform="capitalize")

Enum Formatter

Maps values to display labels. Useful for status codes, categories, and dropdown lists.

Parameters:

Parameter Type Default Description
source object - Mapping or datagrid reference
default string "" Label for unknown values

Source types:

Static mapping:

column status:
    format.enum(
        source={
            "draft": "Draft",
            "pending": "Pending Review",
            "approved": "Approved",
            "rejected": "Rejected"
        },
        default="Unknown"
    )

From another DataGrid:

column category_id:
    format.enum(
        source={
            "type": "datagrid",
            "value": "categories_grid",
            "value_column": "id",
            "display_column": "name"
        }
    )

This looks up the category_id value in the "categories_grid", finds the matching row by "id", and displays the "name" column.

Constant Formatter

Displays a fixed value regardless of the cell's actual value. Useful for placeholder text or fixed labels.

Parameters:

Parameter Type Default Description
value string - The constant text to display

Examples:

# Display "N/A" for all cells
column placeholder:
    format.constant(value="N/A")

# Display fixed label
column type:
    format.constant(value="Product")

# Combined with condition
column optional_field:
    format.constant(value="—") if value isempty

Conditional Formatting

Conditions determine when a rule applies. They use the syntax:

if <left> <operator> <right>
if <operand> <unary_operator>

Comparison Operators

Operator Description Example
== Equal value == 0
!= Not equal value != ""
< Less than value < 0
<= Less or equal value <= 100
> Greater than value > 1000
>= Greater or equal value >= 0

Examples:

column amount:
    style("error") if value < 0
    style("success") if value > 1000
    style("warning") if value == 0

String Operators

Operator Description Example
contains String contains value contains "error"
startswith String starts with value startswith "ERR"
endswith String ends with value endswith ".pdf"

Examples:

column message:
    style("error") if value contains "error"
    style("info") if value startswith "INFO"

column filename:
    style("accent") if value endswith ".pdf"

Note: String comparisons are case-insensitive by default. See "Case Sensitivity" below for case-sensitive matching.

List Operators

Operator Description Example
in Value in list value in ["A", "B", "C"]
between Value in range value between 0 and 100

Examples:

column status:
    style("success") if value in ["approved", "completed"]
    style("warning") if value in ["pending", "review"]

column score:
    style("error") if value between 0 and 30
    style("warning") if value between 31 and 70
    style("success") if value between 71 and 100

Empty and NaN Checks

Operator Description Example
isempty Is null or empty string value isempty
isnotempty Is not null or empty value isnotempty
isnan Is NaN (Not a Number) value isnan

Examples:

column notes:
    style("neutral") if value isempty
    format.constant(value="—") if value isempty

column calculation:
    style("error") if value isnan
    format.constant(value="Error") if value isnan

column required_field:
    style("warning") if value isempty

Note: isempty checks for None or empty string (""). isnan specifically checks for invalid float values (NaN).

Cell References

References allow comparing a cell's value with values from other cells.

Reference Description Example
value Current cell value value < 0
col.<name> Value from another column (same row) value > col.budget
col."<name>" Column with spaces value > col."max amount"
row.<index> Value from another row (same column) value != row.0
cell.<col>-<row> Specific cell by coordinates value == cell.status-0

Examples:

# Compare with another column
column actual:
    style("error") if value > col.budget
    style("warning") if value > col.budget * 0.9

# Compare with header row
column total:
    style("neutral", bold=True) if value == row.0

# Compare with specific cell
column status:
    style("success") if value == cell.status-0

Arithmetic in references:

You can perform simple arithmetic with column references:

column spending:
    style("warning") if value > col.budget * 0.8
    style("error") if value > col.budget * 1.1

Negation

Use not to negate any condition:

column status:
    style("error") if not value in ["valid", "approved"]
    style("warning") if not value contains "OK"

column amount:
    style("success") if not value < 0

Case Sensitivity

String comparisons are case-insensitive by default. Use the (case) modifier for case-sensitive matching:

column code:
    style("error") if value == "Error" (case)
    style("warning") if value contains "WARN" (case)

column status:
    # This matches "approved", "APPROVED", "Approved"
    style("success") if value == "approved"

    # This matches only "APPROVED"
    style("info") if value == "APPROVED" (case)

Complete Examples

Example 1: Highlight Negative Values

Simple financial data with negative values highlighted:

column amount:
    style("error") if value < 0
    format("EUR")

Result:

  • Negative values: red background, formatted as Euro
  • Positive values: normal display, formatted as Euro

Example 2: Status Indicators with Color Coding

Map status codes to colors and labels:

column status:
    format.enum(
        source={
            "draft": "Draft",
            "pending": "Pending Review",
            "approved": "Approved",
            "rejected": "Rejected"
        },
        default="Unknown"
    )
    style("neutral") if value == "draft"
    style("warning") if value == "pending"
    style("success") if value == "approved"
    style("error") if value == "rejected"

Result:

  • "draft" → "Draft" with neutral background
  • "pending" → "Pending Review" with yellow background
  • "approved" → "Approved" with green background
  • "rejected" → "Rejected" with red background

Example 3: Budget vs Actual with Thresholds

Compare actual spending against budget with warning thresholds:

column actual:
    format("EUR")
    style("success") if value <= col.budget * 0.8
    style("warning") if value > col.budget * 0.8
    style("error") if value > col.budget

column budget:
    format("EUR")

Result:

  • Actual ≤ 80% of budget: green background
  • Actual > 80% but ≤ 100%: yellow background
  • Actual > 100%: red background

Example 4: Financial Report with Multiple Rules

Complete financial report with headers, formatting, and conditional highlighting:

# Global styling for all tables
tables:
    style(font_size="14px")

# Header row
row 0:
    style("neutral", bold=True)

# Amount column
column amount:
    format.number(precision=2, suffix=" €", thousands_sep=" ")
    style("error") if value < 0
    style("success") if value > col.target

# Percentage column
column progress:
    format("percentage")
    style("error") if value < 0.5
    style("warning") if value between 0.5 and 0.8
    style("success") if value > 0.8

# Status column
column status:
    format.enum(
        source={
            "draft": "Draft",
            "review": "In Review",
            "approved": "Approved",
            "rejected": "Rejected"
        }
    )
    style("neutral") if value == "draft"
    style("info") if value == "review"
    style("success") if value == "approved"
    style("error") if value == "rejected"

# Date column
column created_at:
    format.date(format="%d %b %Y")

# Highlight specific important cell
cell (amount, 10):
    style("accent", bold=True)

Result: A fully formatted financial report with:

  • Bold headers with neutral background
  • Euro amounts with conditional coloring
  • Progress percentages with traffic light colors
  • Status labels with appropriate colors
  • Formatted dates
  • Highlighted total row

Using the DSL Editor

Editor Features

The DSL editor provides a comfortable writing experience with:

Syntax highlighting:

  • Keywords (column, row, if, not) are highlighted
  • Functions (style, format) are colored differently
  • Strings, numbers, and operators have distinct colors

Line numbers:

  • Easy reference for error messages
  • Quick navigation in large documents

Auto-indentation:

  • Automatic indentation after scope declarations
  • Tab key for manual indentation
  • Maintains Python-style indentation

Autocompletion

The editor provides context-aware suggestions to help you write rules quickly and correctly.

How to trigger:

  • Ctrl+Space - Manual trigger at any time
  • Automatic - Triggers after typing ., (, ", or space in certain contexts

What can be autocompleted:

Context Suggestions
Start of line column, row, cell, table, tables
After column Column names from your DataGrid
After row Row indices (0, 1, 2, ...)
After table Table name from current DataGrid
Indented line style(, format(, format.
Inside style(" Style presets (primary, error, success, ...)
After style( Preset names and parameters (bold=, color=, ...)
Inside format(" Format presets (EUR, USD, percentage, ...)
After format. Format types (number, date, boolean, text, enum, constant)
After if value, col., row., not
After operand Operators (==, <, >, contains, in, ...)
After col. Column names
After operator col., literal values, True, False

Example workflow:

User types     │ Suggestions
───────────────┼────────────────────────────────────
col            │ column
column         │ [column names from grid]
column amount: │ (new line, indent)
    st         │ style
    style(     │ "error", "warning", "success", ...
    style("e   │ "error"
    style("error", │ bold=, italic=, color=, ...
    style("error", bold=│ True, False
    style("error") │ format(, format., if
    style("error") if │ value, col., not
    style("error") if value │ ==, !=, <, >, contains, in, ...
    style("error") if value < │ [number input]

Syntax Validation

The editor provides real-time syntax checking as you type.

Error indicators:

┌─────────────────────────────────────────────────────┐
│  1  column amount:                                  │
│  2      style("error" if value < 0                  │
│         ▲▲▲▲▲▲▲▲▲▲▲▲▲                              │
│         ╰─ Missing closing parenthesis              │
│  3                                                  │
└─────────────────────────────────────────────────────┘

Visual feedback:

  • Red underline - Syntax error at that position
  • Gutter marker - Error icon in the line number area
  • Tooltip - Hover over the error for details

Common errors and fixes:

Error Cause Fix
Expected ':' Missing colon after scope Add : after scope name
Expected indentation Rule not indented Indent rule under scope
Unexpected token Typo or invalid syntax Check spelling, operators
Missing closing quote Unclosed string Add closing " or '
Missing closing parenthesis Unclosed function call Add closing )
Unknown column Column doesn't exist Check column name spelling

Example - Before and after fixing:

Before (error):

column amount
    style("error") if value < 0

Error: Expected ':' after column name

After (fixed):

column amount:
    style("error") if value < 0

Managing Presets

Using Built-in Presets

Style presets (DaisyUI 5):

Preset Background Text Typical Use
primary Primary theme color Primary content Main highlights, important data
secondary Secondary theme color Secondary content Secondary highlights
accent Accent theme color Accent content Accent highlights, special items
neutral Neutral theme color Neutral content Headers, totals, summaries
info Info (blue) Info content Information, notes
success Success (green) Success content Positive values, completed items
warning Warning (yellow) Warning content Warnings, thresholds exceeded
error Error (red) Error content Negative values, errors, failures

Formatter presets:

Preset Type Configuration Example Output
EUR number suffix: " €", thousands_sep: " ", decimal_sep: ",", precision: 2 1 234,56 €
USD number prefix: "$", thousands_sep: ",", decimal_sep: ".", precision: 2 $1,234.56
percentage number suffix: "%", precision: 1, multiplier: 100 45.2%
short_date date format: "%d/%m/%Y" 29/01/2026
iso_date date format: "%Y-%m-%d" 2026-01-29
yes_no boolean true_value: "Yes", false_value: "No" Yes

Creating Custom Presets

You can add your own presets to use across all DataGrids.

Adding a custom style preset:

from myfasthtml.controls.DataGridsManager import DataGridsManager

# Get the global manager
manager = DataGridsManager.get_instance()

# Add custom style preset
manager.add_style_preset("highlight", {
    "background_color": "yellow",
    "color": "black",
    "font_weight": "bold"
})

# Use in DSL
column important:
    style("highlight")

Adding a custom formatter preset:

# Swiss Franc currency
manager.add_formatter_preset("CHF", {
    "type": "number",
    "prefix": "CHF ",
    "thousands_sep": "'",
    "decimal_sep": ".",
    "precision": 2
})

# Use in DSL
column price:
    format("CHF")

Adding multiple presets:

# Custom style presets
manager.add_style_preset("urgent", {
    "background_color": "#ff6b6b",
    "color": "white",
    "font_weight": "bold"
})

manager.add_style_preset("completed", {
    "background_color": "#51cf66",
    "color": "white",
    "text_decoration": "line-through"
})

# Custom formatter presets
manager.add_formatter_preset("GBP", {
    "type": "number",
    "prefix": "£",
    "thousands_sep": ",",
    "precision": 2
})

manager.add_formatter_preset("compact_date", {
    "type": "date",
    "format": "%d/%m/%y"
})

Removing presets:

# Remove style preset
manager.remove_style_preset("highlight")

# Remove formatter preset
manager.remove_formatter_preset("CHF")

Reference

Style Properties

Complete reference of all style properties:

Property Type Values Description
preset string See Style Presets below Preset name (applied first)
background_color string Hex, CSS name, or variable Background color
color string Hex, CSS name, or variable Text color
bold boolean True, False Bold text
italic boolean True, False Italic text
underline boolean True, False Underlined text
strikethrough boolean True, False Strikethrough text
font_size string CSS size value Font size (e.g., "12px", "0.9em")

Color values:

# Hex colors
style(color="#cc0000")

# CSS color names
style(color="red")

# DaisyUI CSS variables
style(color="var(--color-primary)")
style(background_color="var(--color-base-200)")

Combining properties:

# Preset + overrides
style("error", bold=True, font_size="16px")

# Multiple properties
style(background_color="#fffacd", color="#856404", bold=True, italic=True)

Style Presets

DaisyUI 5 color presets:

Preset Background CSS Variable Text CSS Variable
primary var(--color-primary) var(--color-primary-content)
secondary var(--color-secondary) var(--color-secondary-content)
accent var(--color-accent) var(--color-accent-content)
neutral var(--color-neutral) var(--color-neutral-content)
info var(--color-info) var(--color-info-content)
success var(--color-success) var(--color-success-content)
warning var(--color-warning) var(--color-warning-content)
error var(--color-error) var(--color-error-content)

Note: All presets use font_weight: "normal", font_style: "normal", text_decoration: "none" by default.

Formatter Types

Possible formatter types :

  • number
  • date
  • boolean
  • text
  • enum
  • constant

Number Formatter

Parameter Type Default Description
prefix string "" Text before value
suffix string "" Text after value
thousands_sep string "" Thousands separator
decimal_sep string "." Decimal separator
precision int 0 Decimal places
multiplier number 1 Multiply before display
absolute boolean False Display absolute value (no sign)

Date Formatter

Parameter Type Default Description
format string "%Y-%m-%d" strftime pattern

Boolean Formatter

Parameter Type Default Description
true_value string "true" Display for true/1
false_value string "false" Display for false/0
null_value string "" Display for null/None

Text Formatter

Parameter Type Default Description
transform string - "uppercase", "lowercase", "capitalize"
max_length int - Truncate if exceeded
ellipsis string "..." Suffix when truncated

Enum Formatter

Parameter Type Default Description
source object - Mapping or datagrid reference
default string "" Label for unknown values

Constant Formatter

Parameter Type Default Description
value string - The constant text to display

Formatter Presets

Preset Type Configuration
EUR number suffix: " €", thousands_sep: " ", decimal_sep: ",", precision: 2
USD number prefix: "$", thousands_sep: ",", decimal_sep: ".", precision: 2
percentage number suffix: "%", precision: 1, multiplier: 100
short_date date format: "%d/%m/%Y"
iso_date date format: "%Y-%m-%d"
yes_no boolean true_value: "Yes", false_value: "No"

Operators Reference

Comparison Operators

Operator Description Value Type Example
== Equal Any value == 0
!= Not equal Any value != ""
< Less than Number, Date value < 0
<= Less or equal Number, Date value <= 100
> Greater than Number, Date value > 1000
>= Greater or equal Number, Date value >= 0

String Operators

Operator Description Value Type Example
contains String contains String value contains "error"
startswith String starts with String value startswith "ERR"
endswith String ends with String value endswith ".pdf"

Note: String operators are case-insensitive by default. Use (case) modifier for case-sensitive matching.

List Operators

Operator Description Value Type Example
in Value in list List value in ["A", "B", "C"]
between Value in range List [min, max] value between 0 and 100

Unary Operators

Operator Description Example
isempty Is null or empty string value isempty
isnotempty Is not null or empty value isnotempty
isnan Is NaN (Not a Number) value isnan

DSL Grammar (EBNF)

Formal syntax reference for the DSL:

// Top-level structure
program         : scope+

// Scopes
scope           : scope_header NEWLINE INDENT rule+ DEDENT
scope_header    : column_scope | row_scope | cell_scope | table_scope | tables_scope
column_scope    : "column" column_name ":"
row_scope       : "row" INTEGER ":"
cell_scope      : "cell" cell_ref ":"
table_scope     : "table" QUOTED_STRING ":"
tables_scope    : "tables" ":"
column_name     : NAME | QUOTED_STRING
cell_ref        : "(" column_name "," INTEGER ")" | CELL_ID

// Rules
rule            : (style_expr format_expr? | format_expr style_expr?) condition? NEWLINE
condition       : "if" comparison

// Comparisons
comparison      : "not"? (binary_comp | unary_comp) case_modifier?
binary_comp     : operand operator operand
                | operand "in" list
                | operand "between" operand "and" operand
unary_comp      : operand ("isempty" | "isnotempty" | "isnan")
case_modifier   : "(" "case" ")"

// Operators
operator        : "==" | "!=" | "<" | "<=" | ">" | ">="
                | "contains" | "startswith" | "endswith"

// Operands
operand         : value_ref | column_ref | row_ref | cell_ref_expr | literal | arithmetic
value_ref       : "value"
column_ref      : "col." (NAME | QUOTED_STRING)
row_ref         : "row." INTEGER
cell_ref_expr   : "cell." NAME "-" INTEGER
literal         : STRING | NUMBER | BOOLEAN
arithmetic      : operand ("*" | "/" | "+" | "-") operand
list            : "[" (literal ("," literal)*)? "]"

// Style expression
style_expr      : "style" "(" style_args ")"
style_args      : (QUOTED_STRING ("," style_kwargs)?) | style_kwargs
style_kwargs    : style_kwarg ("," style_kwarg)*
style_kwarg     : NAME "=" (QUOTED_STRING | BOOLEAN | NUMBER)

// Format expression
format_expr     : format_preset | format_typed
format_preset   : "format" "(" QUOTED_STRING ("," format_kwargs)? ")"
format_typed    : "format" "." FORMAT_TYPE "(" format_kwargs? ")"
format_kwargs   : format_kwarg ("," format_kwarg)*
format_kwarg    : NAME "=" (QUOTED_STRING | BOOLEAN | NUMBER | dict)
dict            : "{" (dict_entry ("," dict_entry)*)? "}"
dict_entry      : QUOTED_STRING ":" QUOTED_STRING

// Tokens
FORMAT_TYPE     : "number" | "date" | "boolean" | "text" | "enum" | "constant"
NAME            : /[a-zA-Z_][a-zA-Z0-9_]*/
QUOTED_STRING   : /"[^"]*"/ | /'[^']*'/
INTEGER         : /[0-9]+/
NUMBER          : /[0-9]+(\.[0-9]+)?/
BOOLEAN         : "True" | "False" | "true" | "false"
CELL_ID         : /tcell_[a-zA-Z0-9_-]+/
NEWLINE         : /\n/
COMMENT         : /#.*/

End of User Guide

For technical details about the implementation, see the developer documentation.