Working in DataGridFormattingManager
This commit is contained in:
13
src/app.py
13
src/app.py
@@ -6,6 +6,7 @@ from fasthtml import serve
|
|||||||
from fasthtml.components import Div
|
from fasthtml.components import Div
|
||||||
|
|
||||||
from myfasthtml.controls.CommandsDebugger import CommandsDebugger
|
from myfasthtml.controls.CommandsDebugger import CommandsDebugger
|
||||||
|
from myfasthtml.controls.DataGridFormattingManager import DataGridFormattingManager
|
||||||
from myfasthtml.controls.DataGridsManager import DataGridsManager
|
from myfasthtml.controls.DataGridsManager import DataGridsManager
|
||||||
from myfasthtml.controls.Dropdown import Dropdown
|
from myfasthtml.controls.Dropdown import Dropdown
|
||||||
from myfasthtml.controls.FileUpload import FileUpload
|
from myfasthtml.controls.FileUpload import FileUpload
|
||||||
@@ -18,7 +19,7 @@ from myfasthtml.core.dbengine_utils import DataFrameHandler
|
|||||||
from myfasthtml.core.instances import UniqueInstance
|
from myfasthtml.core.instances import UniqueInstance
|
||||||
from myfasthtml.icons.carbon import volume_object_storage
|
from myfasthtml.icons.carbon import volume_object_storage
|
||||||
from myfasthtml.icons.fluent_p2 import key_command16_regular
|
from myfasthtml.icons.fluent_p2 import key_command16_regular
|
||||||
from myfasthtml.icons.fluent_p3 import folder_open20_regular
|
from myfasthtml.icons.fluent_p3 import folder_open20_regular, text_edit_style20_regular
|
||||||
from myfasthtml.myfastapp import create_app
|
from myfasthtml.myfastapp import create_app
|
||||||
|
|
||||||
with open('logging.yaml', 'r') as f:
|
with open('logging.yaml', 'r') as f:
|
||||||
@@ -74,6 +75,15 @@ def index(session):
|
|||||||
layout.header_right.add(btn_show_right_drawer)
|
layout.header_right.add(btn_show_right_drawer)
|
||||||
layout.left_drawer.add(btn_show_instances_debugger, "Debugger")
|
layout.left_drawer.add(btn_show_instances_debugger, "Debugger")
|
||||||
layout.left_drawer.add(btn_show_commands_debugger, "Debugger")
|
layout.left_drawer.add(btn_show_commands_debugger, "Debugger")
|
||||||
|
|
||||||
|
# Parameters
|
||||||
|
formatting_manager = DataGridFormattingManager(layout)
|
||||||
|
btn_show_formatting_manager = mk.label("Formatting",
|
||||||
|
icon=text_edit_style20_regular,
|
||||||
|
command=add_tab("Formatting", formatting_manager),
|
||||||
|
id=formatting_manager.get_id())
|
||||||
|
layout.left_drawer.add(btn_show_formatting_manager, "Parameters")
|
||||||
|
|
||||||
layout.left_drawer.add(btn_file_upload, "Test")
|
layout.left_drawer.add(btn_file_upload, "Test")
|
||||||
layout.left_drawer.add(btn_popup, "Test")
|
layout.left_drawer.add(btn_popup, "Test")
|
||||||
|
|
||||||
@@ -83,6 +93,7 @@ def index(session):
|
|||||||
dgs_manager.mk_main_icons(),
|
dgs_manager.mk_main_icons(),
|
||||||
cls="mf-layout-group flex gap-3"))
|
cls="mf-layout-group flex gap-3"))
|
||||||
layout.left_drawer.add(dgs_manager, "Documents")
|
layout.left_drawer.add(dgs_manager, "Documents")
|
||||||
|
|
||||||
layout.set_main(tabs_manager)
|
layout.set_main(tabs_manager)
|
||||||
|
|
||||||
# keyboard shortcuts
|
# keyboard shortcuts
|
||||||
|
|||||||
391
src/myfasthtml/controls/DataGridFormattingManager.py
Normal file
391
src/myfasthtml/controls/DataGridFormattingManager.py
Normal file
@@ -0,0 +1,391 @@
|
|||||||
|
import logging
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from fasthtml.common import Form, Fieldset, Label, Input, Span
|
||||||
|
from fasthtml.components import Div
|
||||||
|
|
||||||
|
from myfasthtml.controls.BaseCommands import BaseCommands
|
||||||
|
from myfasthtml.controls.DslEditor import DslEditor, DslEditorConf
|
||||||
|
from myfasthtml.controls.IconsHelper import IconsHelper
|
||||||
|
from myfasthtml.controls.Menu import Menu, MenuConf
|
||||||
|
from myfasthtml.controls.Panel import Panel, PanelConf
|
||||||
|
from myfasthtml.controls.Search import Search
|
||||||
|
from myfasthtml.controls.helpers import mk
|
||||||
|
from myfasthtml.core.commands import Command
|
||||||
|
from myfasthtml.core.dbmanager import DbObject
|
||||||
|
from myfasthtml.core.formatting.dataclasses import RulePreset
|
||||||
|
from myfasthtml.core.formatting.dsl import parse_dsl
|
||||||
|
from myfasthtml.core.formatting.dsl.definition import FormattingDSL
|
||||||
|
from myfasthtml.core.formatting.presets import DEFAULT_RULE_PRESETS
|
||||||
|
from myfasthtml.core.instances import SingleInstance
|
||||||
|
|
||||||
|
logger = logging.getLogger("DataGridFormattingManager")
|
||||||
|
|
||||||
|
_DSL_PLACEHOLDER = 'tables:\n style("error") if value < 0\n style("success") if value > 0'
|
||||||
|
|
||||||
|
|
||||||
|
class DataGridFormattingManagerState(DbObject):
|
||||||
|
def __init__(self, owner):
|
||||||
|
with self.initializing():
|
||||||
|
super().__init__(owner)
|
||||||
|
self.presets: list = []
|
||||||
|
self.selected_name: Optional[str] = None
|
||||||
|
self.ns_mode: str = "view"
|
||||||
|
|
||||||
|
|
||||||
|
class Commands(BaseCommands):
|
||||||
|
def new_preset(self):
|
||||||
|
return Command(
|
||||||
|
"NewPreset",
|
||||||
|
"New preset",
|
||||||
|
self._owner,
|
||||||
|
self._owner.on_new_preset,
|
||||||
|
icon=IconsHelper.get("Add20Regular"),
|
||||||
|
).htmx(target=f"#{self._id}", swap="outerHTML")
|
||||||
|
|
||||||
|
def save_preset(self):
|
||||||
|
return Command(
|
||||||
|
"SavePreset",
|
||||||
|
"Save preset",
|
||||||
|
self._owner,
|
||||||
|
self._owner.on_save_preset,
|
||||||
|
icon=IconsHelper.get("Save20Regular"),
|
||||||
|
).htmx(target=f"#{self._id}", swap="outerHTML")
|
||||||
|
|
||||||
|
def rename_preset(self):
|
||||||
|
return Command(
|
||||||
|
"RenamePreset",
|
||||||
|
"Rename preset",
|
||||||
|
self._owner,
|
||||||
|
self._owner.on_rename_preset,
|
||||||
|
icon=IconsHelper.get("Rename20Regular"),
|
||||||
|
).htmx(target=f"#{self._id}", swap="outerHTML")
|
||||||
|
|
||||||
|
def delete_preset(self):
|
||||||
|
return Command(
|
||||||
|
"DeletePreset",
|
||||||
|
"Delete preset",
|
||||||
|
self._owner,
|
||||||
|
self._owner.on_delete_preset,
|
||||||
|
icon=IconsHelper.get("Delete20Regular"),
|
||||||
|
).htmx(target=f"#{self._id}", swap="outerHTML")
|
||||||
|
|
||||||
|
def confirm_new(self):
|
||||||
|
return Command(
|
||||||
|
"ConfirmNew",
|
||||||
|
"Confirm new preset",
|
||||||
|
self._owner,
|
||||||
|
self._owner.on_confirm_new,
|
||||||
|
).htmx(target=f"#{self._id}", swap="outerHTML")
|
||||||
|
|
||||||
|
def confirm_rename(self):
|
||||||
|
return Command(
|
||||||
|
"ConfirmRename",
|
||||||
|
"Confirm rename",
|
||||||
|
self._owner,
|
||||||
|
self._owner.on_confirm_rename,
|
||||||
|
).htmx(target=f"#{self._id}", swap="outerHTML")
|
||||||
|
|
||||||
|
def cancel(self):
|
||||||
|
return Command(
|
||||||
|
"Cancel",
|
||||||
|
"Cancel",
|
||||||
|
self._owner,
|
||||||
|
self._owner.on_cancel,
|
||||||
|
).htmx(target=f"#{self._id}", swap="outerHTML")
|
||||||
|
|
||||||
|
|
||||||
|
class DataGridFormattingManager(SingleInstance):
|
||||||
|
def __init__(self, parent, _id=None):
|
||||||
|
super().__init__(parent, _id=_id)
|
||||||
|
self._state = DataGridFormattingManagerState(self)
|
||||||
|
self.commands = Commands(self)
|
||||||
|
|
||||||
|
self._panel = Panel(
|
||||||
|
self,
|
||||||
|
conf=PanelConf(left=True, right=False, left_title="Presets"),
|
||||||
|
_id="-panel",
|
||||||
|
)
|
||||||
|
self._menu = Menu(
|
||||||
|
self,
|
||||||
|
conf=MenuConf(fixed_items=["NewPreset", "SavePreset", "RenamePreset", "DeletePreset"]),
|
||||||
|
save_state=False,
|
||||||
|
_id="-menu",
|
||||||
|
)
|
||||||
|
self._search = Search(
|
||||||
|
self,
|
||||||
|
items_names="Presets",
|
||||||
|
template=self._mk_preset_item,
|
||||||
|
_id="-search",
|
||||||
|
)
|
||||||
|
self._editor = DslEditor(
|
||||||
|
self,
|
||||||
|
dsl=FormattingDSL(),
|
||||||
|
conf=DslEditorConf(
|
||||||
|
save_button=False,
|
||||||
|
autocompletion=False,
|
||||||
|
linting=False,
|
||||||
|
placeholder=_DSL_PLACEHOLDER,
|
||||||
|
),
|
||||||
|
save_state=False,
|
||||||
|
_id="-editor",
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.debug(f"DataGridFormattingManager created with id={self._id}")
|
||||||
|
|
||||||
|
# === Helpers ===
|
||||||
|
|
||||||
|
def _get_all_presets(self) -> list:
|
||||||
|
"""Returns builtin presets followed by user presets."""
|
||||||
|
return list(DEFAULT_RULE_PRESETS.values()) + self._state.presets
|
||||||
|
|
||||||
|
def _is_builtin(self, name: str) -> bool:
|
||||||
|
return name in DEFAULT_RULE_PRESETS
|
||||||
|
|
||||||
|
def _get_selected_preset(self) -> Optional[RulePreset]:
|
||||||
|
if not self._state.selected_name:
|
||||||
|
return None
|
||||||
|
for p in self._get_all_presets():
|
||||||
|
if p.name == self._state.selected_name:
|
||||||
|
return p
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _get_user_preset(self, name: str) -> Optional[RulePreset]:
|
||||||
|
for p in self._state.presets:
|
||||||
|
if p.name == name:
|
||||||
|
return p
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _parse_dsl_to_rules(self, dsl_text: str) -> list:
|
||||||
|
"""Parse DSL text and extract FormatRule objects, ignoring scopes."""
|
||||||
|
try:
|
||||||
|
scoped_rules = parse_dsl(dsl_text)
|
||||||
|
return [sr.rule for sr in scoped_rules]
|
||||||
|
except Exception:
|
||||||
|
return []
|
||||||
|
|
||||||
|
# === Command handlers ===
|
||||||
|
|
||||||
|
def on_new_preset(self):
|
||||||
|
self._state.ns_mode = "new"
|
||||||
|
return self.render()
|
||||||
|
|
||||||
|
def on_save_preset(self):
|
||||||
|
if not self._state.selected_name or self._is_builtin(self._state.selected_name):
|
||||||
|
return self.render()
|
||||||
|
preset = self._get_user_preset(self._state.selected_name)
|
||||||
|
if preset is None:
|
||||||
|
return self.render()
|
||||||
|
dsl = self._editor.get_content()
|
||||||
|
preset.dsl = dsl
|
||||||
|
preset.rules = self._parse_dsl_to_rules(dsl)
|
||||||
|
logger.debug(f"Saved preset '{preset.name}' with {len(preset.rules)} rules")
|
||||||
|
return self.render()
|
||||||
|
|
||||||
|
def on_rename_preset(self):
|
||||||
|
if not self._state.selected_name or self._is_builtin(self._state.selected_name):
|
||||||
|
return self.render()
|
||||||
|
self._state.ns_mode = "rename"
|
||||||
|
return self.render()
|
||||||
|
|
||||||
|
def on_delete_preset(self):
|
||||||
|
if not self._state.selected_name or self._is_builtin(self._state.selected_name):
|
||||||
|
return self.render()
|
||||||
|
self._state.presets = [p for p in self._state.presets if p.name != self._state.selected_name]
|
||||||
|
self._state.selected_name = None
|
||||||
|
self._editor.set_content("")
|
||||||
|
self._editor.conf.readonly = False
|
||||||
|
logger.debug(f"Deleted preset '{self._state.selected_name}'")
|
||||||
|
return self.render()
|
||||||
|
|
||||||
|
def on_select_preset(self, name: str):
|
||||||
|
preset = None
|
||||||
|
for p in self._get_all_presets():
|
||||||
|
if p.name == name:
|
||||||
|
preset = p
|
||||||
|
break
|
||||||
|
if preset is None:
|
||||||
|
return self.render()
|
||||||
|
self._state.selected_name = name
|
||||||
|
self._state.ns_mode = "view"
|
||||||
|
self._editor.set_content(preset.dsl)
|
||||||
|
self._editor.conf.readonly = self._is_builtin(name)
|
||||||
|
logger.debug(f"Selected preset '{name}', readonly={self._editor.conf.readonly}")
|
||||||
|
return self.render()
|
||||||
|
|
||||||
|
def on_confirm_new(self, client_response):
|
||||||
|
name = (client_response.get("name") or "").strip()
|
||||||
|
description = (client_response.get("description") or "").strip()
|
||||||
|
|
||||||
|
if not name:
|
||||||
|
self._state.ns_mode = "view"
|
||||||
|
return self.render()
|
||||||
|
|
||||||
|
all_names = {p.name for p in self._get_all_presets()}
|
||||||
|
if name in all_names:
|
||||||
|
logger.debug(f"Cannot create preset '{name}': name already exists")
|
||||||
|
self._state.ns_mode = "view"
|
||||||
|
return self.render()
|
||||||
|
|
||||||
|
new_preset = RulePreset(name=name, description=description, rules=[], dsl="")
|
||||||
|
self._state.presets.append(new_preset)
|
||||||
|
self._state.selected_name = name
|
||||||
|
self._state.ns_mode = "view"
|
||||||
|
self._editor.set_content("")
|
||||||
|
self._editor.conf.readonly = False
|
||||||
|
logger.debug(f"Created preset '{name}'")
|
||||||
|
return self.render()
|
||||||
|
|
||||||
|
def on_confirm_rename(self, client_response):
|
||||||
|
name = (client_response.get("name") or "").strip()
|
||||||
|
description = (client_response.get("description") or "").strip()
|
||||||
|
|
||||||
|
preset = self._get_user_preset(self._state.selected_name)
|
||||||
|
if not name or preset is None:
|
||||||
|
self._state.ns_mode = "view"
|
||||||
|
return self.render()
|
||||||
|
|
||||||
|
if name != preset.name:
|
||||||
|
all_names = {p.name for p in self._get_all_presets()}
|
||||||
|
if name in all_names:
|
||||||
|
logger.debug(f"Cannot rename to '{name}': name already exists")
|
||||||
|
self._state.ns_mode = "view"
|
||||||
|
return self.render()
|
||||||
|
|
||||||
|
old_name = preset.name
|
||||||
|
preset.name = name
|
||||||
|
preset.description = description
|
||||||
|
self._state.selected_name = name
|
||||||
|
self._state.ns_mode = "view"
|
||||||
|
logger.debug(f"Renamed preset '{old_name}' → '{name}'")
|
||||||
|
return self.render()
|
||||||
|
|
||||||
|
def on_cancel(self):
|
||||||
|
self._state.ns_mode = "view"
|
||||||
|
return self.render()
|
||||||
|
|
||||||
|
# === Rendering ===
|
||||||
|
|
||||||
|
def _mk_preset_item(self, preset: RulePreset):
|
||||||
|
is_active = self._state.selected_name == preset.name
|
||||||
|
is_builtin = self._is_builtin(preset.name)
|
||||||
|
|
||||||
|
badges = []
|
||||||
|
if preset.has_formatter():
|
||||||
|
badges.append(Span("format()", cls="badge badge-xs badge-secondary"))
|
||||||
|
if preset.has_style():
|
||||||
|
badges.append(Span("style()", cls="badge badge-xs badge-primary"))
|
||||||
|
if is_builtin:
|
||||||
|
badges.append(Span("built-in", cls="badge badge-xs badge-ghost"))
|
||||||
|
|
||||||
|
item_cls = "mf-fmgr-preset-item"
|
||||||
|
if is_active:
|
||||||
|
item_cls += " mf-fmgr-preset-item-active"
|
||||||
|
|
||||||
|
select_cmd = Command(
|
||||||
|
"SelectPreset",
|
||||||
|
f"Select {preset.name}",
|
||||||
|
self,
|
||||||
|
self.on_select_preset,
|
||||||
|
args=[preset.name],
|
||||||
|
).htmx(target=f"#{self._id}", swap="outerHTML")
|
||||||
|
|
||||||
|
return mk.mk(
|
||||||
|
Div(
|
||||||
|
Div(preset.name, cls="mf-fmgr-preset-name"),
|
||||||
|
Div(preset.description, cls="mf-fmgr-preset-desc") if preset.description else None,
|
||||||
|
Div(*badges, cls="mf-fmgr-preset-badges") if badges else None,
|
||||||
|
cls=item_cls,
|
||||||
|
),
|
||||||
|
command=select_cmd,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _mk_preset_header(self, preset: RulePreset, is_builtin: bool):
|
||||||
|
badges = []
|
||||||
|
if preset.has_formatter():
|
||||||
|
badges.append(Span("format()", cls="badge badge-secondary"))
|
||||||
|
if preset.has_style():
|
||||||
|
badges.append(Span("style()", cls="badge badge-primary"))
|
||||||
|
if is_builtin:
|
||||||
|
badges.append(Span("built-in", cls="badge badge-ghost"))
|
||||||
|
|
||||||
|
return Div(
|
||||||
|
Div(
|
||||||
|
Div(preset.name, cls="text-sm font-semibold"),
|
||||||
|
Div(preset.description, cls="text-xs opacity-50") if preset.description else None,
|
||||||
|
),
|
||||||
|
Div(*badges, cls="flex gap-1") if badges else None,
|
||||||
|
cls="mf-fmgr-editor-meta",
|
||||||
|
)
|
||||||
|
|
||||||
|
def _mk_editor_view(self):
|
||||||
|
preset = self._get_selected_preset()
|
||||||
|
if preset is None:
|
||||||
|
return Div("Select a preset to edit", cls="mf-fmgr-placeholder p-4 text-sm opacity-50")
|
||||||
|
is_builtin = self._is_builtin(preset.name)
|
||||||
|
return Div(
|
||||||
|
self._mk_preset_header(preset, is_builtin),
|
||||||
|
self._editor,
|
||||||
|
cls="mf-fmgr-editor-view",
|
||||||
|
)
|
||||||
|
|
||||||
|
def _mk_new_form(self):
|
||||||
|
return Div(
|
||||||
|
Form(
|
||||||
|
Fieldset(
|
||||||
|
Label("Name"),
|
||||||
|
Input(name="name", cls="input input-sm w-full"),
|
||||||
|
Label("Description"),
|
||||||
|
Input(name="description", cls="input input-sm w-full"),
|
||||||
|
legend="New preset",
|
||||||
|
cls="fieldset border-base-300 rounded-box p-4",
|
||||||
|
),
|
||||||
|
mk.dialog_buttons(
|
||||||
|
on_ok=self.commands.confirm_new(),
|
||||||
|
on_cancel=self.commands.cancel(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
cls="mf-fmgr-form p-4",
|
||||||
|
)
|
||||||
|
|
||||||
|
def _mk_rename_form(self):
|
||||||
|
preset = self._get_selected_preset()
|
||||||
|
return Div(
|
||||||
|
Form(
|
||||||
|
Fieldset(
|
||||||
|
Label("Name"),
|
||||||
|
Input(name="name", value=preset.name if preset else "", cls="input input-sm w-full"),
|
||||||
|
Label("Description"),
|
||||||
|
Input(name="description", value=preset.description if preset else "", cls="input input-sm w-full"),
|
||||||
|
legend="Rename preset",
|
||||||
|
cls="fieldset border-base-300 rounded-box p-4",
|
||||||
|
),
|
||||||
|
mk.dialog_buttons(
|
||||||
|
on_ok=self.commands.confirm_rename(),
|
||||||
|
on_cancel=self.commands.cancel(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
cls="mf-fmgr-form p-4",
|
||||||
|
)
|
||||||
|
|
||||||
|
def _mk_main_content(self):
|
||||||
|
if self._state.ns_mode == "new":
|
||||||
|
return self._mk_new_form()
|
||||||
|
elif self._state.ns_mode == "rename":
|
||||||
|
return self._mk_rename_form()
|
||||||
|
return self._mk_editor_view()
|
||||||
|
|
||||||
|
def render(self):
|
||||||
|
self._search.set_items(self._get_all_presets())
|
||||||
|
self._panel._main = self._mk_main_content()
|
||||||
|
self._panel._left = self._search
|
||||||
|
|
||||||
|
return Div(
|
||||||
|
self._menu,
|
||||||
|
self._panel,
|
||||||
|
id=self._id,
|
||||||
|
cls="mf-formatting-manager",
|
||||||
|
)
|
||||||
|
|
||||||
|
def __ft__(self):
|
||||||
|
return self.render()
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
from fastcore.basics import NotStr
|
from fastcore.basics import NotStr
|
||||||
|
|
||||||
from myfasthtml.core.constants import ColumnType
|
from myfasthtml.core.constants import ColumnType
|
||||||
|
from myfasthtml.core.utils import pascal_to_snake
|
||||||
from myfasthtml.icons.fluent import question20_regular, brain_circuit20_regular, number_symbol20_regular, \
|
from myfasthtml.icons.fluent import question20_regular, brain_circuit20_regular, number_symbol20_regular, \
|
||||||
number_row20_regular
|
number_row20_regular
|
||||||
from myfasthtml.icons.fluent_p1 import checkbox_checked20_regular, checkbox_unchecked20_regular, \
|
from myfasthtml.icons.fluent_p1 import checkbox_checked20_regular, checkbox_unchecked20_regular, \
|
||||||
@@ -63,6 +64,9 @@ class IconsHelper:
|
|||||||
|
|
||||||
_UTILITY_MODULES = {'manage_icons', 'update_icons'}
|
_UTILITY_MODULES = {'manage_icons', 'update_icons'}
|
||||||
|
|
||||||
|
if name[0].isupper():
|
||||||
|
name = pascal_to_snake(name)
|
||||||
|
|
||||||
if package:
|
if package:
|
||||||
module = importlib.import_module(f"myfasthtml.icons.{package}")
|
module = importlib.import_module(f"myfasthtml.icons.{package}")
|
||||||
icon = getattr(module, name, None)
|
icon = getattr(module, name, None)
|
||||||
|
|||||||
@@ -190,6 +190,7 @@ class RulePreset:
|
|||||||
name: str
|
name: str
|
||||||
description: str
|
description: str
|
||||||
rules: list[FormatRule] = field(default_factory=list)
|
rules: list[FormatRule] = field(default_factory=list)
|
||||||
|
dsl: str = ""
|
||||||
|
|
||||||
def has_formatter(self) -> bool:
|
def has_formatter(self) -> bool:
|
||||||
"""Returns True if at least one rule defines a formatter."""
|
"""Returns True if at least one rule defines a formatter."""
|
||||||
|
|||||||
@@ -45,6 +45,9 @@ DEFAULT_RULE_PRESETS: dict[str, RulePreset] = {
|
|||||||
formatter=NumberFormatter(precision=0, thousands_sep=" "),
|
formatter=NumberFormatter(precision=0, thousands_sep=" "),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
dsl='tables:\n'
|
||||||
|
' format.number(precision=0, prefix="(", suffix=")", absolute=True, thousands_sep=" ") if value < 0\n'
|
||||||
|
' format.number(precision=0, thousands_sep=" ") if value > 0\n',
|
||||||
),
|
),
|
||||||
"traffic_light": RulePreset(
|
"traffic_light": RulePreset(
|
||||||
name="traffic_light",
|
name="traffic_light",
|
||||||
@@ -54,6 +57,10 @@ DEFAULT_RULE_PRESETS: dict[str, RulePreset] = {
|
|||||||
FormatRule(condition=Condition(operator="==", value=0), style=Style(preset="warning")),
|
FormatRule(condition=Condition(operator="==", value=0), style=Style(preset="warning")),
|
||||||
FormatRule(condition=Condition(operator=">", value=0), style=Style(preset="success")),
|
FormatRule(condition=Condition(operator=">", value=0), style=Style(preset="success")),
|
||||||
],
|
],
|
||||||
|
dsl='tables:\n'
|
||||||
|
' style("error") if value < 0\n'
|
||||||
|
' style("warning") if value == 0\n'
|
||||||
|
' style("success") if value > 0\n',
|
||||||
),
|
),
|
||||||
"budget_variance": RulePreset(
|
"budget_variance": RulePreset(
|
||||||
name="budget_variance",
|
name="budget_variance",
|
||||||
@@ -73,6 +80,10 @@ DEFAULT_RULE_PRESETS: dict[str, RulePreset] = {
|
|||||||
formatter=NumberFormatter(precision=1, suffix="%", multiplier=100),
|
formatter=NumberFormatter(precision=1, suffix="%", multiplier=100),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
dsl='tables:\n'
|
||||||
|
' format.number(precision=1, suffix="%", multiplier=100) style("error") if value < 0\n'
|
||||||
|
' format.number(precision=1, suffix="%", multiplier=100) style("warning") if value > 0.1\n'
|
||||||
|
' format.number(precision=1, suffix="%", multiplier=100)\n',
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user