maidr 1.7.0__tar.gz → 1.7.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. {maidr-1.7.0 → maidr-1.7.2}/CHANGELOG.md +16 -0
  2. {maidr-1.7.0 → maidr-1.7.2}/PKG-INFO +1 -1
  3. {maidr-1.7.0 → maidr-1.7.2}/maidr/__init__.py +1 -1
  4. {maidr-1.7.0 → maidr-1.7.2}/maidr/core/maidr.py +46 -13
  5. {maidr-1.7.0 → maidr-1.7.2}/maidr/core/plot/lineplot.py +8 -3
  6. {maidr-1.7.0 → maidr-1.7.2}/maidr/util/mixin/extractor_mixin.py +54 -7
  7. {maidr-1.7.0 → maidr-1.7.2}/pyproject.toml +1 -1
  8. {maidr-1.7.0 → maidr-1.7.2}/uv.lock +1 -1
  9. {maidr-1.7.0 → maidr-1.7.2}/.commitlintrc.cjs +0 -0
  10. {maidr-1.7.0 → maidr-1.7.2}/.editorconfig +0 -0
  11. {maidr-1.7.0 → maidr-1.7.2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  12. {maidr-1.7.0 → maidr-1.7.2}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  13. {maidr-1.7.0 → maidr-1.7.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  14. {maidr-1.7.0 → maidr-1.7.2}/.github/copilot-instructions.md +0 -0
  15. {maidr-1.7.0 → maidr-1.7.2}/.github/workflows/ci.yml +0 -0
  16. {maidr-1.7.0 → maidr-1.7.2}/.github/workflows/docs.yml +0 -0
  17. {maidr-1.7.0 → maidr-1.7.2}/.github/workflows/release.yml +0 -0
  18. {maidr-1.7.0 → maidr-1.7.2}/.gitignore +0 -0
  19. {maidr-1.7.0 → maidr-1.7.2}/.pre-commit-config.yaml +0 -0
  20. {maidr-1.7.0 → maidr-1.7.2}/.vscode/extensions.json +0 -0
  21. {maidr-1.7.0 → maidr-1.7.2}/.vscode/settings.json +0 -0
  22. {maidr-1.7.0 → maidr-1.7.2}/CONDUCT.md +0 -0
  23. {maidr-1.7.0 → maidr-1.7.2}/CONTRIBUTING.md +0 -0
  24. {maidr-1.7.0 → maidr-1.7.2}/LICENSE +0 -0
  25. {maidr-1.7.0 → maidr-1.7.2}/README.md +0 -0
  26. {maidr-1.7.0 → maidr-1.7.2}/docs/.gitignore +0 -0
  27. {maidr-1.7.0 → maidr-1.7.2}/docs/CNAME +0 -0
  28. {maidr-1.7.0 → maidr-1.7.2}/docs/_environment +0 -0
  29. {maidr-1.7.0 → maidr-1.7.2}/docs/_extensions/machow/interlinks/.gitignore +0 -0
  30. {maidr-1.7.0 → maidr-1.7.2}/docs/_extensions/machow/interlinks/_extension.yml +0 -0
  31. {maidr-1.7.0 → maidr-1.7.2}/docs/_extensions/machow/interlinks/interlinks.lua +0 -0
  32. {maidr-1.7.0 → maidr-1.7.2}/docs/_extensions/shafayetShafee/line-highlight/_extension.yml +0 -0
  33. {maidr-1.7.0 → maidr-1.7.2}/docs/_extensions/shafayetShafee/line-highlight/line-highlight.lua +0 -0
  34. {maidr-1.7.0 → maidr-1.7.2}/docs/_extensions/shafayetShafee/line-highlight/resources/css/line-highlight.css +0 -0
  35. {maidr-1.7.0 → maidr-1.7.2}/docs/_extensions/shafayetShafee/line-highlight/resources/js/line-highlight.js +0 -0
  36. {maidr-1.7.0 → maidr-1.7.2}/docs/_quarto.yml +0 -0
  37. {maidr-1.7.0 → maidr-1.7.2}/docs/examples.qmd +0 -0
  38. {maidr-1.7.0 → maidr-1.7.2}/docs/index.qmd +0 -0
  39. {maidr-1.7.0 → maidr-1.7.2}/docs/styles.css +0 -0
  40. {maidr-1.7.0 → maidr-1.7.2}/example/bar/example_bar_plot.ipynb +0 -0
  41. {maidr-1.7.0 → maidr-1.7.2}/example/bar/matplotlib/example_mpl_bar_plot.py +0 -0
  42. {maidr-1.7.0 → maidr-1.7.2}/example/bar/seaborn/example_sns_bar_plot.py +0 -0
  43. {maidr-1.7.0 → maidr-1.7.2}/example/box/example_box_plot.ipynb +0 -0
  44. {maidr-1.7.0 → maidr-1.7.2}/example/box/matplotlib/example_mpl_box.py +0 -0
  45. {maidr-1.7.0 → maidr-1.7.2}/example/box/seaborn/example_sns_box.py +0 -0
  46. {maidr-1.7.0 → maidr-1.7.2}/example/candle_stick/legacy_candlestick_example.py +0 -0
  47. {maidr-1.7.0 → maidr-1.7.2}/example/candle_stick/mplfinance_candlestick_example.py +0 -0
  48. {maidr-1.7.0 → maidr-1.7.2}/example/candle_stick/test_data_daily_current_year.csv +0 -0
  49. {maidr-1.7.0 → maidr-1.7.2}/example/candle_stick/test_data_daily_mixed_years.csv +0 -0
  50. {maidr-1.7.0 → maidr-1.7.2}/example/candle_stick/test_data_hourly.csv +0 -0
  51. {maidr-1.7.0 → maidr-1.7.2}/example/candle_stick/test_data_minute.csv +0 -0
  52. {maidr-1.7.0 → maidr-1.7.2}/example/candle_stick/test_data_weekly.csv +0 -0
  53. {maidr-1.7.0 → maidr-1.7.2}/example/candle_stick/volcandat.csv +0 -0
  54. {maidr-1.7.0 → maidr-1.7.2}/example/count/example_count_plot.ipynb +0 -0
  55. {maidr-1.7.0 → maidr-1.7.2}/example/count/seaborn/example_sns_count_plot.py +0 -0
  56. {maidr-1.7.0 → maidr-1.7.2}/example/dodged/matplotlib/example_mpl_dodged.py +0 -0
  57. {maidr-1.7.0 → maidr-1.7.2}/example/dodged/seaborn/example_sns_dodged.py +0 -0
  58. {maidr-1.7.0 → maidr-1.7.2}/example/facet-subplots/matplotlib/example_mpl_facet_bar_plot.py +0 -0
  59. {maidr-1.7.0 → maidr-1.7.2}/example/facet-subplots/matplotlib/example_mpl_facet_combined_plot.py +0 -0
  60. {maidr-1.7.0 → maidr-1.7.2}/example/facet-subplots/seaborn/example_sns_facet_bar_plot.py +0 -0
  61. {maidr-1.7.0 → maidr-1.7.2}/example/facet-subplots/seaborn/example_sns_facet_combined_plot.py +0 -0
  62. {maidr-1.7.0 → maidr-1.7.2}/example/flask/test_flask_app.py +0 -0
  63. {maidr-1.7.0 → maidr-1.7.2}/example/heatmap/example_heatmap_plot.ipynb +0 -0
  64. {maidr-1.7.0 → maidr-1.7.2}/example/heatmap/matplotlib/example_mpl_heatmap.py +0 -0
  65. {maidr-1.7.0 → maidr-1.7.2}/example/heatmap/seaborn/example_sns_heatmap.py +0 -0
  66. {maidr-1.7.0 → maidr-1.7.2}/example/histogram/example_histogram_plot.ipynb +0 -0
  67. {maidr-1.7.0 → maidr-1.7.2}/example/histogram/matplotlib/example_mpl_hist.py +0 -0
  68. {maidr-1.7.0 → maidr-1.7.2}/example/histogram/matplotlib/histogram_with_kde_matplotlib.py +0 -0
  69. {maidr-1.7.0 → maidr-1.7.2}/example/histogram/seaborn/example_sns_hist.py +0 -0
  70. {maidr-1.7.0 → maidr-1.7.2}/example/histogram/seaborn/histogram_with_kde_seaborn.py +0 -0
  71. {maidr-1.7.0 → maidr-1.7.2}/example/kde/example_kde_plots.ipynb +0 -0
  72. {maidr-1.7.0 → maidr-1.7.2}/example/kde/matplotlib/multiple_kde_matplotlib.py +0 -0
  73. {maidr-1.7.0 → maidr-1.7.2}/example/kde/matplotlib/single_kde_matplotlib.py +0 -0
  74. {maidr-1.7.0 → maidr-1.7.2}/example/kde/seaborn/multiple_kde_seaborn.py +0 -0
  75. {maidr-1.7.0 → maidr-1.7.2}/example/kde/seaborn/single_kde_seaborn.py +0 -0
  76. {maidr-1.7.0 → maidr-1.7.2}/example/line/example_line_plot.ipynb +0 -0
  77. {maidr-1.7.0 → maidr-1.7.2}/example/line/matplotlib/example_mpl_line.py +0 -0
  78. {maidr-1.7.0 → maidr-1.7.2}/example/line/seaborn/example_sns_line.py +0 -0
  79. {maidr-1.7.0 → maidr-1.7.2}/example/multilayer/example_mpl_multilayer.py +0 -0
  80. {maidr-1.7.0 → maidr-1.7.2}/example/multilayer/example_multilayer_plot.ipynb +0 -0
  81. {maidr-1.7.0 → maidr-1.7.2}/example/multiline/example_multiline_plot.ipynb +0 -0
  82. {maidr-1.7.0 → maidr-1.7.2}/example/multiline/matplotlib/example_mpl_multiline.py +0 -0
  83. {maidr-1.7.0 → maidr-1.7.2}/example/multiline/seaborn/example_sns_multiline.py +0 -0
  84. {maidr-1.7.0 → maidr-1.7.2}/example/multipanel/example_multipanel_plot.ipynb +0 -0
  85. {maidr-1.7.0 → maidr-1.7.2}/example/multipanel/matplotlib/example_mpl_multipanel.py +0 -0
  86. {maidr-1.7.0 → maidr-1.7.2}/example/multipanel/seaborn/example_sns_multipanel.py +0 -0
  87. {maidr-1.7.0 → maidr-1.7.2}/example/quarto/demo.qmd +0 -0
  88. {maidr-1.7.0 → maidr-1.7.2}/example/reg/example_reg_plots.ipynb +0 -0
  89. {maidr-1.7.0 → maidr-1.7.2}/example/reg/matplotlib/example_matplotlib_smooth_plot.py +0 -0
  90. {maidr-1.7.0 → maidr-1.7.2}/example/reg/seaborn/example_sns_reg.py +0 -0
  91. {maidr-1.7.0 → maidr-1.7.2}/example/scatter/example_scatter_plot.ipynb +0 -0
  92. {maidr-1.7.0 → maidr-1.7.2}/example/scatter/matplotlib/example_mpl_scatter.py +0 -0
  93. {maidr-1.7.0 → maidr-1.7.2}/example/scatter/seaborn/example_sns_scatter.py +0 -0
  94. {maidr-1.7.0 → maidr-1.7.2}/example/shiny/example_shiny_scatter.py +0 -0
  95. {maidr-1.7.0 → maidr-1.7.2}/example/stacked/matplotlib/example_mpl_stacked.html +0 -0
  96. {maidr-1.7.0 → maidr-1.7.2}/example/stacked/matplotlib/example_mpl_stacked.py +0 -0
  97. {maidr-1.7.0 → maidr-1.7.2}/example/stacked/seaborn/example_sns_stacked.html +0 -0
  98. {maidr-1.7.0 → maidr-1.7.2}/example/stacked/seaborn/example_sns_stacked.py +0 -0
  99. {maidr-1.7.0 → maidr-1.7.2}/example/streamlit/example_streamlit_app.py +0 -0
  100. {maidr-1.7.0 → maidr-1.7.2}/maidr/api.py +0 -0
  101. {maidr-1.7.0 → maidr-1.7.2}/maidr/core/__init__.py +0 -0
  102. {maidr-1.7.0 → maidr-1.7.2}/maidr/core/context_manager.py +0 -0
  103. {maidr-1.7.0 → maidr-1.7.2}/maidr/core/enum/__init__.py +0 -0
  104. {maidr-1.7.0 → maidr-1.7.2}/maidr/core/enum/library.py +0 -0
  105. {maidr-1.7.0 → maidr-1.7.2}/maidr/core/enum/maidr_key.py +0 -0
  106. {maidr-1.7.0 → maidr-1.7.2}/maidr/core/enum/plot_type.py +0 -0
  107. {maidr-1.7.0 → maidr-1.7.2}/maidr/core/enum/smooth_keywords.py +0 -0
  108. {maidr-1.7.0 → maidr-1.7.2}/maidr/core/figure_manager.py +0 -0
  109. {maidr-1.7.0 → maidr-1.7.2}/maidr/core/plot/__init__.py +0 -0
  110. {maidr-1.7.0 → maidr-1.7.2}/maidr/core/plot/barplot.py +0 -0
  111. {maidr-1.7.0 → maidr-1.7.2}/maidr/core/plot/boxplot.py +0 -0
  112. {maidr-1.7.0 → maidr-1.7.2}/maidr/core/plot/candlestick.py +0 -0
  113. {maidr-1.7.0 → maidr-1.7.2}/maidr/core/plot/grouped_barplot.py +0 -0
  114. {maidr-1.7.0 → maidr-1.7.2}/maidr/core/plot/heatmap.py +0 -0
  115. {maidr-1.7.0 → maidr-1.7.2}/maidr/core/plot/histogram.py +0 -0
  116. {maidr-1.7.0 → maidr-1.7.2}/maidr/core/plot/maidr_plot.py +0 -0
  117. {maidr-1.7.0 → maidr-1.7.2}/maidr/core/plot/maidr_plot_factory.py +0 -0
  118. {maidr-1.7.0 → maidr-1.7.2}/maidr/core/plot/mplfinance_barplot.py +0 -0
  119. {maidr-1.7.0 → maidr-1.7.2}/maidr/core/plot/mplfinance_lineplot.py +0 -0
  120. {maidr-1.7.0 → maidr-1.7.2}/maidr/core/plot/regplot.py +0 -0
  121. {maidr-1.7.0 → maidr-1.7.2}/maidr/core/plot/scatterplot.py +0 -0
  122. {maidr-1.7.0 → maidr-1.7.2}/maidr/exception/__init__.py +0 -0
  123. {maidr-1.7.0 → maidr-1.7.2}/maidr/exception/extraction_error.py +0 -0
  124. {maidr-1.7.0 → maidr-1.7.2}/maidr/patch/__init__.py +0 -0
  125. {maidr-1.7.0 → maidr-1.7.2}/maidr/patch/barplot.py +0 -0
  126. {maidr-1.7.0 → maidr-1.7.2}/maidr/patch/boxplot.py +0 -0
  127. {maidr-1.7.0 → maidr-1.7.2}/maidr/patch/candlestick.py +0 -0
  128. {maidr-1.7.0 → maidr-1.7.2}/maidr/patch/clear.py +0 -0
  129. {maidr-1.7.0 → maidr-1.7.2}/maidr/patch/common.py +0 -0
  130. {maidr-1.7.0 → maidr-1.7.2}/maidr/patch/heatmap.py +0 -0
  131. {maidr-1.7.0 → maidr-1.7.2}/maidr/patch/highlight.py +0 -0
  132. {maidr-1.7.0 → maidr-1.7.2}/maidr/patch/histogram.py +0 -0
  133. {maidr-1.7.0 → maidr-1.7.2}/maidr/patch/kdeplot.py +0 -0
  134. {maidr-1.7.0 → maidr-1.7.2}/maidr/patch/lineplot.py +0 -0
  135. {maidr-1.7.0 → maidr-1.7.2}/maidr/patch/mplfinance.py +0 -0
  136. {maidr-1.7.0 → maidr-1.7.2}/maidr/patch/regplot.py +0 -0
  137. {maidr-1.7.0 → maidr-1.7.2}/maidr/patch/scatterplot.py +0 -0
  138. {maidr-1.7.0 → maidr-1.7.2}/maidr/util/__init__.py +0 -0
  139. {maidr-1.7.0 → maidr-1.7.2}/maidr/util/datetime_conversion.py +0 -0
  140. {maidr-1.7.0 → maidr-1.7.2}/maidr/util/dedup_utils.py +0 -0
  141. {maidr-1.7.0 → maidr-1.7.2}/maidr/util/environment.py +0 -0
  142. {maidr-1.7.0 → maidr-1.7.2}/maidr/util/mixin/__init__.py +0 -0
  143. {maidr-1.7.0 → maidr-1.7.2}/maidr/util/mixin/merger_mixin.py +0 -0
  144. {maidr-1.7.0 → maidr-1.7.2}/maidr/util/mplfinance_utils.py +0 -0
  145. {maidr-1.7.0 → maidr-1.7.2}/maidr/util/plot_detection.py +0 -0
  146. {maidr-1.7.0 → maidr-1.7.2}/maidr/util/regression_line_utils.py +0 -0
  147. {maidr-1.7.0 → maidr-1.7.2}/maidr/util/svg_utils.py +0 -0
  148. {maidr-1.7.0 → maidr-1.7.2}/maidr/widget/__init__.py +0 -0
  149. {maidr-1.7.0 → maidr-1.7.2}/maidr/widget/shiny.py +0 -0
  150. {maidr-1.7.0 → maidr-1.7.2}/tests/__init__.py +0 -0
  151. {maidr-1.7.0 → maidr-1.7.2}/tests/conftest.py +0 -0
  152. {maidr-1.7.0 → maidr-1.7.2}/tests/core/__init__.py +0 -0
  153. {maidr-1.7.0 → maidr-1.7.2}/tests/core/enum/__init__.py +0 -0
  154. {maidr-1.7.0 → maidr-1.7.2}/tests/core/plot/__init__.py +0 -0
  155. {maidr-1.7.0 → maidr-1.7.2}/tests/core/test_figure_manager.py +0 -0
  156. {maidr-1.7.0 → maidr-1.7.2}/tests/core/test_maidr_plot.py +0 -0
  157. {maidr-1.7.0 → maidr-1.7.2}/tests/core/test_maidr_plot_factory.py +0 -0
  158. {maidr-1.7.0 → maidr-1.7.2}/tests/fixture/__init__.py +0 -0
  159. {maidr-1.7.0 → maidr-1.7.2}/tests/fixture/library_factory.py +0 -0
  160. {maidr-1.7.0 → maidr-1.7.2}/tests/fixture/matplotlib_factory.py +0 -0
  161. {maidr-1.7.0 → maidr-1.7.2}/tests/fixture/seaborn_factory.py +0 -0
  162. {maidr-1.7.0 → maidr-1.7.2}/tox.ini +0 -0
@@ -1,6 +1,22 @@
1
1
  # CHANGELOG
2
2
 
3
3
 
4
+ ## v1.7.2 (2025-09-12)
5
+
6
+ ### Bug Fixes
7
+
8
+ - Prevent iframe resizing when modal is open ([#237](https://github.com/xability/py-maidr/pull/237),
9
+ [`89a4253`](https://github.com/xability/py-maidr/commit/89a42537a32025b286b8089b7f4b21b7c409c4e4))
10
+
11
+
12
+ ## v1.7.1 (2025-09-03)
13
+
14
+ ### Bug Fixes
15
+
16
+ - Address categorical x-values in line plot ([#236](https://github.com/xability/py-maidr/pull/236),
17
+ [`d4e0eea`](https://github.com/xability/py-maidr/commit/d4e0eea6e558fc6540eb9500b82dedb339f739b4))
18
+
19
+
4
20
  ## v1.7.0 (2025-08-21)
5
21
 
6
22
  ### Features
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: maidr
3
- Version: 1.7.0
3
+ Version: 1.7.2
4
4
  Summary: Multimodal Access and Interactive Data Representations
5
5
  Project-URL: Homepage, https://xability.github.io/py-maidr
6
6
  Project-URL: Repository, https://github.com/xability/py-maidr
@@ -1,4 +1,4 @@
1
- __version__ = "1.7.0"
1
+ __version__ = "1.7.2"
2
2
 
3
3
  from .api import close, render, save_html, show, stacked
4
4
  from .core import Maidr
@@ -366,16 +366,29 @@ class Maidr:
366
366
  iframe.contentWindow.document
367
367
  ) {{
368
368
  let iframeDocument = iframe.contentWindow.document;
369
- let brailleContainer =
370
- iframeDocument.getElementById('braille-input');
369
+ // Detect braille textarea by dynamic id prefix
370
+ let brailleContainer = iframeDocument.querySelector('[id^="maidr-braille-textarea"]');
371
+ // Detect review input container by class name
372
+ let reviewInputContainer = iframeDocument.querySelector('.maidr-review-input');
371
373
  iframe.style.height = 'auto';
372
374
  let height = iframeDocument.body.scrollHeight;
373
- if (brailleContainer &&
374
- brailleContainer === iframeDocument.activeElement
375
- ) {{
375
+ // Consider braille active if it or any descendant has focus
376
+ let isBrailleActive = brailleContainer && (
377
+ brailleContainer === iframeDocument.activeElement ||
378
+ (typeof brailleContainer.contains === 'function' && brailleContainer.contains(iframeDocument.activeElement))
379
+ );
380
+ // Consider review input active if it or any descendant has focus
381
+ let isReviewInputActive = reviewInputContainer && (
382
+ reviewInputContainer === iframeDocument.activeElement ||
383
+ (typeof reviewInputContainer.contains === 'function' && reviewInputContainer.contains(iframeDocument.activeElement))
384
+ );
385
+ // (logs removed)
386
+ if (isBrailleActive) {{
376
387
  height += 100;
377
- }}else{{
378
- height += 50
388
+ }} else if (isReviewInputActive) {{
389
+ height += 50;
390
+ }} else {{
391
+ height += 50;
379
392
  }}
380
393
  iframe.style.height = (height) + 'px';
381
394
  iframe.style.width = iframeDocument.body.scrollWidth + 'px';
@@ -387,12 +400,32 @@ class Maidr:
387
400
  resizeIframe();
388
401
  iframe.contentWindow.addEventListener('resize', resizeIframe);
389
402
  }};
390
- iframe.contentWindow.document.addEventListener('focusin', () => {{
391
- resizeIframe();
392
- }});
393
- iframe.contentWindow.document.addEventListener('focusout', () => {{
394
- resizeIframe();
395
- }});
403
+ // Delegate focus events for braille textarea (by id prefix)
404
+ iframe.contentWindow.document.addEventListener('focusin', (e) => {{
405
+ try {{
406
+ const t = e && e.target ? e.target : null;
407
+ if (t && typeof t.id === 'string' && t.id.startsWith('maidr-braille-textarea')) resizeIframe();
408
+ }} catch (_) {{ resizeIframe(); }}
409
+ }}, true);
410
+ iframe.contentWindow.document.addEventListener('focusout', (e) => {{
411
+ try {{
412
+ const t = e && e.target ? e.target : null;
413
+ if (t && typeof t.id === 'string' && t.id.startsWith('maidr-braille-textarea')) resizeIframe();
414
+ }} catch (_) {{ resizeIframe(); }}
415
+ }}, true);
416
+ // Delegate focus events for review input container (by class name)
417
+ iframe.contentWindow.document.addEventListener('focusin', (e) => {{
418
+ try {{
419
+ const t = e && e.target ? e.target : null;
420
+ if (t && t.classList && t.classList.contains('maidr-review-input')) resizeIframe();
421
+ }} catch (_) {{ resizeIframe(); }}
422
+ }}, true);
423
+ iframe.contentWindow.document.addEventListener('focusout', (e) => {{
424
+ try {{
425
+ const t = e && e.target ? e.target : null;
426
+ if (t && t.classList && t.classList.contains('maidr-review-input')) resizeIframe();
427
+ }} catch (_) {{ resizeIframe(); }}
428
+ }}, true);
396
429
  """
397
430
  return resizing_script
398
431
 
@@ -114,13 +114,18 @@ class MultiLinePlot(MaidrPlot, LineExtractorMixin):
114
114
  elif not label.startswith("_child"):
115
115
  line_type = label
116
116
 
117
+ # Use the new method to extract data with categorical labels
118
+ line_coords = LineExtractorMixin.extract_line_data_with_categorical_labels(self.ax, line)
119
+ if line_coords is None:
120
+ continue
121
+
117
122
  line_data = [
118
123
  {
119
- MaidrKey.X: float(x),
120
- MaidrKey.Y: float(y),
124
+ MaidrKey.X: x,
125
+ MaidrKey.Y: y,
121
126
  **({MaidrKey.FILL: line_type} if line_type else {}),
122
127
  }
123
- for x, y in line.get_xydata() # type: ignore
128
+ for x, y in line_coords
124
129
  ]
125
130
 
126
131
  if line_data:
@@ -1,6 +1,5 @@
1
- from __future__ import annotations
2
-
3
- from typing import Any
1
+ import numpy as np
2
+ from typing import Any, List, Optional, Tuple, Union
4
3
 
5
4
  from matplotlib.axes import Axes
6
5
  from matplotlib.cm import ScalarMappable
@@ -38,7 +37,7 @@ class ContainerExtractorMixin:
38
37
 
39
38
  class LevelExtractorMixin:
40
39
  @staticmethod
41
- def extract_level(ax: Axes, key: MaidrKey = MaidrKey.X) -> list[str] | None:
40
+ def extract_level(ax: Axes, key: MaidrKey = MaidrKey.X) -> Optional[List[str]]:
42
41
  """Retrieve label texts from Axes based on the specified Maidr key."""
43
42
  if ax is None:
44
43
  return None
@@ -95,7 +94,7 @@ class LevelExtractorMixin:
95
94
 
96
95
  class LineExtractorMixin:
97
96
  @staticmethod
98
- def extract_line(ax: Axes) -> Line2D | None:
97
+ def extract_line(ax: Axes) -> Optional[Line2D]:
99
98
  """Retrieve the last line object from Axes, if available."""
100
99
  if ax is None or ax.get_lines() is None:
101
100
  return None
@@ -105,7 +104,7 @@ class LineExtractorMixin:
105
104
  return ax.get_lines()[-1]
106
105
 
107
106
  @staticmethod
108
- def extract_lines(ax: Axes) -> list[Line2D] | None:
107
+ def extract_lines(ax: Axes) -> Optional[List[Line2D]]:
109
108
  """Retrieve all line objects from Axes, if available."""
110
109
  if ax is None or ax.get_lines() is None:
111
110
  return None
@@ -114,6 +113,54 @@ class LineExtractorMixin:
114
113
  # `maidr` package supports the same.
115
114
  return ax.get_lines()
116
115
 
116
+ @staticmethod
117
+ def extract_line_data_with_categorical_labels(ax: Axes, line: Line2D) -> Optional[List[Tuple[Union[str, float], float]]]:
118
+ """
119
+ Extract line data with proper handling of categorical x-axis labels.
120
+
121
+ Parameters
122
+ ----------
123
+ ax : Axes
124
+ The matplotlib axes object
125
+ line : Line2D
126
+ The line object to extract data from
127
+
128
+ Returns
129
+ -------
130
+ Optional[List[Tuple[Union[str, float], float]]]
131
+ List of (x, y) tuples where x values are categorical labels if available,
132
+ or numeric values if no categorical labels are found
133
+ """
134
+ if ax is None or line is None:
135
+ return None
136
+
137
+ xydata = line.get_xydata()
138
+ if xydata is None:
139
+ return None
140
+
141
+ # Convert to numpy array for easier handling
142
+ xy_array = np.asarray(xydata)
143
+ if xy_array.size == 0:
144
+ return None
145
+
146
+ # Extract x-axis labels for categorical data
147
+ x_labels = LevelExtractorMixin.extract_level(ax, MaidrKey.X)
148
+
149
+ # If we have categorical labels, map numeric coordinates to labels
150
+ if x_labels:
151
+ result = []
152
+ for x, y in xy_array:
153
+ # Map numeric x-coordinate to categorical label if available
154
+ if isinstance(x, (int, float)) and 0 <= int(x) < len(x_labels):
155
+ x_value = x_labels[int(x)]
156
+ else:
157
+ x_value = float(x)
158
+ result.append((x_value, float(y)))
159
+ return result
160
+ else:
161
+ # No categorical labels, return numeric values
162
+ return [(float(x), float(y)) for x, y in xy_array]
163
+
117
164
 
118
165
  class CollectionExtractorMixin:
119
166
  @staticmethod
@@ -133,7 +180,7 @@ class CollectionExtractorMixin:
133
180
 
134
181
  class ScalarMappableExtractorMixin:
135
182
  @staticmethod
136
- def extract_scalar_mappable(ax: Axes) -> ScalarMappable | None:
183
+ def extract_scalar_mappable(ax: Axes) -> Optional[ScalarMappable]:
137
184
  """Retrieve the first collection ScalarMappable from an Axes object."""
138
185
  if ax is None or ax.get_children() is None:
139
186
  return None
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "maidr"
7
- version = "1.7.0"
7
+ version = "1.7.2"
8
8
  description = "Multimodal Access and Interactive Data Representations"
9
9
  authors = [
10
10
  { name = "JooYoung Seo", email = "jseo1005@illinois.edu" },
@@ -1675,7 +1675,7 @@ wheels = [
1675
1675
 
1676
1676
  [[package]]
1677
1677
  name = "maidr"
1678
- version = "1.6.1"
1678
+ version = "1.7.1"
1679
1679
  source = { editable = "." }
1680
1680
  dependencies = [
1681
1681
  { name = "htmltools" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes