Clean version before implementing formatting

This commit is contained in:
2026-01-26 19:09:15 +01:00
parent 05d4e5cd89
commit 3083f3b1fd
6 changed files with 486 additions and 10 deletions

480
docs/DataGrid Formatting.md Normal file
View File

@@ -0,0 +1,480 @@
# DataGrid Formatting
## Overview
This document describes the formatting capabilities for the DataGrid component.
**Formatting applies at three levels:**
| Level | Cells Targeted | Condition Evaluated On |
|------------|-------------------------|----------------------------------------------|
| **Cell** | 1 specific cell | The cell value |
| **Row** | All cells in the row | Each cell value (or fixed column with `col`) |
| **Column** | All cells in the column | Each cell value (or fixed row with `row`) |
---
## 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 |
|------------------|------------------------|---------|----------|---------------------------------------------|
| `operator` | string | - | Yes | Comparison operator |
| `value` | scalar / list / object | - | Depends | Value to compare against |
| `not` | bool | `false` | No | Inverts the condition result |
| `case_sensitive` | bool | `false` | No | Case-sensitive string comparison |
| `col` | string | - | No | Reference column (for row-level conditions) |
| `row` | int | - | No | Reference row (for column-level conditions) |
### Operators
| 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
### 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
### `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:**
```json
{
"type": "enum",
"source": {
"type": "mapping",
"value": {
"draft": "Brouillon",
"pending": "En attente",
"approved": "Approuvé"
}
},
"default": "Inconnu"
}
```
**From another DataGrid:**
```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
### Format Storage Location
| Level | Storage | Key |
|------------|------------------------------|---------|
| **Column** | `DataGridColumnState.format` | - |
| **Row** | `DataGridRowState.format` | - |
| **Cell** | `DatagridState.cell_formats` | Cell ID |
### Cell ID Format
```
tcell_{datagrid_id}-{row_index}-{col_index}
```
---
## DataGridsManager
Global settings stored in `DataGridsManager`:
| Property | Type | Description |
|---------------------|--------|-------------------------------------------|
| `style_presets` | dict | Style presets (primary, success, etc.) |
| `formatter_presets` | dict | Formatter presets (EUR, percentage, etc.) |
| `default_locale` | string | Default locale for number/date formatting |
---
## Future Considerations
- **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