Merge branch 'feature/columns_modal' into dev

This commit is contained in:
Colin Maudry
2026-01-31 01:34:20 +01:00
4 changed files with 181 additions and 13 deletions
+18 -3
View File
@@ -194,7 +194,7 @@ p.version > a {
margin: 12px 0 12px 0; margin: 12px 0 12px 0;
height: 50px; height: 50px;
display: flex; display: flex;
align-items: baseline; align-items: center;
} }
.table-menu > * { .table-menu > * {
@@ -240,7 +240,11 @@ table.cell-table th {
.dash-table-container .dash-table-container
.dash-spreadsheet-container .dash-spreadsheet-container
.dash-spreadsheet-inner .dash-spreadsheet-inner
th.dash-header { th.dash-header,
.dash-table-container
.dash-spreadsheet-container
.dash-spreadsheet-inner
th.dash-select-header {
margin: 0; margin: 0;
color: white; color: white;
font-family: "Inter", sans-serif; font-family: "Inter", sans-serif;
@@ -258,7 +262,13 @@ table.cell-table th {
.dash-spreadsheet-inner .dash-spreadsheet-inner
table.cell-table table.cell-table
tr:first-of-type tr:first-of-type
th.dash-header:first-of-type { th.dash-header:first-of-type,
.dash-table-container
.dash-spreadsheet-container
.dash-spreadsheet-inner
table.cell-table
tr:first-of-type
th.dash-select-header:first-of-type {
border-top-left-radius: 3px !important; border-top-left-radius: 3px !important;
} }
@@ -354,6 +364,11 @@ table.cell-table th {
right: 5px; right: 5px;
} }
#column_list .show-hide,
#table .show-hide {
display: none;
}
.show-hide-menu-item > input { .show-hide-menu-item > input {
margin-right: 10px; margin-right: 10px;
} }
+17 -4
View File
@@ -258,10 +258,12 @@ class DataTable(dash_table.DataTable):
page_action: Literal["native", "custom", "none"] = "native", page_action: Literal["native", "custom", "none"] = "native",
sort_action: Literal["native", "custom", "none"] = "native", sort_action: Literal["native", "custom", "none"] = "native",
filter_action: Literal["native", "custom", "none"] = "native", filter_action: Literal["native", "custom", "none"] = "native",
style_cell_conditional: list | None = None,
style_cell: dict | None = None,
**kwargs, **kwargs,
): ):
# Styles de base # Styles de base
style_cell_conditional = [ style_cell_conditional_common = [
{ {
"if": {"column_id": "objet"}, "if": {"column_id": "objet"},
"minWidth": "350px", "minWidth": "350px",
@@ -291,6 +293,8 @@ class DataTable(dash_table.DataTable):
}, },
] ]
style_cell_common = {"fontFamily": "Inter", "fontSize": "16px"}
for key in data_schema.keys(): for key in data_schema.keys():
field = data_schema[key] field = data_schema[key]
if field["type"] in ["number", "integer"]: if field["type"] in ["number", "integer"]:
@@ -299,7 +303,16 @@ class DataTable(dash_table.DataTable):
"textAlign": "right", "textAlign": "right",
# "fontFamily": "Fira Code", # "fontFamily": "Fira Code",
} }
style_cell_conditional.append(rule) style_cell_conditional_common.append(rule)
style_cell_conditional = (
style_cell_conditional or []
) + style_cell_conditional_common
if style_cell:
style_cell.update(style_cell_common)
else:
style_cell = style_cell_common
style_header = style_cell
# Initialisation de la classe parente avec les arguments # Initialisation de la classe parente avec les arguments
super().__init__( super().__init__(
@@ -322,8 +335,8 @@ class DataTable(dash_table.DataTable):
style_cell_conditional=style_cell_conditional, style_cell_conditional=style_cell_conditional,
data_timestamp=0, data_timestamp=0,
markdown_options={"html": True}, markdown_options={"html": True},
style_header={"fontFamily": "Inter", "fontSize": "16px"}, style_header=style_header,
style_cell={"fontFamily": "Inter", "fontSize": "16px"}, style_cell=style_cell,
tooltip_duration=8000, tooltip_duration=8000,
tooltip_delay=350, tooltip_delay=350,
hidden_columns=hidden_columns, hidden_columns=hidden_columns,
+143 -5
View File
@@ -21,6 +21,8 @@ from dash import (
from src.figures import DataTable from src.figures import DataTable
from src.utils import ( from src.utils import (
columns,
data_schema,
df, df,
filter_table_data, filter_table_data,
get_default_hidden_columns, get_default_hidden_columns,
@@ -61,9 +63,70 @@ datatable = html.Div(
), ),
) )
def make_tableau_columns_table():
table_data = []
table_columns = [
{
"id": col,
"name": data_schema[col]["title"],
"description": data_schema[col]["description"],
}
for col in df.columns
]
for column in table_columns:
new_column = {
"id": column["id"],
"name": column["name"],
"description": data_schema[column["id"]]["description"],
}
table_data.append(new_column)
table = (
DataTable(
row_selectable="multi",
data=table_data,
filter_action="none",
sort_action="none",
style_cell={
"textAlign": "left",
},
columns=[
{
"name": "Nom",
"id": "name",
},
{
"name": "Description",
"id": "description",
},
{
"name": "column_id",
"id": "id",
},
],
hidden_columns=["id"],
style_cell_conditional=[
{
"if": {"column_id": "description"},
"minWidth": "450px",
"overflow": "hidden",
"lineHeight": "18px",
"whiteSpace": "normal",
}
],
page_action="none",
dtid="column_list",
),
)
return table
layout = [ layout = [
dcc.Location(id="tableau_url", refresh=False), dcc.Location(id="tableau_url", refresh=False),
dcc.Store(id="filter-cleanup-trigger"), dcc.Store(id="filter-cleanup-trigger"),
dcc.Store(id="tableau-hidden-columns"),
html.Script( html.Script(
type="application/ld+json", type="application/ld+json",
id="dataset_jsonld", id="dataset_jsonld",
@@ -192,7 +255,7 @@ layout = [
), ),
dbc.ModalFooter( dbc.ModalFooter(
dbc.Button( dbc.Button(
"Close", "Fermer",
id="tableau_help_close", id="tableau_help_close",
className="ms-auto", className="ms-auto",
n_clicks=0, n_clicks=0,
@@ -221,6 +284,11 @@ layout = [
children=[ children=[
html.Div( html.Div(
[ [
dbc.Button(
"Colonnes affichées",
id="tableau_columns_open",
className="column_list",
),
html.P("lignes", id="nb_rows"), html.P("lignes", id="nb_rows"),
html.Div(id="copy-container"), html.Div(id="copy-container"),
dcc.Input(id="share-url", readOnly=True, style={"display": "none"}), dcc.Input(id="share-url", readOnly=True, style={"display": "none"}),
@@ -235,6 +303,27 @@ layout = [
], ],
className="table-menu", className="table-menu",
), ),
dbc.Modal(
[
dbc.ModalHeader(dbc.ModalTitle("Choix des colonnes à afficher")),
dbc.ModalBody(
id="tableau_columns_body", children=make_tableau_columns_table()
),
dbc.ModalFooter(
dbc.Button(
"Fermer",
id="tableau_columns_close",
className="ms-auto",
n_clicks=0,
)
),
],
id="tableau_columns",
is_open=False,
fullscreen="md-down",
scrollable=True,
size="xl",
),
datatable, datatable,
], ],
), ),
@@ -297,7 +386,7 @@ def download_data(n_clicks, filter_query, sort_by, hidden_columns: list = None):
@callback( @callback(
Output("table", "filter_query"), Output("table", "filter_query"),
Output("table", "sort_by"), Output("table", "sort_by"),
Output("table", "hidden_columns"), Output("tableau-hidden-columns", "data"),
Output("tableau_url", "search", allow_duplicate=True), Output("tableau_url", "search", allow_duplicate=True),
Output("filter-cleanup-trigger", "data"), Output("filter-cleanup-trigger", "data"),
Input("tableau_url", "search"), Input("tableau_url", "search"),
@@ -368,9 +457,9 @@ def sync_url_and_reset_button(filter_query, sort_by, hidden_columns, href):
params["tris"] = json.dumps(sort_by) params["tris"] = json.dumps(sort_by)
if hidden_columns: if hidden_columns:
columns = invert_columns(hidden_columns) table_columns = invert_columns(hidden_columns)
columns = ",".join(columns) table_columns = ",".join(table_columns)
params["colonnes"] = columns params["colonnes"] = table_columns
query_string = urllib.parse.urlencode(params) query_string = urllib.parse.urlencode(params)
full_url = f"{base_url}?{query_string}" if query_string else base_url full_url = f"{base_url}?{query_string}" if query_string else base_url
@@ -414,3 +503,52 @@ def toggle_tableau_help(click_open, click_close, is_open):
if click_open or click_close: if click_open or click_close:
return not is_open return not is_open
return is_open return is_open
@callback(
Output("tableau-hidden-columns", "data", allow_duplicate=True),
Input("column_list", "selected_rows"),
prevent_initial_call=True,
)
def update_hidden_columns_from_checkboxes(selected_columns):
if selected_columns:
selected_columns = [columns[i] for i in selected_columns]
hidden_columns = [col for col in columns if col not in selected_columns]
return hidden_columns
else:
return []
@callback(
Output("table", "hidden_columns", allow_duplicate=True),
Input(
"tableau-hidden-columns",
"data",
),
prevent_initial_call=True,
)
def store_hidden_columns(hidden_columns):
return hidden_columns
@callback(
Output("column_list", "selected_rows"),
Input("table", "hidden_columns"),
State("column_list", "selected_rows"), # pour éviter la boucle infinie
)
def update_checkboxes_from_hidden_columns(hidden_cols, current_checkboxes):
# Show all columns that are NOT hidden
visible_cols = [columns.index(col) for col in columns if col not in hidden_cols]
return visible_cols
@callback(
Output("tableau_columns", "is_open"),
Input("tableau_columns_open", "n_clicks"),
Input("tableau_columns_close", "n_clicks"),
State("tableau_columns", "is_open"),
)
def toggle_tableau_columns(click_open, click_close, is_open):
if click_open or click_close:
return not is_open
return is_open
+3 -1
View File
@@ -2,6 +2,7 @@ import json
import logging import logging
import os import os
import uuid import uuid
from collections import OrderedDict
from time import localtime, sleep from time import localtime, sleep
import polars as pl import polars as pl
@@ -456,7 +457,7 @@ def get_data_schema() -> dict:
else: else:
raise Exception(f"Chemin vers le schéma invalide: {path}") raise Exception(f"Chemin vers le schéma invalide: {path}")
new_schema = {} new_schema = OrderedDict()
for col in original_schema["fields"]: for col in original_schema["fields"]:
new_schema[col["name"]] = col new_schema[col["name"]] = col
@@ -757,3 +758,4 @@ meta_content = {
), ),
} }
data_schema = get_data_schema() data_schema = get_data_schema()
columns = df.columns