Removed deprecated doc
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1,551 +0,0 @@
|
|||||||
# DataGrid Formatting
|
|
||||||
|
|
||||||
## Implementation Status
|
|
||||||
|
|
||||||
| Component | Status | Location |
|
|
||||||
|-----------|--------|----------|
|
|
||||||
| **Core Module** | | `src/myfasthtml/core/formatting/` |
|
|
||||||
| Dataclasses (Condition, Style, Formatter, FormatRule) | :white_check_mark: Implemented | `dataclasses.py` |
|
|
||||||
| Style Presets (DaisyUI 5) | :white_check_mark: Implemented | `presets.py` |
|
|
||||||
| Formatter Presets (EUR, USD, etc.) | :white_check_mark: Implemented | `presets.py` |
|
|
||||||
| ConditionEvaluator (12 operators) | :white_check_mark: Implemented | `condition_evaluator.py` |
|
|
||||||
| StyleResolver | :white_check_mark: Implemented | `style_resolver.py` |
|
|
||||||
| FormatterResolver (Number, Date, Boolean, Text, Enum) | :white_check_mark: Implemented | `formatter_resolver.py` |
|
|
||||||
| FormattingEngine (facade + conflict resolution) | :white_check_mark: Implemented | `engine.py` |
|
|
||||||
| **Condition Features** | | |
|
|
||||||
| `col` parameter (row-level conditions) | :white_check_mark: Implemented | |
|
|
||||||
| `row` parameter (column-level conditions) | :x: Not implemented | |
|
|
||||||
| Column reference in value `{"col": "..."}` | :white_check_mark: Implemented | |
|
|
||||||
| **Scope Levels** | | |
|
|
||||||
| Cell scope | :white_check_mark: Implemented | |
|
|
||||||
| Row scope | :white_check_mark: Implemented | |
|
|
||||||
| Column scope | :white_check_mark: Implemented | |
|
|
||||||
| Table scope | :o: To implement | |
|
|
||||||
| Tables scope (global) | :o: To implement | |
|
|
||||||
| **DataGrid Integration** | | |
|
|
||||||
| Integration in `mk_body_cell_content()` | :white_check_mark: Implemented | `DataGrid.py` |
|
|
||||||
| DataGridFormattingEditor | :white_check_mark: Implemented | `DataGridFormattingEditor.py` |
|
|
||||||
| DataGridsManager (global presets) | :white_check_mark: Implemented | `DataGridsManager.py` |
|
|
||||||
| **Tests** | | `tests/core/formatting/` |
|
|
||||||
| test_condition_evaluator.py | :white_check_mark: ~45 test cases | |
|
|
||||||
| test_style_resolver.py | :white_check_mark: ~12 test cases | |
|
|
||||||
| test_formatter_resolver.py | :white_check_mark: ~40 test cases | |
|
|
||||||
| test_engine.py | :white_check_mark: ~18 test cases | |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
This document describes the formatting capabilities for the DataGrid component.
|
|
||||||
|
|
||||||
**Formatting applies at five levels:**
|
|
||||||
|
|
||||||
| 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 (or fixed column with `col`) | High (2) |
|
|
||||||
| **Column** | All cells in the column | Each cell value (or fixed row with `row`) | 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) |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Format Rule Structure
|
|
||||||
|
|
||||||
A format is a **list** of rules. Each rule is an object:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"condition": {},
|
|
||||||
"style": {},
|
|
||||||
"formatter": {}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Rules:**
|
|
||||||
|
|
||||||
- `style` and `formatter` can appear alone (unconditional formatting)
|
|
||||||
- `condition` **cannot** appear alone - must be paired with `style` and/or `formatter`
|
|
||||||
- If `condition` is present, the `style`/`formatter` is applied **only if** the condition is met
|
|
||||||
- Rules are evaluated in order; multiple rules can match
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Conflict Resolution
|
|
||||||
|
|
||||||
When multiple rules match the same cell:
|
|
||||||
|
|
||||||
1. **Specificity** = number of conditions in the rule
|
|
||||||
2. **Higher specificity wins**
|
|
||||||
3. **At equal specificity, last rule wins entirely** (no fusion)
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"style": {
|
|
||||||
"color": "gray"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"condition": {
|
|
||||||
"operator": "<",
|
|
||||||
"value": 0
|
|
||||||
},
|
|
||||||
"style": {
|
|
||||||
"color": "red"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"condition": {
|
|
||||||
"operator": "==",
|
|
||||||
"value": -5
|
|
||||||
},
|
|
||||||
"style": {
|
|
||||||
"color": "black"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
For `value = -5`: Rule 3 wins (same specificity as rule 2, but defined later).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Condition Structure
|
|
||||||
|
|
||||||
### Fields
|
|
||||||
|
|
||||||
| Field | Type | Default | Required | Description | Status |
|
|
||||||
|------------------|------------------------|---------|----------|---------------------------------------------|--------|
|
|
||||||
| `operator` | string | - | Yes | Comparison operator | :white_check_mark: |
|
|
||||||
| `value` | scalar / list / object | - | Depends | Value to compare against | :white_check_mark: |
|
|
||||||
| `not` | bool | `false` | No | Inverts the condition result | :white_check_mark: (as `negate`) |
|
|
||||||
| `case_sensitive` | bool | `false` | No | Case-sensitive string comparison | :white_check_mark: |
|
|
||||||
| `col` | string | - | No | Reference column (for row-level conditions) | :white_check_mark: |
|
|
||||||
| `row` | int | - | No | Reference row (for column-level conditions) | :x: Not implemented |
|
|
||||||
|
|
||||||
### Operators
|
|
||||||
|
|
||||||
All operators are :white_check_mark: **implemented**.
|
|
||||||
|
|
||||||
| Operator | Description | Value Required |
|
|
||||||
|--------------|--------------------------|------------------|
|
|
||||||
| `==` | Equal | Yes |
|
|
||||||
| `!=` | Not equal | Yes |
|
|
||||||
| `<` | Less than | Yes |
|
|
||||||
| `<=` | Less than or equal | Yes |
|
|
||||||
| `>` | Greater than | Yes |
|
|
||||||
| `>=` | Greater than or equal | Yes |
|
|
||||||
| `contains` | String contains | Yes |
|
|
||||||
| `startswith` | String starts with | Yes |
|
|
||||||
| `endswith` | String ends with | Yes |
|
|
||||||
| `in` | Value in list | Yes (list) |
|
|
||||||
| `between` | Value between two values | Yes ([min, max]) |
|
|
||||||
| `isempty` | Value is empty/null | No |
|
|
||||||
| `isnotempty` | Value is not empty/null | No |
|
|
||||||
|
|
||||||
### Value Types
|
|
||||||
|
|
||||||
**Literal value:**
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"operator": "<",
|
|
||||||
"value": 0
|
|
||||||
}
|
|
||||||
{
|
|
||||||
"operator": "in",
|
|
||||||
"value": [
|
|
||||||
"A",
|
|
||||||
"B",
|
|
||||||
"C"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Cell reference (compare with another column):**
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"operator": ">",
|
|
||||||
"value": {
|
|
||||||
"col": "budget"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Negation
|
|
||||||
|
|
||||||
Use the `not` flag instead of separate operators:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"operator": "in",
|
|
||||||
"value": [
|
|
||||||
"A",
|
|
||||||
"B"
|
|
||||||
],
|
|
||||||
"not": true
|
|
||||||
}
|
|
||||||
{
|
|
||||||
"operator": "contains",
|
|
||||||
"value": "error",
|
|
||||||
"not": true
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Case Sensitivity
|
|
||||||
|
|
||||||
String comparisons are **case-insensitive by default**.
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"operator": "==",
|
|
||||||
"value": "Error",
|
|
||||||
"case_sensitive": true
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Evaluation Behavior
|
|
||||||
|
|
||||||
| Situation | Behavior |
|
|
||||||
|---------------------------|-----------------------------------|
|
|
||||||
| Cell value is `null` | Condition = `false` |
|
|
||||||
| Referenced cell is `null` | Condition = `false` |
|
|
||||||
| Type mismatch | Condition = `false` (no coercion) |
|
|
||||||
| String operators | Converts value to string first |
|
|
||||||
|
|
||||||
### Examples
|
|
||||||
|
|
||||||
```json
|
|
||||||
// Row-level: highlight if "status" column == "error"
|
|
||||||
{
|
|
||||||
"col": "status",
|
|
||||||
"operator": "==",
|
|
||||||
"value": "error"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Column-level: bold if row 0 has value "Total"
|
|
||||||
{
|
|
||||||
"row": 0,
|
|
||||||
"operator": "==",
|
|
||||||
"value": "Total"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compare with another column
|
|
||||||
{
|
|
||||||
"operator": ">",
|
|
||||||
"value": {
|
|
||||||
"col": "budget"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Negated condition
|
|
||||||
{
|
|
||||||
"operator": "in",
|
|
||||||
"value": [
|
|
||||||
"draft",
|
|
||||||
"pending"
|
|
||||||
],
|
|
||||||
"not": true
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Style Structure
|
|
||||||
|
|
||||||
:white_check_mark: **Fully implemented** in `style_resolver.py`
|
|
||||||
|
|
||||||
### Fields
|
|
||||||
|
|
||||||
| Field | Type | Default | Description |
|
|
||||||
|--------------------|--------|------------|---------------------------------------------------|
|
|
||||||
| `preset` | string | - | Preset name (applied first, can be overridden) |
|
|
||||||
| `background_color` | string | - | Background color (hex, CSS name, or CSS variable) |
|
|
||||||
| `color` | string | - | Text color |
|
|
||||||
| `font_weight` | string | `"normal"` | `"normal"` or `"bold"` |
|
|
||||||
| `font_style` | string | `"normal"` | `"normal"` or `"italic"` |
|
|
||||||
| `font_size` | string | - | Font size (`"12px"`, `"0.9em"`) |
|
|
||||||
| `text_decoration` | string | `"none"` | `"none"`, `"underline"`, `"line-through"` |
|
|
||||||
|
|
||||||
### Example
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"style": {
|
|
||||||
"preset": "success",
|
|
||||||
"font_weight": "bold"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Default Presets (DaisyUI 5)
|
|
||||||
|
|
||||||
| Preset | Background | Text |
|
|
||||||
|-------------|--------------------------|----------------------------------|
|
|
||||||
| `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)` |
|
|
||||||
|
|
||||||
All presets default to `font_weight: "normal"`, `font_style: "normal"`, `text_decoration: "none"`.
|
|
||||||
|
|
||||||
### Resolution Logic
|
|
||||||
|
|
||||||
1. If `preset` is specified, apply all preset properties
|
|
||||||
2. Override with any explicit properties
|
|
||||||
|
|
||||||
**No style fusion:** When multiple rules match, the winning rule's style applies entirely.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Formatter Structure
|
|
||||||
|
|
||||||
Formatters transform cell values for display without changing the underlying data.
|
|
||||||
|
|
||||||
### Usage
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"formatter": {
|
|
||||||
"preset": "EUR"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
"formatter": {
|
|
||||||
"preset": "EUR",
|
|
||||||
"precision": 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Error Handling
|
|
||||||
|
|
||||||
If formatting fails (e.g., non-numeric value for `number` formatter), display `"⚠"`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Formatter Types
|
|
||||||
|
|
||||||
All formatter types are :white_check_mark: **implemented** in `formatter_resolver.py`.
|
|
||||||
|
|
||||||
### `number`
|
|
||||||
|
|
||||||
For numbers, currencies, and percentages.
|
|
||||||
|
|
||||||
| Property | 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` | Number of decimal places |
|
|
||||||
| `multiplier` | number | `1` | Multiply value before display |
|
|
||||||
|
|
||||||
### `date`
|
|
||||||
|
|
||||||
For dates and datetimes.
|
|
||||||
|
|
||||||
| Property | Type | Default | Description |
|
|
||||||
|----------|--------|--------------|-------------------------|
|
|
||||||
| `format` | string | `"%Y-%m-%d"` | strftime format pattern |
|
|
||||||
|
|
||||||
### `boolean`
|
|
||||||
|
|
||||||
For true/false values.
|
|
||||||
|
|
||||||
| Property | Type | Default | Description |
|
|
||||||
|---------------|--------|-----------|-------------------|
|
|
||||||
| `true_value` | string | `"true"` | Display for true |
|
|
||||||
| `false_value` | string | `"false"` | Display for false |
|
|
||||||
| `null_value` | string | `""` | Display for null |
|
|
||||||
|
|
||||||
### `text`
|
|
||||||
|
|
||||||
For text transformations.
|
|
||||||
|
|
||||||
| Property | Type | Default | Description |
|
|
||||||
|--------------|--------|---------|----------------------------------------------|
|
|
||||||
| `transform` | string | - | `"uppercase"`, `"lowercase"`, `"capitalize"` |
|
|
||||||
| `max_length` | int | - | Truncate if exceeded |
|
|
||||||
| `ellipsis` | string | `"..."` | Suffix when truncated |
|
|
||||||
|
|
||||||
### `enum`
|
|
||||||
|
|
||||||
For mapping values to display labels. Also used for Select dropdowns.
|
|
||||||
|
|
||||||
| Property | Type | Default | Description |
|
|
||||||
|---------------|--------|------------------|------------------------------------|
|
|
||||||
| `source` | object | - | Data source (see below) |
|
|
||||||
| `default` | string | `""` | Label for unknown values |
|
|
||||||
| `allow_empty` | bool | `true` | Show empty option in Select |
|
|
||||||
| `empty_label` | string | `"-- Select --"` | Label for empty option |
|
|
||||||
| `order_by` | string | `"source"` | `"source"`, `"display"`, `"value"` |
|
|
||||||
|
|
||||||
#### Source Types
|
|
||||||
|
|
||||||
**Static mapping:** :white_check_mark: Implemented
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"type": "enum",
|
|
||||||
"source": {
|
|
||||||
"type": "mapping",
|
|
||||||
"value": {
|
|
||||||
"draft": "Brouillon",
|
|
||||||
"pending": "En attente",
|
|
||||||
"approved": "Approuvé"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"default": "Inconnu"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**From another DataGrid:** :white_check_mark: Implemented (requires `lookup_resolver` injection)
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"type": "enum",
|
|
||||||
"source": {
|
|
||||||
"type": "datagrid",
|
|
||||||
"value": "categories_grid",
|
|
||||||
"value_column": "id",
|
|
||||||
"display_column": "name"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Empty Value Behavior
|
|
||||||
|
|
||||||
- `allow_empty: true` → Empty option displayed with `empty_label`
|
|
||||||
- `allow_empty: false` → First entry selected by default
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Default Formatter Presets
|
|
||||||
|
|
||||||
```python
|
|
||||||
formatter_presets = {
|
|
||||||
"EUR": {
|
|
||||||
"type": "number",
|
|
||||||
"suffix": " €",
|
|
||||||
"thousands_sep": " ",
|
|
||||||
"decimal_sep": ",",
|
|
||||||
"precision": 2
|
|
||||||
},
|
|
||||||
"USD": {
|
|
||||||
"type": "number",
|
|
||||||
"prefix": "$",
|
|
||||||
"thousands_sep": ",",
|
|
||||||
"decimal_sep": ".",
|
|
||||||
"precision": 2
|
|
||||||
},
|
|
||||||
"percentage": {
|
|
||||||
"type": "number",
|
|
||||||
"suffix": "%",
|
|
||||||
"precision": 1,
|
|
||||||
"multiplier": 100
|
|
||||||
},
|
|
||||||
"short_date": {
|
|
||||||
"type": "date",
|
|
||||||
"format": "%d/%m/%Y"
|
|
||||||
},
|
|
||||||
"iso_date": {
|
|
||||||
"type": "date",
|
|
||||||
"format": "%Y-%m-%d"
|
|
||||||
},
|
|
||||||
"yes_no": {
|
|
||||||
"type": "boolean",
|
|
||||||
"true_value": "Yes",
|
|
||||||
"false_value": "No"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Storage Architecture
|
|
||||||
|
|
||||||
:warning: **Structures exist but integration with formatting engine not implemented**
|
|
||||||
|
|
||||||
### Format Storage Location
|
|
||||||
|
|
||||||
| Level | Storage | Key | Status |
|
|
||||||
|------------|--------------------------------|---------|--------|
|
|
||||||
| **Cell** | `DatagridState.cell_formats` | Cell ID | Structure exists |
|
|
||||||
| **Row** | `DataGridRowState.format` | - | Structure exists |
|
|
||||||
| **Column** | `DataGridColumnState.format` | - | Structure exists |
|
|
||||||
| **Table** | `DatagridState.table_format` | - | :o: To implement |
|
|
||||||
| **Tables** | `DataGridsManager.all_tables_formats` | - | :o: To implement |
|
|
||||||
|
|
||||||
### Cell ID Format
|
|
||||||
|
|
||||||
```
|
|
||||||
tcell_{datagrid_id}-{row_index}-{col_index}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## DataGridsManager
|
|
||||||
|
|
||||||
:white_check_mark: **Implemented** in `src/myfasthtml/controls/DataGridsManager.py`
|
|
||||||
|
|
||||||
Global presets stored as instance attributes:
|
|
||||||
|
|
||||||
| Property | Type | Description | Status |
|
|
||||||
|---------------------|--------|-------------------------------------------|--------|
|
|
||||||
| `style_presets` | dict | Style presets (primary, success, etc.) | :white_check_mark: |
|
|
||||||
| `formatter_presets` | dict | Formatter presets (EUR, percentage, etc.) | :white_check_mark: |
|
|
||||||
| `default_locale` | string | Default locale for number/date formatting | :x: Not implemented |
|
|
||||||
|
|
||||||
**Methods:**
|
|
||||||
|
|
||||||
| Method | Description |
|
|
||||||
|--------|-------------|
|
|
||||||
| `get_style_presets()` | Get the global style presets |
|
|
||||||
| `get_formatter_presets()` | Get the global formatter presets |
|
|
||||||
| `add_style_preset(name, preset)` | Add or update a style preset |
|
|
||||||
| `add_formatter_preset(name, preset)` | Add or update a formatter preset |
|
|
||||||
| `remove_style_preset(name)` | Remove a style preset |
|
|
||||||
| `remove_formatter_preset(name)` | Remove a formatter preset |
|
|
||||||
|
|
||||||
**Usage:**
|
|
||||||
|
|
||||||
```python
|
|
||||||
# Add custom presets
|
|
||||||
manager.add_style_preset("highlight", {"background-color": "yellow", "color": "black"})
|
|
||||||
manager.add_formatter_preset("CHF", {"type": "number", "prefix": "CHF ", "precision": 2})
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Future Considerations
|
|
||||||
|
|
||||||
All items below are :x: **not implemented**.
|
|
||||||
|
|
||||||
- **`row` parameter for column-level conditions**: Evaluate condition on a specific row
|
|
||||||
- **AND/OR conditions**: Add explicit `and`/`or` operators if `between`/`in` prove insufficient
|
|
||||||
- **Cell references**: Extend to `{"col": "x", "row": 0}` for specific cell and `{"col": "x", "row_offset": -1}` for
|
|
||||||
relative references
|
|
||||||
- **Enum cascade (draft)**: Dependent dropdowns with `depends_on` and `filter_column`
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"source": {
|
|
||||||
"type": "datagrid",
|
|
||||||
"value": "cities_grid",
|
|
||||||
"value_column": "id",
|
|
||||||
"display_column": "name",
|
|
||||||
"filter_column": "country_id"
|
|
||||||
},
|
|
||||||
"depends_on": "country"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
- **API source for enum**: `{"type": "api", "value": "https://...", ...}`
|
|
||||||
- **Searchable enum**: For large option lists
|
|
||||||
- **Formatter chaining**: Apply multiple formatters in sequence
|
|
||||||
- ~~**DataGrid integration**: Connect `FormattingEngine` to `DataGrid.mk_body_cell_content()`~~ Done
|
|
||||||
Reference in New Issue
Block a user