def register_info_modal_callbacks(app):
"""
Register info modal callbacks with Dash app.
Parameters
----------
app : Dash
Dash application instance
Notes
-----
Registered Callbacks:
1. Toggle modal (open button, close button, backdrop click)
Behavior:
- Modal opens when "More Info" button is clicked
- Modal closes when X button is clicked or when clicking outside
- Modal is extra-large and vertically centered
See Also
--------
create_info_modal : Modal component
create_intro_card : Intro card with More Info button
"""
global _info_modal_callbacks_registered
# Prevent duplicate registration
if _info_modal_callbacks_registered:
logger.warning(
"[INFO_MODAL] Callbacks already registered, skipping duplicate registration"
)
return
logger.info("[INFO_MODAL] Registering info modal callbacks...")
# ========================================
# Callback: Toggle Modal Open/Close
# ========================================
@app.callback(
Output("info-modal", "is_open"),
[
Input("info-modal-open-button", "n_clicks"),
Input("info-modal-close-button", "n_clicks"),
],
State("info-modal", "is_open"),
prevent_initial_call=True,
)
def toggle_info_modal(open_clicks, close_clicks, is_open):
"""
Toggle info modal open/close state.
Parameters
----------
open_clicks : int
Number of open button clicks
close_clicks : int
Number of close button clicks
is_open : bool
Current modal state
Returns
-------
bool
New modal state (toggled based on which button was clicked)
Notes
-----
- Uses ctx.triggered_id to determine which button was clicked
- Open button opens the modal
- Close button closes the modal
- Backdrop click automatically closes (handled by dbc.Modal backdrop=True)
"""
logger.info("=" * 80)
logger.info(f"[INFO_MODAL-{_callback_instance_id}] TOGGLE_INFO_MODAL CALLED")
logger.info(
f"[INFO_MODAL-{_callback_instance_id}] Input open_clicks: {open_clicks}"
)
logger.info(
f"[INFO_MODAL-{_callback_instance_id}] Input close_clicks: {close_clicks}"
)
logger.info(f"[INFO_MODAL-{_callback_instance_id}] State is_open: {is_open}")
logger.info(
f"[INFO_MODAL-{_callback_instance_id}] ctx.triggered: {ctx.triggered}"
)
logger.info(
f"[INFO_MODAL-{_callback_instance_id}] ctx.triggered_id: {ctx.triggered_id}"
)
if not ctx.triggered_id:
logger.warning(
f"[INFO_MODAL-{_callback_instance_id}] No triggered_id, returning no_update"
)
logger.info("=" * 80)
return no_update
# Determine action based on which button was clicked
if ctx.triggered_id == "info-modal-open-button":
new_state = True
logger.info(
f"[INFO_MODAL-{_callback_instance_id}] OPEN BUTTON CLICKED: "
f"Setting modal to OPEN (clicks={open_clicks})"
)
elif ctx.triggered_id == "info-modal-close-button":
new_state = False
logger.info(
f"[INFO_MODAL-{_callback_instance_id}] CLOSE BUTTON CLICKED: "
f"Setting modal to CLOSED (clicks={close_clicks})"
)
else:
logger.warning(
f"[INFO_MODAL-{_callback_instance_id}] Unknown trigger: {ctx.triggered_id}, "
f"returning current state"
)
new_state = is_open
logger.info(f"[INFO_MODAL-{_callback_instance_id}] RETURNING: {new_state}")
logger.info("=" * 80)
return new_state
# ========================================
# Callback: Toggle Sample Data Modal Open/Close
# ========================================
@app.callback(
Output("sample-data-modal", "is_open"),
[
Input("sample-data-card", "n_clicks"),
Input("sample-data-modal-close-button", "n_clicks"),
],
State("sample-data-modal", "is_open"),
prevent_initial_call=True,
)
def toggle_sample_data_modal(card_clicks, close_clicks, is_open):
"""
Toggle sample data modal open/close state.
Parameters
----------
card_clicks : int
Number of card clicks
close_clicks : int
Number of close button clicks
is_open : bool
Current modal state
Returns
-------
bool
New modal state (toggled based on which element was clicked)
Notes
-----
- Uses ctx.triggered_id to determine which element was clicked
- Card click opens the modal
- Close button closes the modal
- Backdrop click automatically closes (handled by dbc.Modal backdrop=True)
"""
logger.info("=" * 80)
logger.info(
f"[SAMPLE_DATA_MODAL-{_callback_instance_id}] TOGGLE_SAMPLE_DATA_MODAL CALLED"
)
logger.info(
f"[SAMPLE_DATA_MODAL-{_callback_instance_id}] Input card_clicks: {card_clicks}"
)
logger.info(
f"[SAMPLE_DATA_MODAL-{_callback_instance_id}] Input close_clicks: {close_clicks}"
)
logger.info(
f"[SAMPLE_DATA_MODAL-{_callback_instance_id}] State is_open: {is_open}"
)
logger.info(
f"[SAMPLE_DATA_MODAL-{_callback_instance_id}] ctx.triggered: {ctx.triggered}"
)
logger.info(
f"[SAMPLE_DATA_MODAL-{_callback_instance_id}] ctx.triggered_id: {ctx.triggered_id}"
)
if not ctx.triggered_id:
logger.warning(
f"[SAMPLE_DATA_MODAL-{_callback_instance_id}] No triggered_id, returning no_update"
)
logger.info("=" * 80)
return no_update
# Determine action based on which element was clicked
if ctx.triggered_id == "sample-data-card":
new_state = True
logger.info(
f"[SAMPLE_DATA_MODAL-{_callback_instance_id}] CARD CLICKED: "
f"Setting modal to OPEN (clicks={card_clicks})"
)
elif ctx.triggered_id == "sample-data-modal-close-button":
new_state = False
logger.info(
f"[SAMPLE_DATA_MODAL-{_callback_instance_id}] CLOSE BUTTON CLICKED: "
f"Setting modal to CLOSED (clicks={close_clicks})"
)
else:
logger.warning(
f"[SAMPLE_DATA_MODAL-{_callback_instance_id}] Unknown trigger: {ctx.triggered_id}, "
f"returning current state"
)
new_state = is_open
logger.info(
f"[SAMPLE_DATA_MODAL-{_callback_instance_id}] RETURNING: {new_state}"
)
logger.info("=" * 80)
return new_state
# ========================================
# Callback: Trigger sample data download
# ========================================
@app.callback(
Output("sample-data-download", "data"),
Input("sample-data-download-btn", "n_clicks"),
prevent_initial_call=True,
)
def download_sample_data(n_clicks):
"""Download sample dataset file without URL navigation."""
if not n_clicks:
return no_update
dataset_path = (
Path(__file__).resolve().parents[3] / "data" / "exemple_dataset.txt"
)
if not dataset_path.exists():
logger.error("[SAMPLE_DATA_MODAL] Sample dataset file not found")
return no_update
return dcc.send_file(str(dataset_path), "exemple_dataset.txt")
# ========================================
# Callback: Toggle Publications Modal Open/Close
# ========================================
@app.callback(
Output("publications-modal", "is_open"),
[
Input("publications-card", "n_clicks"),
Input("publications-modal-close-button", "n_clicks"),
],
State("publications-modal", "is_open"),
prevent_initial_call=True,
)
def toggle_publications_modal(card_clicks, close_clicks, is_open):
"""
Toggle publications modal open/close state.
Parameters
----------
card_clicks : int
Number of card clicks
close_clicks : int
Number of close button clicks
is_open : bool
Current modal state
Returns
-------
bool
New modal state (toggled based on which element was clicked)
Notes
-----
- Uses ctx.triggered_id to determine which element was clicked
- Card click opens the modal
- Close button closes the modal
- Backdrop click automatically closes (handled by dbc.Modal backdrop=True)
"""
logger.info("=" * 80)
logger.info(
f"[PUBLICATIONS_MODAL-{_callback_instance_id}] TOGGLE_PUBLICATIONS_MODAL CALLED"
)
logger.info(
f"[PUBLICATIONS_MODAL-{_callback_instance_id}] Input card_clicks: {card_clicks}"
)
logger.info(
f"[PUBLICATIONS_MODAL-{_callback_instance_id}] Input close_clicks: {close_clicks}"
)
logger.info(
f"[PUBLICATIONS_MODAL-{_callback_instance_id}] State is_open: {is_open}"
)
logger.info(
f"[PUBLICATIONS_MODAL-{_callback_instance_id}] ctx.triggered: {ctx.triggered}"
)
logger.info(
f"[PUBLICATIONS_MODAL-{_callback_instance_id}] ctx.triggered_id: {ctx.triggered_id}"
)
if not ctx.triggered_id:
logger.warning(
f"[PUBLICATIONS_MODAL-{_callback_instance_id}] No triggered_id, returning no_update"
)
logger.info("=" * 80)
return no_update
# Determine action based on which element was clicked
if ctx.triggered_id == "publications-card":
new_state = True
logger.info(
f"[PUBLICATIONS_MODAL-{_callback_instance_id}] CARD CLICKED: "
f"Setting modal to OPEN (clicks={card_clicks})"
)
elif ctx.triggered_id == "publications-modal-close-button":
new_state = False
logger.info(
f"[PUBLICATIONS_MODAL-{_callback_instance_id}] CLOSE BUTTON CLICKED: "
f"Setting modal to CLOSED (clicks={close_clicks})"
)
else:
logger.warning(
f"[PUBLICATIONS_MODAL-{_callback_instance_id}] Unknown trigger: {ctx.triggered_id}, "
f"returning current state"
)
new_state = is_open
logger.info(
f"[PUBLICATIONS_MODAL-{_callback_instance_id}] RETURNING: {new_state}"
)
logger.info("=" * 80)
return new_state
# Mark callbacks as registered
_info_modal_callbacks_registered = True
logger.info("[INFO_MODAL] [OK] Info modal callbacks registered successfully")
logger.info(f"[INFO_MODAL] [OK] Callback IDs registered:")
logger.info(
f"[INFO_MODAL] - toggle_info_modal: Inputs=['info-modal-open-button', 'info-modal-close-button'], Output='info-modal.is_open'"
)
logger.info(
f"[INFO_MODAL] - toggle_sample_data_modal: Inputs=['sample-data-card', 'sample-data-modal-close-button'], Output='sample-data-modal.is_open'"
)
logger.info(
"[INFO_MODAL] - download_sample_data: Input='sample-data-download-btn', Output='sample-data-download.data'"
)
logger.info(
f"[INFO_MODAL] - toggle_publications_modal: Inputs=['publications-card', 'publications-modal-close-button'], Output='publications-modal.is_open'"
)