From 81c0100bae79cea958e10e9dc0ab642271159c48 Mon Sep 17 00:00:00 2001 From: 2bndy5 <2bndy5@gmail.com> Date: Thu, 17 Mar 2022 16:56:52 -0700 Subject: [PATCH 01/25] minor fixups: spelling, pylint, & pkg.lock --- docs/admonitions.rst | 72 ++++++++-------------------- docs/customization.rst | 4 +- package-lock.json | 103 +++++++++++++++++++---------------------- setup.py | 5 +- 4 files changed, 71 insertions(+), 113 deletions(-) diff --git a/docs/admonitions.rst b/docs/admonitions.rst index 36e335653..bfc936bf2 100644 --- a/docs/admonitions.rst +++ b/docs/admonitions.rst @@ -33,7 +33,7 @@ usage in other sphinx-based themes. They are: Admonitions from mkdocs-material ******************************** -Some addtional admonitions are supported via the source code from the mkdocs-material theme. +Some additional admonitions are supported via the source code from the mkdocs-material theme. These admonitions can still be used, but the syntax is a little different because it relies on the generic admonition defined in the reStructuredText specifications. @@ -120,67 +120,35 @@ shown inside the demonstrated admonition. .. admonition:: Quote :class: quote -Collapsable dropdown +Collapsible dropdown ********************* -For collapsable dropdown admonitions, the mkdocs-material theme relies on a markdown syntax +For collapsible dropdown admonitions, the mkdocs-material theme relies on a markdown syntax extension that cannot be used with sphinx. Instead, this sphinx-immaterial theme relies on -other sphinx extensions to get similar (and more customizable) results. +the sphinxcontrib-details-directive extension to get similar results. -.. dropdown:: We endorse the sphinx-design extension! - :icon: package-dependents - :animate: fade-in-slide-down - :class-title: sd-text-primary sd-outline-primary - :class-container: sd-outline-danger +.. details:: Open by default + :class: example + :open: - .. card:: You can do some pretty cool stuff with the :bdg-info-line:`sphinx-design extension`. - :class-title: sd-text-center - :margin: auto - - .. grid:: - - .. grid-item:: - :columns: auto - :margin: auto - - .. button-ref:: buttons - :color: success - - .. grid-item:: - :columns: auto - :margin: auto - - .. button-ref:: tabs - :color: success - - .. grid-item:: - :columns: auto - :margin: auto - - .. button-ref:: grids - :color: success - - .. grid-item:: - :columns: auto - :margin: auto + .. code-block:: rst - .. button-ref:: cards - :color: success + .. details:: Open by default + :class: example + :open: - .. grid-item:: - :columns: auto - :margin: auto +.. details:: Closed by default + :class: help - .. button-ref:: dropdowns - :color: success + .. code-block:: rst - Not to mention inline octicon :octicon:`infinity;1.5rem;sd-text-info` and fontawesome - :fab:`font-awesome-flag` icons and :bdg-ref-info:`badges`. + .. details:: Closed by default + :class: help Removing the title ****************** -Since the mkdocs-material theme relies on a mardown extension that also allows removing the title +Since the mkdocs-material theme relies on a markdown extension that also allows removing the title from an admonition, this theme has an added directive to do just that: ``md-admonition``. The admonition's title can be removed if the ``md-admonition`` directive is not provided @@ -259,10 +227,10 @@ folder and add the new CSS to an additional style sheet. .. tab-item:: conf.py code :class-label: sd-font-weight-light - .. code-block:: python + .. code-block:: python - html_static_path = ["_static"] - html_css_files = ["extra_css.css"] + html_static_path = ["_static"] + html_css_files = ["extra_css.css"] .. admonition:: Pied Piper diff --git a/docs/customization.rst b/docs/customization.rst index 3d5e3422c..fd4255477 100644 --- a/docs/customization.rst +++ b/docs/customization.rst @@ -264,7 +264,7 @@ Configuration Options .. confval:: palette - The theme's color pallete **must be** a single `dict` or a `list` of `dict`\ s. + The theme's color pallette **must be** a single `dict` or a `list` of `dict`\ s. Each `dict` can optionally specify a ``scheme``, ``primary``, ``accent``, and ``media`` fields. @@ -427,7 +427,7 @@ The standard structure of the site (relative to the base domain) is usually /1.0 /2.0 -Whereas Sphinx must be executed seperately for each version of the documentation you are +Whereas Sphinx must be executed separately for each version of the documentation you are building. .. code-block:: text diff --git a/package-lock.json b/package-lock.json index 80dc7d9fd..38ecbdef4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,16 +26,16 @@ "@types/css-modules": "^1.0.2", "@types/escape-html": "1.0.1", "@types/html-minifier": "^4.0.2", - "@types/node": "^17.0.18", + "@types/node": "^17.0.21", "@types/resize-observer-browser": "^0.1.7", "@types/sass": "^1.43.1", - "@typescript-eslint/eslint-plugin": "^5.12.0", - "@typescript-eslint/parser": "^5.12.0", + "@typescript-eslint/eslint-plugin": "^5.12.1", + "@typescript-eslint/parser": "^5.12.1", "autoprefixer": "^10.4.2", "chokidar": "^3.5.3", "cssnano": "^5.0.17", "esbuild": "^0.14.23", - "eslint": "^8.9.0", + "eslint": "^8.10.0", "eslint-plugin-eslint-comments": "^3.2.0", "eslint-plugin-import": "^2.25.4", "eslint-plugin-jsdoc": "^37.9.4", @@ -48,14 +48,14 @@ "material-shadows": "^3.0.1", "npm-check-updates": "^12.4.0", "npm-run-all": "^4.1.5", - "postcss": "^8.4.6", + "postcss": "^8.4.7", "postcss-dir-pseudo-class": "^6.0.4", "postcss-inline-svg": "^5.0.0", "postcss-logical": "^5.0.4", "preact": "^10.6.6", "rimraf": "^3.0.2", - "sass": "^1.49.8", - "stylelint": "^14.5.1", + "sass": "^1.49.9", + "stylelint": "^14.5.3", "stylelint-config-rational-order": "^0.1.2", "stylelint-config-recommended": "^7.0.0", "stylelint-config-standard-scss": "^3.0.0", @@ -428,16 +428,16 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.1.0.tgz", - "integrity": "sha512-C1DfL7XX4nPqGd6jcP01W9pVM1HYCuUkFk1432D7F0v3JSlUIeOYn9oCoi3eoLZ+iwBSb29BMFxxny0YrrEZqg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.1.tgz", + "integrity": "sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^9.3.1", "globals": "^13.9.0", - "ignore": "^4.0.6", + "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.0.4", @@ -453,15 +453,6 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "node_modules/@eslint/eslintrc/node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, "node_modules/@eslint/eslintrc/node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -3449,12 +3440,12 @@ } }, "node_modules/eslint": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.9.0.tgz", - "integrity": "sha512-PB09IGwv4F4b0/atrbcMFboF/giawbBLVC7fyDamk5Wtey4Jh2K+rYaBhCAbUyEI4QzB1ly09Uglc9iCtFaG2Q==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.11.0.tgz", + "integrity": "sha512-/KRpd9mIRg2raGxHRGwW9ZywYNAClZrHjdueHcrVDuO3a6bj83eoTirCCk0M0yPwOjWYKHwRVRid+xK4F/GHgA==", "dev": true, "dependencies": { - "@eslint/eslintrc": "^1.1.0", + "@eslint/eslintrc": "^1.2.1", "@humanwhocodes/config-array": "^0.9.2", "ajv": "^6.10.0", "chalk": "^4.0.0", @@ -4666,9 +4657,9 @@ } }, "node_modules/globals": { - "version": "13.12.1", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.1.tgz", - "integrity": "sha512-317dFlgY2pdJZ9rspXDks7073GpDmXdfbM3vYYp0HAMKGDh1FfWPleI2ljVNLQX5M5lXcAslTcPTrOrMEFOjyw==", + "version": "13.13.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.13.0.tgz", + "integrity": "sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -8145,21 +8136,27 @@ } }, "node_modules/postcss": { - "version": "8.4.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.6.tgz", - "integrity": "sha512-OovjwIzs9Te46vlEx7+uXB0PLijpwjXGKXjVGGPIGubGpq7uh5Xgf6D6FiJ/SzJMBosHDp6a2hiXOS97iBXcaA==", + "version": "8.4.12", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.12.tgz", + "integrity": "sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + } + ], "dependencies": { - "nanoid": "^3.2.0", + "nanoid": "^3.3.1", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" }, "engines": { "node": "^10 || ^12 || >=14" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" } }, "node_modules/postcss-calc": { @@ -13020,16 +13017,16 @@ } }, "@eslint/eslintrc": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.1.0.tgz", - "integrity": "sha512-C1DfL7XX4nPqGd6jcP01W9pVM1HYCuUkFk1432D7F0v3JSlUIeOYn9oCoi3eoLZ+iwBSb29BMFxxny0YrrEZqg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.1.tgz", + "integrity": "sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ==", "dev": true, "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^9.3.1", "globals": "^13.9.0", - "ignore": "^4.0.6", + "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.0.4", @@ -13042,12 +13039,6 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, "import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -15216,12 +15207,12 @@ "dev": true }, "eslint": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.9.0.tgz", - "integrity": "sha512-PB09IGwv4F4b0/atrbcMFboF/giawbBLVC7fyDamk5Wtey4Jh2K+rYaBhCAbUyEI4QzB1ly09Uglc9iCtFaG2Q==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.11.0.tgz", + "integrity": "sha512-/KRpd9mIRg2raGxHRGwW9ZywYNAClZrHjdueHcrVDuO3a6bj83eoTirCCk0M0yPwOjWYKHwRVRid+xK4F/GHgA==", "dev": true, "requires": { - "@eslint/eslintrc": "^1.1.0", + "@eslint/eslintrc": "^1.2.1", "@humanwhocodes/config-array": "^0.9.2", "ajv": "^6.10.0", "chalk": "^4.0.0", @@ -16157,9 +16148,9 @@ } }, "globals": { - "version": "13.12.1", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.1.tgz", - "integrity": "sha512-317dFlgY2pdJZ9rspXDks7073GpDmXdfbM3vYYp0HAMKGDh1FfWPleI2ljVNLQX5M5lXcAslTcPTrOrMEFOjyw==", + "version": "13.13.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.13.0.tgz", + "integrity": "sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -18784,12 +18775,12 @@ "dev": true }, "postcss": { - "version": "8.4.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.6.tgz", - "integrity": "sha512-OovjwIzs9Te46vlEx7+uXB0PLijpwjXGKXjVGGPIGubGpq7uh5Xgf6D6FiJ/SzJMBosHDp6a2hiXOS97iBXcaA==", + "version": "8.4.12", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.12.tgz", + "integrity": "sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg==", "dev": true, "requires": { - "nanoid": "^3.2.0", + "nanoid": "^3.3.1", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } diff --git a/setup.py b/setup.py index e2df1e3c7..10cec1824 100644 --- a/setup.py +++ b/setup.py @@ -17,20 +17,19 @@ # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -import setuptools - import atexit import distutils.command.build import os import subprocess import tempfile +import setuptools import setuptools.command.build_py import setuptools.command.develop import setuptools.command.install import setuptools.command.sdist -with open("requirements.txt") as reqs: +with open("requirements.txt", encoding="utf-8") as reqs: REQUIREMENTS = [reqs.readlines()] root_dir = os.path.dirname(os.path.abspath(__file__)) From 8d007e393733ff44d450231bd313dc4d83b997a9 Mon Sep 17 00:00:00 2001 From: 2bndy5 <2bndy5@gmail.com> Date: Thu, 17 Mar 2022 16:58:57 -0700 Subject: [PATCH 02/25] fix #26 (till sphinx v6.0) --- sphinx_immaterial/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sphinx_immaterial/__init__.py b/sphinx_immaterial/__init__.py index 60cb2b4fc..23f427bb4 100644 --- a/sphinx_immaterial/__init__.py +++ b/sphinx_immaterial/__init__.py @@ -260,6 +260,8 @@ def _config_inited( """Merge defaults into theme options.""" if config["language"] is None: config["language"] = "en" # default to English language + # make code-blocks' line numbers be a separate column of a 1-row table + config["html_codeblock_linenos_style"] = "table" # default is "inline" config["html_theme_options"] = dict_merge( DEFAULT_THEME_OPTIONS, config["html_theme_options"] ) From 06f5a85dfbef62ee15a03999ee6ebeda9914d9a3 Mon Sep 17 00:00:00 2001 From: 2bndy5 <2bndy5@gmail.com> Date: Thu, 17 Mar 2022 20:09:14 -0700 Subject: [PATCH 03/25] adding directives Docs require sphinxcontrib-details-directive, and use it instead of sphinx-design dropdown directive. This also reverts the currently erroneous fix to #15. Add `md-tab-set` & `md-tab-item` directives. Update docs with new page on how to use these new directives. --- docs/_static/extra_css.css | 16 ++- docs/admonitions.rst | 32 +---- docs/conf.py | 3 +- docs/content_tabs.rst | 128 ++++++++++++++++++ docs/customization.rst | 9 +- docs/index.rst | 1 + docs/requirements.txt | 2 +- sphinx_immaterial/content_tabs.py | 217 ++++++++++++++++++++++++++++++ src/assets/stylesheets/main.scss | 2 +- 9 files changed, 368 insertions(+), 42 deletions(-) create mode 100644 docs/content_tabs.rst create mode 100644 sphinx_immaterial/content_tabs.py diff --git a/docs/_static/extra_css.css b/docs/_static/extra_css.css index 68db199bf..bdce350a5 100644 --- a/docs/_static/extra_css.css +++ b/docs/_static/extra_css.css @@ -36,15 +36,19 @@ mask-image: var(--md-admonition-icon--pied-piper); } -/* *********************** sphinx-design tab-set style overrides ********************* */ +/* ************************ custom-tab-set-style **************************** */ -.sd-tab-set>input:checked+label { - border-color: var(--md-primary-fg-color); - color: var(--md-primary-fg-color); +.custom-tab-set-style { + border: solid 2px var(--md-default-fg-color); + padding: 0 5px; } -.sd-tab-set>input:not(:checked)+label:hover { - color: var(--md-primary-fg-color); +/* *********************** custom-tab-item-style **************************** */ + +.custom-tab-item-style { + border: solid 2px var(--md-default-fg-color); + padding: 0 5px; + margin-top: 3px; } /* ************************* inline icon stuff ****************************** */ diff --git a/docs/admonitions.rst b/docs/admonitions.rst index bfc936bf2..243adbd64 100644 --- a/docs/admonitions.rst +++ b/docs/admonitions.rst @@ -190,10 +190,9 @@ If you want to add a custom admonition type, all you need is a color and an \*.s Copy the icon's code from the `.icons `_ folder and add the new CSS to an additional style sheet. -.. tab-set:: +.. md-tab-set:: - .. tab-item:: rST code - :class-label: sd-font-weight-light + .. md-tab-item:: rST code .. code-block:: rst @@ -202,8 +201,7 @@ folder and add the new CSS to an additional style sheet. Don't tell him you use spaces instead of tabs... - .. tab-item:: CSS code - :class-label: sd-font-weight-light + .. md-tab-item:: CSS code .. code-block:: css :caption: docs/_static/extra_css.css @@ -224,8 +222,7 @@ folder and add the new CSS to an additional style sheet. mask-image: var(--md-admonition-icon--pied-piper); } - .. tab-item:: conf.py code - :class-label: sd-font-weight-light + .. md-tab-item:: conf.py code .. code-block:: python @@ -237,24 +234,3 @@ folder and add the new CSS to an additional style sheet. :class: pied-piper Don't tell him you use spaces instead of tabs... - -.. _tabbed_locks: - -.. md-admonition:: - :class: todo - - The use of tabbed blocks (as seen above) are provided from `sphinx-design extension`_. - We added some custom CSS to make the tabs' labels conform to this theme's color palete. - - .. code-block:: css - - .sd-tab-set>input:checked+label { - border-color: var(--md-primary-fg-color); - color: var(--md-primary-fg-color); - } - - .sd-tab-set>input:not(:checked)+label:hover { - color: var(--md-primary-fg-color); - } - -.. _sphinx-design extension: ` `_ diff --git a/docs/conf.py b/docs/conf.py index b215a937f..f7f981ddd 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -34,7 +34,8 @@ "sphinx.ext.todo", "sphinx.ext.mathjax", "sphinx.ext.viewcode", - "sphinx_design", + "sphinx_immaterial.content_tabs", + "sphinxcontrib.details.directive", ] intersphinx_mapping = { diff --git a/docs/content_tabs.rst b/docs/content_tabs.rst new file mode 100644 index 000000000..3d4fb143c --- /dev/null +++ b/docs/content_tabs.rst @@ -0,0 +1,128 @@ +.. _sphinx-design tabs: https://sphinx-design.readthedocs.io/en/furo-theme/tabs.html + +Using tabbed content +==================== + +.. note:: + This document focused on content tabs, not navigation tabs. + +Use of `content tabs in mkdocs-material `_ +theme relies on a markdown extension that isn't used in the world of Sphinx. Instead, +the sphinx-immaterial theme provides its own directives to make use of content tabs. + +.. code-block:: python + :caption: These directives require an added entry in conf.py's extension list: + + extensions = ["sphinx_immaterial", "sphinx_immaterial.content_tabs"] + +.. admonition:: Linked Tabs + :class: missing + + The `linked content tabs `_ + seen in mkdocs-material is not supported until that feature transitions from the mkdocs-material theme's insider + releases to it's public releases. + + However, you can use other sphinx extensions (like `sphinx-design tabs`_) to achieve this functionality. + Although, other extensions will require some custom CSS styling to match the mkdocs-material + theme's styling for content tabs. + +.. confval:: md-tab-set + + Each set of tabs on a page must begin with a `md-tab-set` directive. This directive + only accepts children that `md-tab-item` directives. + + This directive supports ``:class:`` and ``:name:`` options to use custom CSS classes + and reference links (respectively) + + .. code-block:: rst + + .. md-tab-set:: + :class: custom-tab-set-style + :name: ref_this_tab_set + + .. md-tab-item:: Local Ref + + A reference to this tab set renders like so: + `tab set description `. + + This syntax can only be used on the same page as the tab set. + + .. md-tab-item:: Cross-page Ref + + To cross-reference this tab set from a different page, use + :ref:`tab set description ` + + Use this syntax to give a tab set a custom description. + Clearly this also works on the same page. + + .. md-tab-item:: Custom CSS + + .. literalinclude:: _static/extra_css.css + :language: css + :start-at: /* ************************ custom-tab-set-style + :end-before: /* *********************** custom-tab-item-style + + .. md-tab-set:: + :class: custom-tab-set-style + :name: ref_this_tab_set + + .. md-tab-item:: Local Ref + + A reference to this tab set renders like so: + `tab set description `. + + This syntax can only be used on the same page as the tab set. + + .. md-tab-item:: Cross-page Ref + + To cross-reference this tab set from a different page, use + :ref:`tab set description ` + + Clearly this also works on the same page as the tab set. + + .. md-tab-item:: Custom CSS + + .. literalinclude:: _static/extra_css.css + :language: css + :start-at: /* ************************ custom-tab-set-style + :end-before: /* *********************** custom-tab-item-style + +.. confval:: md-tab-item + + This directive is used to create a tab within a set of content tabs. It requires a + label as it's argument. Additionally, it also supports the ``:class:`` option, to + optionally provide custom CSS classes to the tab's content (not the tab's label). + + .. code-block:: rst + + .. md-tab-set:: + + .. md-tab-item:: Customized content + :class: custom-tab-item-style + + This content could be styled differently from other page content. + + .. md-tab-item:: Custom CSS + + .. literalinclude:: _static/extra_css.css + :language: css + :start-at: /* *********************** custom-tab-item-style + :end-before: /* ************************* inline icon stuff + + .. md-tab-set:: + + .. md-tab-item:: Customized content + :class: custom-tab-item-style + + This content could be styled differently from other page content. + + .. md-tab-item:: Custom CSS + + .. literalinclude:: _static/extra_css.css + :language: css + :start-at: /* *********************** custom-tab-item-style + :end-before: /* ************************* inline icon stuff + +Typical examples are seen in this documentations' +`Custom admonitions `_ and +:ref:`Version Information Structure ` sections. \ No newline at end of file diff --git a/docs/customization.rst b/docs/customization.rst index fd4255477..b93102a5a 100644 --- a/docs/customization.rst +++ b/docs/customization.rst @@ -531,10 +531,10 @@ aliases. Other required fields include ``version`` and ``title``. ``aliases`` do not apply when using an external URL (as in not relative to the same webserver) in the ``verion`` field. -.. tab-set:: +.. md-tab-set:: + :name: version_info_example - .. tab-item:: Using ``version_info`` in conf.py - :class-label: sd-font-weight-light + .. md-tab-item:: Using ``version_info`` in conf.py .. code-block:: python @@ -546,8 +546,7 @@ aliases. Other required fields include ``version`` and ``title``. ], } - .. tab-item:: Using a JSON file - :class-label: sd-font-weight-light + .. md-tab-item:: Using a JSON file .. hint:: Remember to set ``"version_dropdown": True`` in the conf.py file's `html_theme_options` `dict`. diff --git a/docs/index.rst b/docs/index.rst index b7a40c184..ce626485c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -69,6 +69,7 @@ or ``theme.conf`` for more details. customization admonitions + content_tabs .. toctree:: :caption: Examples and Uses diff --git a/docs/requirements.txt b/docs/requirements.txt index fa0dda8e3..4f1766b22 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,3 +1,3 @@ # The sphinx docs theme to use sphinx_immaterial -sphinx-design +sphinxcontrib-details-directive diff --git a/sphinx_immaterial/content_tabs.py b/sphinx_immaterial/content_tabs.py new file mode 100644 index 000000000..806e8cad5 --- /dev/null +++ b/sphinx_immaterial/content_tabs.py @@ -0,0 +1,217 @@ +""" +A special theme-specific extension to support "content tabs" from mkdocs-material. +""" +from typing import List, Any +from docutils import nodes +from docutils.parsers.rst import directives +from sphinx.util.docutils import SphinxDirective +from sphinx.util.nodes import NodeMatcher +from sphinx.application import Sphinx +from sphinx.util.logging import getLogger +from sphinx.transforms.post_transforms import SphinxPostTransform + + +page_tab_set_counter = [0] +LOGGER = getLogger(__name__) + + +def is_component(node: nodes.Node, name: str): + """Check if a node is a certain tabbed component.""" + try: + return node.get("type") == name + except AttributeError: + return False + + +class content_tab_set(nodes.container): + pass + + +class MaterialTabSetDirective(SphinxDirective): + """A container for a set of tab items.""" + + has_content = True + option_spec = { + "name": directives.unchanged, + "class": directives.class_option, + } + + def run(self) -> List[nodes.Node]: + """Run the directive.""" + self.assert_has_content() + tab_set = content_tab_set( + "", + is_div=True, + type="md-tab-set", + classes=["tabbed-set", "tabbed-alternate"] + self.options.get("class", []), + ) + self.set_source_info(tab_set) + if self.options.get("name", ""): + self.add_name(tab_set) + page_tab_set_counter[0] += 1 + self.state.nested_parse(self.content, self.content_offset, tab_set) + tab_total = 0 + for item in tab_set.children: + if not is_component(item, "md-tab-item"): + LOGGER.warning( + "All children of a 'md-tab-set' should be 'md-tab-item'", + location=item, + ) + break + tab_total += 1 + tab_set["data-tabs"] = f"{page_tab_set_counter[0]}:{tab_total}" + return [tab_set] + + +class MaterialTabItemDirective(SphinxDirective): + """A single tab item in a tab set. + Note: This directive generates a single container, + for the label and content:: + + + ...title nodes + + ...content nodes + This allows for a default rendering in non-HTML outputs. + The ``MaterialTabSetHtmlTransform`` then transforms this container + into the HTML specific structure. + """ + + required_arguments = 1 # the tab label is the first argument + final_argument_whitespace = True + has_content = True + option_spec = { + "class": directives.class_option, + } + + def run(self) -> List[nodes.Node]: + """Run the directive.""" + self.assert_has_content() + if not is_component(self.state_machine.node, "md-tab-set"): + LOGGER.warning( + "The parent of a 'md-tab-item' should be a 'md-tab-set'", + location=(self.env.docname, self.lineno), + ) + tab_item = nodes.container( + "", is_div=True, type="md-tab-item", classes=["tabbed-block"] + ) + + # add tab label + textnodes, _ = self.state.inline_text(self.arguments[0], self.lineno) + tab_label = nodes.rubric( + self.arguments[0], + *textnodes, + classes=["tabbed-label"], + ) + self.add_name(tab_label) + tab_item += tab_label + + # add tab content + tab_content = nodes.container( + "", is_div=True, type="", classes=["tabbed-block"] + self.options.get("class", []) + ) + self.state.nested_parse(self.content, self.content_offset, tab_content) + tab_item += tab_content + + return [tab_item] + + +class content_tab_input(nodes.Element, nodes.General): + pass + + +class content_tab_label(nodes.TextElement, nodes.General): + pass + + +def visit_tab_input(self, node): + attributes = {"ids": [node["id"]], "type": node["type"], "name": node["set_id"]} + if node["checked"]: + attributes["checked"] = "checked" + self.body.append(self.starttag(node, "input", **attributes)) + + +def depart_tab_input(self, node): + self.body.append("") + + +def visit_tab_label(self, node): + attributes = {"for": node["input_id"]} + self.body.append(self.starttag(node, "label", **attributes)) + + +def depart_tab_label(self, node): + self.body.append("") + + +def visit_tab_set(self, node): + attributes = {"data-tabs": node["data-tabs"]} + self.body.append(self.starttag(node, "div", **attributes)) + + +def depart_tab_set(self, node): + self.body.append("") + + +class MaterialTabSetHtmlTransform(SphinxPostTransform): + """Transform tab-set to HTML specific AST structure.""" + + default_priority = 200 + formats = ("html",) + + def run(self, **kwargs: Any) -> None: + """Run the transform.""" + matcher = NodeMatcher(nodes.container, type="md-tab-set") + for set_numb, tab_set in enumerate(self.document.traverse(matcher)): + tab_set_identity = f"__tabbed_{set_numb + 1}" + children = [] + tab_label_div = nodes.container("", is_div=True, classes=["tabbed-labels"]) + tab_content_div = nodes.container( + "", is_div=True, classes=["tabbed-content"] + ) + tab_total = 0 + for tab_item in tab_set.children: + try: + tab_label, tab_block = tab_item.children + except ValueError: + print(tab_item) + raise + tab_total += 1 + tab_item_identity = tab_set_identity + f"_{tab_total}" + + # create: + input_node = content_tab_input( + "", + id=tab_item_identity, + set_id=tab_set_identity, + type="radio", + checked=(tab_total == 1), + ) + input_node.source, input_node.line = tab_item.source, tab_item.line + children.append(input_node) + + # create: + label_node = content_tab_label( + "", + *tab_label.children, + input_id=tab_item_identity, + classes=tab_label["classes"], + ) + label_node.source, label_node.line = tab_item.source, tab_item.line + tab_label_div += label_node + + # add content + tab_content_div += tab_block + + tab_set.children = children + tab_set += tab_label_div + tab_set += tab_content_div + + +def setup(app: Sphinx) -> None: + app.add_directive("md-tab-set", MaterialTabSetDirective) + app.add_directive("md-tab-item", MaterialTabItemDirective) + app.add_post_transform(MaterialTabSetHtmlTransform) + app.add_node(content_tab_input, html=(visit_tab_input, depart_tab_input)) + app.add_node(content_tab_label, html=(visit_tab_label, depart_tab_label)) + app.add_node(content_tab_set, html=(visit_tab_set, depart_tab_set)) diff --git a/src/assets/stylesheets/main.scss b/src/assets/stylesheets/main.scss index dcabfd15b..7a4ead995 100644 --- a/src/assets/stylesheets/main.scss +++ b/src/assets/stylesheets/main.scss @@ -67,7 +67,7 @@ @import "main/extensions/pymdownx/arithmatex"; @import "main/extensions/pymdownx/critic"; -/// @import "main/extensions/pymdownx/details"; +@import "main/extensions/pymdownx/details"; @import "main/extensions/pymdownx/emoji"; @import "main/extensions/pymdownx/highlight"; @import "main/extensions/pymdownx/keys"; From 134b97742d20137417c880179e2fa8ede7b989c3 Mon Sep 17 00:00:00 2001 From: 2bndy5 <2bndy5@gmail.com> Date: Thu, 17 Mar 2022 20:52:36 -0700 Subject: [PATCH 04/25] monkey patch details directive --- sphinx_immaterial/__init__.py | 5 ++++- sphinx_immaterial/details_patch.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 sphinx_immaterial/details_patch.py diff --git a/sphinx_immaterial/__init__.py b/sphinx_immaterial/__init__.py index 23f427bb4..2d3ebe747 100644 --- a/sphinx_immaterial/__init__.py +++ b/sphinx_immaterial/__init__.py @@ -20,6 +20,7 @@ from . import object_toc from . import postprocess_html from . import search_adapt +from .details_patch import monkey_patch_details_run logger = sphinx.util.logging.getLogger(__name__) @@ -286,7 +287,9 @@ def setup(app): # register our custom adminition directive app.setup_extension("sphinx_immaterial.md_admonition") - + # patch the details directive's run method + monkey_patch_details_run() + return { "parallel_read_safe": True, "parallel_write_safe": True, diff --git a/sphinx_immaterial/details_patch.py b/sphinx_immaterial/details_patch.py new file mode 100644 index 000000000..fa6726ccb --- /dev/null +++ b/sphinx_immaterial/details_patch.py @@ -0,0 +1,30 @@ +from typing import List +from docutils import nodes +try: + from sphinxcontrib.details.directive import DetailsDirective +except ImportError: + DetailsDirective = None + +def monkey_patch_details_run(): + """Patch the details directive to respect the class option. + This solution is a temporary fix pending response from + https://github.com/tk0miya/sphinxcontrib-details-directive/issues/4 + """ + if DetailsDirective is None: + return + + def run(self) -> List[nodes.container]: + admonition = nodes.container( + "", + classes=self.options.get("class", []) + self.options.get("classes", []), + opened="open" in self.options, + type="details", + ) + textnodes, messages = self.state.inline_text(self.arguments[0], self.lineno) + admonition += nodes.paragraph(self.arguments[0], "", *textnodes) + admonition += messages + self.state.nested_parse(self.content, self.content_offset, admonition) + self.add_name(admonition) + return [admonition] + + DetailsDirective.run = run From a5c7f05318696be22274a77601b703a171f8dc6f Mon Sep 17 00:00:00 2001 From: 2bndy5 <2bndy5@gmail.com> Date: Thu, 17 Mar 2022 20:56:42 -0700 Subject: [PATCH 05/25] pleasing pylint --- sphinx_immaterial/__init__.py | 2 +- sphinx_immaterial/content_tabs.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/sphinx_immaterial/__init__.py b/sphinx_immaterial/__init__.py index 2d3ebe747..2d1b8b734 100644 --- a/sphinx_immaterial/__init__.py +++ b/sphinx_immaterial/__init__.py @@ -289,7 +289,7 @@ def setup(app): app.setup_extension("sphinx_immaterial.md_admonition") # patch the details directive's run method monkey_patch_details_run() - + return { "parallel_read_safe": True, "parallel_write_safe": True, diff --git a/sphinx_immaterial/content_tabs.py b/sphinx_immaterial/content_tabs.py index 806e8cad5..98eaf393f 100644 --- a/sphinx_immaterial/content_tabs.py +++ b/sphinx_immaterial/content_tabs.py @@ -108,7 +108,10 @@ def run(self) -> List[nodes.Node]: # add tab content tab_content = nodes.container( - "", is_div=True, type="", classes=["tabbed-block"] + self.options.get("class", []) + "", + is_div=True, + type="", + classes=["tabbed-block"] + self.options.get("class", []), ) self.state.nested_parse(self.content, self.content_offset, tab_content) tab_item += tab_content From db46c854ad49591b4b353702a3506c89e6e17e0d Mon Sep 17 00:00:00 2001 From: 2bndy5 <2bndy5@gmail.com> Date: Thu, 17 Mar 2022 20:58:40 -0700 Subject: [PATCH 06/25] ran black on details_patch.py --- sphinx_immaterial/details_patch.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sphinx_immaterial/details_patch.py b/sphinx_immaterial/details_patch.py index fa6726ccb..da5dde10b 100644 --- a/sphinx_immaterial/details_patch.py +++ b/sphinx_immaterial/details_patch.py @@ -1,10 +1,12 @@ from typing import List from docutils import nodes + try: from sphinxcontrib.details.directive import DetailsDirective except ImportError: DetailsDirective = None + def monkey_patch_details_run(): """Patch the details directive to respect the class option. This solution is a temporary fix pending response from From 67084bbcd87511cd5cef06d9c2e6a9d4245166e2 Mon Sep 17 00:00:00 2001 From: 2bndy5 <2bndy5@gmail.com> Date: Thu, 17 Mar 2022 21:04:16 -0700 Subject: [PATCH 07/25] [no ci] remove dev artifact from docs --- docs/content_tabs.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/content_tabs.rst b/docs/content_tabs.rst index 3d4fb143c..74eb056da 100644 --- a/docs/content_tabs.rst +++ b/docs/content_tabs.rst @@ -52,7 +52,6 @@ the sphinx-immaterial theme provides its own directives to make use of content t To cross-reference this tab set from a different page, use :ref:`tab set description ` - Use this syntax to give a tab set a custom description. Clearly this also works on the same page. .. md-tab-item:: Custom CSS From 870b644df17924be3dc237fb8e19aa3431cdbd44 Mon Sep 17 00:00:00 2001 From: 2bndy5 <2bndy5@gmail.com> Date: Thu, 17 Mar 2022 23:38:16 -0700 Subject: [PATCH 08/25] introducing mermaid implementation - updates docs about the details directive - adds page that demonstrates how to use the md-mermaid directive. --- docs/admonitions.rst | 15 +- docs/conf.py | 7 +- docs/content_tabs.rst | 4 +- docs/index.rst | 1 + docs/mermaid_diagrams.rst | 214 ++++++++++++++++++++++++++ sphinx_immaterial/mermaid_diagrams.py | 47 ++++++ 6 files changed, 282 insertions(+), 6 deletions(-) create mode 100644 docs/mermaid_diagrams.rst create mode 100644 sphinx_immaterial/mermaid_diagrams.py diff --git a/docs/admonitions.rst b/docs/admonitions.rst index 243adbd64..0f3a475ac 100644 --- a/docs/admonitions.rst +++ b/docs/admonitions.rst @@ -123,9 +123,22 @@ shown inside the demonstrated admonition. Collapsible dropdown ********************* +.. _sphinxcontrib-details-directive extension: https://pypi.org/project/sphinxcontrib-details-directive + For collapsible dropdown admonitions, the mkdocs-material theme relies on a markdown syntax extension that cannot be used with sphinx. Instead, this sphinx-immaterial theme relies on -the sphinxcontrib-details-directive extension to get similar results. +the `sphinxcontrib-details-directive extension`_ +to get similar results. + +The `sphinxcontrib-details-directive extension`_ should be added to conf.py's extension list. + +.. code-block:: python + + extensions = ["sphinx_immaterial", "sphinxcontrib.details.directive"] + + +If the ``:class:`` option is not supplied to the ``details`` directive then the admonition +style falls back to a `note` admonition style. .. details:: Open by default :class: example diff --git a/docs/conf.py b/docs/conf.py index f7f981ddd..606411991 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -34,14 +34,15 @@ "sphinx.ext.todo", "sphinx.ext.mathjax", "sphinx.ext.viewcode", - "sphinx_immaterial.content_tabs", + "sphinx_immaterial", "sphinxcontrib.details.directive", + "sphinx_immaterial.content_tabs", + "sphinx_immaterial.mermaid_diagrams", ] intersphinx_mapping = { "python": ("https://docs.python.org/3", None), "sphinx_docs": ("https://www.sphinx-doc.org/en/master", None), - "sphinx-design": ("https://sphinx-design.readthedocs.io/en/furo-theme", None), } # The reST default role (used for this markup: `text`) to use for all @@ -76,7 +77,7 @@ # -- HTML theme settings ------------------------------------------------ -extensions.append("sphinx_immaterial") +# extensions.append("sphinx_immaterial") html_title = "Sphinx-Immaterial" html_theme = "sphinx_immaterial" html_favicon = "_static/images/favicon.ico" # colored version of material/bookshelf.svg diff --git a/docs/content_tabs.rst b/docs/content_tabs.rst index 74eb056da..807a24a66 100644 --- a/docs/content_tabs.rst +++ b/docs/content_tabs.rst @@ -1,7 +1,7 @@ .. _sphinx-design tabs: https://sphinx-design.readthedocs.io/en/furo-theme/tabs.html -Using tabbed content -==================== +Content tabs +============ .. note:: This document focused on content tabs, not navigation tabs. diff --git a/docs/index.rst b/docs/index.rst index ce626485c..1230d213b 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -70,6 +70,7 @@ or ``theme.conf`` for more details. customization admonitions content_tabs + mermaid_diagrams .. toctree:: :caption: Examples and Uses diff --git a/docs/mermaid_diagrams.rst b/docs/mermaid_diagrams.rst new file mode 100644 index 000000000..acd9e4a9d --- /dev/null +++ b/docs/mermaid_diagrams.rst @@ -0,0 +1,214 @@ +.. _mermaid.js: https://mermaid-js.github.io/mermaid/ + +Mermaid diagrams +================ + +.. note:: + Use of this feature has no affect or affiliation with sphinx's graphviz implementation. + +The mkdocs-material theme is equipped to make use of diagrams generated (during page load time) +with `mermaid.js`_. Although, its implementation relies on a markdown extension that does not get +used by this sphinx-immaterial theme. Thus, the sphinx-immaterial theme provides with an optional +directive that exposes the underlying implementation in mkdocs-material theme. + +.. confval:: md-mermaid + + To enable this directive, simply add the extension to the conf.py extension list: + + .. code-block:: python + + extensions = ["sphinx_immaterial", "sphinx_immaterial.mermaid_diagrams"] + + The `md-mermaid` directive does support the ``:class:`` option, but it has no affect after + the graph is generated. + + This theme comes with CSS styling that conforms to the chosen primary & accent colors + (based on the selected scheme). To modify the CSS for a particular graph, you should use the + mechanics available in `mermaid.js`_. For example, each graph's node has a unique ``id`` + and generic ``class`` attribute that can be used in a CSS rule's selector. + + .. md-admonition:: + :class: missing + + While all `mermaid.js`_ features should work out-of-the-box, This theme will currently only + adjust the fonts and colors for `flowcharts`_, `sequence diagrams`_, `class diagrams`_, + `state diagrams`_ and `entity-relationship diagrams`_. + +.. _flowcharts: + +flowcharts +---------------- + +.. code-block:: rst + + .. md-mermaid:: + + graph LR + A[Start] --> B{Error?}; + B -->|Yes| C[Hmm...]; + C --> D[Debug]; + D --> B; + B ---->|No| E[Yay!]; + +.. md-mermaid:: + + graph LR + A[Start] --> B{Error?}; + B -->|Yes| C[Hmm...]; + C --> D[Debug]; + D --> B; + B ---->|No| E[Yay!]; + +.. _sequence diagrams: + +sequence diagrams +----------------------- + +.. code-block:: rst + + .. md-mermaid:: + + sequenceDiagram + Alice->>John: Hello John, how are you? + loop Healthcheck + John->>John: Fight against hypochondria + end + Note right of John: Rational thoughts! + John-->>Alice: Great! + John->>Bob: How about you? + Bob-->>John: Jolly good! + +.. md-mermaid:: + + sequenceDiagram + Alice->>John: Hello John, how are you? + loop Healthcheck + John->>John: Fight against hypochondria + end + Note right of John: Rational thoughts! + John-->>Alice: Great! + John->>Bob: How about you? + Bob-->>John: Jolly good! + +.. _state diagrams: + +state diagrams +-------------------- + +.. code-block:: rst + + .. md-mermaid:: + + stateDiagram-v2 + state fork_state <> + [*] --> fork_state + fork_state --> State2 + fork_state --> State3 + + state join_state <> + State2 --> join_state + State3 --> join_state + join_state --> State4 + State4 --> [*] + +.. md-mermaid:: + + stateDiagram-v2 + state fork_state <> + [*] --> fork_state + fork_state --> State2 + fork_state --> State3 + + state join_state <> + State2 --> join_state + State3 --> join_state + join_state --> State4 + State4 --> [*] + +.. _class diagrams: + +class diagrams +-------------------- + + +.. code-block:: rst + + .. md-mermaid:: + + classDiagram + Person <|-- Student + Person <|-- Professor + Person : +String name + Person : +String phoneNumber + Person : +String emailAddress + Person: +purchaseParkingPass() + Address "1" <-- "0..1" Person:lives at + class Student{ + +int studentNumber + +int averageMark + +isEligibleToEnrol() + +getSeminarsTaken() + } + class Professor{ + +int salary + } + class Address{ + +String street + +String city + +String state + +int postalCode + +String country + -validate() + +outputAsLabel() + } + +.. md-mermaid:: + + classDiagram + Person <|-- Student + Person <|-- Professor + Person : +String name + Person : +String phoneNumber + Person : +String emailAddress + Person: +purchaseParkingPass() + Address "1" <-- "0..1" Person:lives at + class Student{ + +int studentNumber + +int averageMark + +isEligibleToEnrol() + +getSeminarsTaken() + } + class Professor{ + +int salary + } + class Address{ + +String street + +String city + +String state + +int postalCode + +String country + -validate() + +outputAsLabel() + } + +.. _entity-relationship diagrams: + +entity-relationship diagrams +---------------------------------- + + +.. code-block:: rst + + .. md-mermaid:: + + erDiagram + CUSTOMER ||--o{ ORDER : places + ORDER ||--|{ LINE-ITEM : contains + CUSTOMER }|..|{ DELIVERY-ADDRESS : uses + +.. md-mermaid:: + + erDiagram + CUSTOMER ||--o{ ORDER : places + ORDER ||--|{ LINE-ITEM : contains + CUSTOMER }|..|{ DELIVERY-ADDRESS : uses diff --git a/sphinx_immaterial/mermaid_diagrams.py b/sphinx_immaterial/mermaid_diagrams.py new file mode 100644 index 000000000..697b13564 --- /dev/null +++ b/sphinx_immaterial/mermaid_diagrams.py @@ -0,0 +1,47 @@ +"""A custom directive that allows using mermaid diagrams""" +from typing import List +from docutils import nodes +from docutils.parsers.rst import directives +from sphinx.util.docutils import SphinxDirective +from sphinx.application import Sphinx + + +class mermaid_node(nodes.General, nodes.Element): + pass + + +class MermaidDirective(SphinxDirective): + """a special directive""" + + has_content = True + option_spec = { + "name": directives.unchanged, + "class": directives.class_option, + } + + def run(self) -> List[nodes.Node]: + """Run the directive.""" + self.assert_has_content() + diagram = mermaid_node( + "", + classes=["mermaid"] + self.options.get("class", []), + name=self.options.get("name", ""), + ) + self.set_source_info(diagram) + diagram += nodes.raw("", "\n".join(self.content), format="html") + return [diagram] + + +def visit_mermaid(self, node: mermaid_node): + attributes = {"class": " ".join(node["classes"]), "name": node["name"]} + self.body.append(self.starttag(node, "pre", **attributes)) + self.body.append("") + + +def depart_mermaid(self, node: mermaid_node): + self.body.append("") + + +def setup(app: Sphinx): + app.add_directive("md-mermaid", MermaidDirective) + app.add_node(mermaid_node, html=(visit_mermaid, depart_mermaid)) From d2ed612e3d7d30495f9696ff0df5b59179b44481 Mon Sep 17 00:00:00 2001 From: 2bndy5 <2bndy5@gmail.com> Date: Fri, 18 Mar 2022 00:14:28 -0700 Subject: [PATCH 09/25] avoid unknown nodes.raw error revert conf.py a bit --- docs/conf.py | 3 +-- sphinx_immaterial/mermaid_diagrams.py | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 606411991..faf0a15e4 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -34,7 +34,6 @@ "sphinx.ext.todo", "sphinx.ext.mathjax", "sphinx.ext.viewcode", - "sphinx_immaterial", "sphinxcontrib.details.directive", "sphinx_immaterial.content_tabs", "sphinx_immaterial.mermaid_diagrams", @@ -77,7 +76,7 @@ # -- HTML theme settings ------------------------------------------------ -# extensions.append("sphinx_immaterial") +extensions.append("sphinx_immaterial") html_title = "Sphinx-Immaterial" html_theme = "sphinx_immaterial" html_favicon = "_static/images/favicon.ico" # colored version of material/bookshelf.svg diff --git a/sphinx_immaterial/mermaid_diagrams.py b/sphinx_immaterial/mermaid_diagrams.py index 697b13564..ea06a7eae 100644 --- a/sphinx_immaterial/mermaid_diagrams.py +++ b/sphinx_immaterial/mermaid_diagrams.py @@ -26,16 +26,16 @@ def run(self) -> List[nodes.Node]: "", classes=["mermaid"] + self.options.get("class", []), name=self.options.get("name", ""), + content="\n".join(self.content), ) self.set_source_info(diagram) - diagram += nodes.raw("", "\n".join(self.content), format="html") return [diagram] def visit_mermaid(self, node: mermaid_node): attributes = {"class": " ".join(node["classes"]), "name": node["name"]} self.body.append(self.starttag(node, "pre", **attributes)) - self.body.append("") + self.body.append("\n" + node["content"]) def depart_mermaid(self, node: mermaid_node): From d26b637900495ba5a799fab487250917231caf30 Mon Sep 17 00:00:00 2001 From: 2bndy5 <2bndy5@gmail.com> Date: Fri, 18 Mar 2022 00:30:15 -0700 Subject: [PATCH 10/25] fix mermaid directive for pseudo latex output --- sphinx_immaterial/mermaid_diagrams.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/sphinx_immaterial/mermaid_diagrams.py b/sphinx_immaterial/mermaid_diagrams.py index ea06a7eae..35237d0df 100644 --- a/sphinx_immaterial/mermaid_diagrams.py +++ b/sphinx_immaterial/mermaid_diagrams.py @@ -1,4 +1,5 @@ """A custom directive that allows using mermaid diagrams""" +from heapq import merge from typing import List from docutils import nodes from docutils.parsers.rst import directives @@ -38,10 +39,19 @@ def visit_mermaid(self, node: mermaid_node): self.body.append("\n" + node["content"]) +def visit_mermaid_latex(self, node: mermaid_node): + self.body.append("
")
+    self.body.append("\n" + node["content"])
+
+
 def depart_mermaid(self, node: mermaid_node):
     self.body.append("
") def setup(app: Sphinx): app.add_directive("md-mermaid", MermaidDirective) - app.add_node(mermaid_node, html=(visit_mermaid, depart_mermaid)) + app.add_node( + mermaid_node, + html=(visit_mermaid, depart_mermaid), + latex=(visit_mermaid_latex, depart_mermaid), + ) From ec144b0161b1da8cff0263f620f8396af15d250a Mon Sep 17 00:00:00 2001 From: 2bndy5 <2bndy5@gmail.com> Date: Fri, 18 Mar 2022 00:33:01 -0700 Subject: [PATCH 11/25] pylance's auto-import is annoying for typos --- sphinx_immaterial/mermaid_diagrams.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sphinx_immaterial/mermaid_diagrams.py b/sphinx_immaterial/mermaid_diagrams.py index 35237d0df..4e867c3c4 100644 --- a/sphinx_immaterial/mermaid_diagrams.py +++ b/sphinx_immaterial/mermaid_diagrams.py @@ -1,5 +1,4 @@ """A custom directive that allows using mermaid diagrams""" -from heapq import merge from typing import List from docutils import nodes from docutils.parsers.rst import directives From 64641ad4879eee7d3c808a7ecf5868385135543c Mon Sep 17 00:00:00 2001 From: 2bndy5 <2bndy5@gmail.com> Date: Fri, 18 Mar 2022 02:16:32 -0700 Subject: [PATCH 12/25] fix html/latex output for mermaid diagrams --- docs/mermaid_diagrams.rst | 43 +++++++++++++-------------- sphinx_immaterial/mermaid_diagrams.py | 38 +++++++++++++---------- 2 files changed, 44 insertions(+), 37 deletions(-) diff --git a/docs/mermaid_diagrams.rst b/docs/mermaid_diagrams.rst index acd9e4a9d..e86e75208 100644 --- a/docs/mermaid_diagrams.rst +++ b/docs/mermaid_diagrams.rst @@ -19,29 +19,27 @@ directive that exposes the underlying implementation in mkdocs-material theme. extensions = ["sphinx_immaterial", "sphinx_immaterial.mermaid_diagrams"] - The `md-mermaid` directive does support the ``:class:`` option, but it has no affect after - the graph is generated. + The `md-mermaid` directive does support the ``:class:`` and ``:name:`` options which can used + as respective class and id specifiers in custom CSS. This theme comes with CSS styling that conforms to the chosen primary & accent colors - (based on the selected scheme). To modify the CSS for a particular graph, you should use the - mechanics available in `mermaid.js`_. For example, each graph's node has a unique ``id`` - and generic ``class`` attribute that can be used in a CSS rule's selector. + (based on the selected scheme). .. md-admonition:: :class: missing While all `mermaid.js`_ features should work out-of-the-box, This theme will currently only - adjust the fonts and colors for `flowcharts`_, `sequence diagrams`_, `class diagrams`_, - `state diagrams`_ and `entity-relationship diagrams`_. + adjust the fonts and colors for `flowcharts`_, `sequence diagrams `, + `class diagrams `, `state diagrams `, and + `entity-relationship diagrams `. -.. _flowcharts: - -flowcharts +Using flowcharts ---------------- .. code-block:: rst .. md-mermaid:: + :name: flowcharts graph LR A[Start] --> B{Error?}; @@ -51,6 +49,7 @@ flowcharts B ---->|No| E[Yay!]; .. md-mermaid:: + :name: flowcharts graph LR A[Start] --> B{Error?}; @@ -59,14 +58,13 @@ flowcharts D --> B; B ---->|No| E[Yay!]; -.. _sequence diagrams: - -sequence diagrams +Using sequence diagrams ----------------------- .. code-block:: rst .. md-mermaid:: + :name: sequence-diagrams sequenceDiagram Alice->>John: Hello John, how are you? @@ -79,6 +77,7 @@ sequence diagrams Bob-->>John: Jolly good! .. md-mermaid:: + :name: sequence-diagrams sequenceDiagram Alice->>John: Hello John, how are you? @@ -90,14 +89,13 @@ sequence diagrams John->>Bob: How about you? Bob-->>John: Jolly good! -.. _state diagrams: - -state diagrams +Using state diagrams -------------------- .. code-block:: rst .. md-mermaid:: + :name: state-diagrams stateDiagram-v2 state fork_state <> @@ -112,6 +110,7 @@ state diagrams State4 --> [*] .. md-mermaid:: + :name: state-diagrams stateDiagram-v2 state fork_state <> @@ -125,15 +124,14 @@ state diagrams join_state --> State4 State4 --> [*] -.. _class diagrams: - -class diagrams +Using class diagrams -------------------- .. code-block:: rst .. md-mermaid:: + :name: class-diagrams classDiagram Person <|-- Student @@ -163,6 +161,7 @@ class diagrams } .. md-mermaid:: + :name: class-diagrams classDiagram Person <|-- Student @@ -191,15 +190,14 @@ class diagrams +outputAsLabel() } -.. _entity-relationship diagrams: - -entity-relationship diagrams +Using entity-relationship diagrams ---------------------------------- .. code-block:: rst .. md-mermaid:: + :name: entity-relationship-diagrams erDiagram CUSTOMER ||--o{ ORDER : places @@ -207,6 +205,7 @@ entity-relationship diagrams CUSTOMER }|..|{ DELIVERY-ADDRESS : uses .. md-mermaid:: + :name: entity-relationship-diagrams erDiagram CUSTOMER ||--o{ ORDER : places diff --git a/sphinx_immaterial/mermaid_diagrams.py b/sphinx_immaterial/mermaid_diagrams.py index 4e867c3c4..aa9cac6ee 100644 --- a/sphinx_immaterial/mermaid_diagrams.py +++ b/sphinx_immaterial/mermaid_diagrams.py @@ -2,7 +2,7 @@ from typing import List from docutils import nodes from docutils.parsers.rst import directives -from sphinx.util.docutils import SphinxDirective +from sphinx.util.docutils import SphinxDirective, is_node_registered from sphinx.application import Sphinx @@ -22,28 +22,36 @@ class MermaidDirective(SphinxDirective): def run(self) -> List[nodes.Node]: """Run the directive.""" self.assert_has_content() - diagram = mermaid_node( + content = "\n".join(self.content) + diagram = mermaid_node("", classes=["mermaid"], content=content) + diagram += nodes.literal("", content, format="html") + diagram_div = nodes.container( "", - classes=["mermaid"] + self.options.get("class", []), - name=self.options.get("name", ""), - content="\n".join(self.content), + is_div=True, + classes=["mermaid-diagram"] + self.options.get("class", []), ) - self.set_source_info(diagram) - return [diagram] + if self.options.get("name", ""): + self.add_name(diagram_div) + diagram_div += diagram + self.set_source_info(diagram_div) + return [diagram_div] -def visit_mermaid(self, node: mermaid_node): - attributes = {"class": " ".join(node["classes"]), "name": node["name"]} +def visit_mermaid_node_html(self, node: mermaid_node): + attributes = {"class": "mermaid"} self.body.append(self.starttag(node, "pre", **attributes)) - self.body.append("\n" + node["content"]) -def visit_mermaid_latex(self, node: mermaid_node): - self.body.append("
")
+def depart_mermaid_node_html(self, node: mermaid_node):
+    self.body.append("
") + + +def visit_mermaid_node_latex(self, node: mermaid_node): + self.body.append('
')
     self.body.append("\n" + node["content"])
 
 
-def depart_mermaid(self, node: mermaid_node):
+def depart_mermaid_node_latex(self, node: mermaid_node):
     self.body.append("
") @@ -51,6 +59,6 @@ def setup(app: Sphinx): app.add_directive("md-mermaid", MermaidDirective) app.add_node( mermaid_node, - html=(visit_mermaid, depart_mermaid), - latex=(visit_mermaid_latex, depart_mermaid), + html=(visit_mermaid_node_html, depart_mermaid_node_html), + latex=(visit_mermaid_node_latex, depart_mermaid_node_latex), ) From b8be76c064594a50f29165db4bb9e5aaa2e81240 Mon Sep 17 00:00:00 2001 From: 2bndy5 <2bndy5@gmail.com> Date: Fri, 18 Mar 2022 02:21:47 -0700 Subject: [PATCH 13/25] cleaning it up a bit --- sphinx_immaterial/mermaid_diagrams.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/sphinx_immaterial/mermaid_diagrams.py b/sphinx_immaterial/mermaid_diagrams.py index aa9cac6ee..e7068cfd5 100644 --- a/sphinx_immaterial/mermaid_diagrams.py +++ b/sphinx_immaterial/mermaid_diagrams.py @@ -2,7 +2,7 @@ from typing import List from docutils import nodes from docutils.parsers.rst import directives -from sphinx.util.docutils import SphinxDirective, is_node_registered +from sphinx.util.docutils import SphinxDirective from sphinx.application import Sphinx @@ -42,23 +42,18 @@ def visit_mermaid_node_html(self, node: mermaid_node): self.body.append(self.starttag(node, "pre", **attributes)) -def depart_mermaid_node_html(self, node: mermaid_node): +def depart_mermaid_node(self, node: mermaid_node): self.body.append("") def visit_mermaid_node_latex(self, node: mermaid_node): self.body.append('
')
-    self.body.append("\n" + node["content"])
-
-
-def depart_mermaid_node_latex(self, node: mermaid_node):
-    self.body.append("
") def setup(app: Sphinx): app.add_directive("md-mermaid", MermaidDirective) app.add_node( mermaid_node, - html=(visit_mermaid_node_html, depart_mermaid_node_html), - latex=(visit_mermaid_node_latex, depart_mermaid_node_latex), + html=(visit_mermaid_node_html, depart_mermaid_node), + latex=(visit_mermaid_node_latex, depart_mermaid_node), ) From cee663b70879a289a7d2fe9ef794578209781668 Mon Sep 17 00:00:00 2001 From: 2bndy5 <2bndy5@gmail.com> Date: Fri, 18 Mar 2022 02:31:18 -0700 Subject: [PATCH 14/25] auto add directives that are tied to our CSS --- docs/conf.py | 2 -- docs/content_tabs.rst | 5 ----- docs/mermaid_diagrams.rst | 6 ------ sphinx_immaterial/__init__.py | 4 +++- 4 files changed, 3 insertions(+), 14 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index faf0a15e4..212e58cc9 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -35,8 +35,6 @@ "sphinx.ext.mathjax", "sphinx.ext.viewcode", "sphinxcontrib.details.directive", - "sphinx_immaterial.content_tabs", - "sphinx_immaterial.mermaid_diagrams", ] intersphinx_mapping = { diff --git a/docs/content_tabs.rst b/docs/content_tabs.rst index 807a24a66..8815a5b02 100644 --- a/docs/content_tabs.rst +++ b/docs/content_tabs.rst @@ -10,11 +10,6 @@ Use of `content tabs in mkdocs-material Date: Fri, 18 Mar 2022 03:55:05 -0700 Subject: [PATCH 15/25] docs proofreading --- docs/admonitions.rst | 2 +- docs/content_tabs.rst | 6 +++--- docs/mermaid_diagrams.rst | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/admonitions.rst b/docs/admonitions.rst index 0f3a475ac..b96c24a9a 100644 --- a/docs/admonitions.rst +++ b/docs/admonitions.rst @@ -42,7 +42,7 @@ shown inside the demonstrated admonition. .. important:: The ``:class:`` options below (in the rST code blocks) must use lower case letters for the - styling to work. Otherwise, the admonition will look like a ``note`` (as that is the + styling to work. Otherwise, the admonition will look like a `note` (as that is the default fallback style). ``todo``, ``info`` diff --git a/docs/content_tabs.rst b/docs/content_tabs.rst index 8815a5b02..0d1cb7c7a 100644 --- a/docs/content_tabs.rst +++ b/docs/content_tabs.rst @@ -24,7 +24,7 @@ the sphinx-immaterial theme provides its own directives to make use of content t .. confval:: md-tab-set Each set of tabs on a page must begin with a `md-tab-set` directive. This directive - only accepts children that `md-tab-item` directives. + only accepts children that are `md-tab-item` directives. This directive supports ``:class:`` and ``:name:`` options to use custom CSS classes and reference links (respectively) @@ -47,7 +47,7 @@ the sphinx-immaterial theme provides its own directives to make use of content t To cross-reference this tab set from a different page, use :ref:`tab set description ` - Clearly this also works on the same page. + Clearly, this also works on the same page. .. md-tab-item:: Custom CSS @@ -72,7 +72,7 @@ the sphinx-immaterial theme provides its own directives to make use of content t To cross-reference this tab set from a different page, use :ref:`tab set description ` - Clearly this also works on the same page as the tab set. + Clearly, this also works on the same page as the tab set. .. md-tab-item:: Custom CSS diff --git a/docs/mermaid_diagrams.rst b/docs/mermaid_diagrams.rst index d0986b7e5..6b60678f9 100644 --- a/docs/mermaid_diagrams.rst +++ b/docs/mermaid_diagrams.rst @@ -22,7 +22,7 @@ directive that exposes the underlying implementation in mkdocs-material theme. .. md-admonition:: :class: missing - While all `mermaid.js`_ features should work out-of-the-box, This theme will currently only + While all `mermaid.js`_ features should work out-of-the-box, this theme will currently only adjust the fonts and colors for `flowcharts`_, `sequence diagrams `, `class diagrams `, `state diagrams `, and `entity-relationship diagrams `. From 837e1e357b9bfbc07ec65ebb98a2ea9a90c7c36e Mon Sep 17 00:00:00 2001 From: 2bndy5 <2bndy5@gmail.com> Date: Fri, 18 Mar 2022 16:10:56 -0700 Subject: [PATCH 16/25] some simple review changes --- docs/content_tabs.rst | 71 +++++++++++++++++++++++++-------------- docs/mermaid_diagrams.rst | 23 ++++++++++--- setup.py | 3 +- 3 files changed, 66 insertions(+), 31 deletions(-) diff --git a/docs/content_tabs.rst b/docs/content_tabs.rst index 0d1cb7c7a..a0ba7a1ce 100644 --- a/docs/content_tabs.rst +++ b/docs/content_tabs.rst @@ -4,9 +4,9 @@ Content tabs ============ .. note:: - This document focused on content tabs, not navigation tabs. + This document discusses content tabs, not navigation tabs. -Use of `content tabs in mkdocs-material `_ +Use of `content tabs in the mkdocs-material `_ theme relies on a markdown extension that isn't used in the world of Sphinx. Instead, the sphinx-immaterial theme provides its own directives to make use of content tabs. @@ -14,18 +14,31 @@ the sphinx-immaterial theme provides its own directives to make use of content t :class: missing The `linked content tabs `_ - seen in mkdocs-material is not supported until that feature transitions from the mkdocs-material theme's insider - releases to it's public releases. + feature seen in mkdocs-material is not supported until that feature transitions from the mkdocs-material theme's insider + releases to its public releases. - However, you can use other sphinx extensions (like `sphinx-design tabs`_) to achieve this functionality. - Although, other extensions will require some custom CSS styling to match the mkdocs-material + You can use other sphinx extensions (like `sphinx-design tabs`_) to achieve this functionality. + However, other extensions will require some custom CSS styling to match the mkdocs-material theme's styling for content tabs. -.. confval:: md-tab-set +.. rst:directive:: md-tab-set Each set of tabs on a page must begin with a `md-tab-set` directive. This directive only accepts children that are `md-tab-item` directives. + .. rst:directive:option:: class + :type: string + + A space delimited list of qualified names that get used as the HTMl element's + ``class`` attribute. + + .. rst:directive:option:: name + :type: string + + A qualified name that get used as the HTML element's ``id`` attribute. + + Use the `ref` role to reference the element by name. + This directive supports ``:class:`` and ``:name:`` options to use custom CSS classes and reference links (respectively) @@ -81,13 +94,35 @@ the sphinx-immaterial theme provides its own directives to make use of content t :start-at: /* ************************ custom-tab-set-style :end-before: /* *********************** custom-tab-item-style -.. confval:: md-tab-item +.. rst:directive:: md-tab-item This directive is used to create a tab within a set of content tabs. It requires a - label as it's argument. Additionally, it also supports the ``:class:`` option, to - optionally provide custom CSS classes to the tab's content (not the tab's label). + label as it's argument. - .. code-block:: rst + .. rst:directive:option:: class + :type: string + + A space delimited list of qualified names that get used as the HTMl element's + ``class`` attribute. + + Use the ``:class:`` option to optionally provide custom CSS classes to the tab's content + (not the tab's label). + + .. code-block:: rst + + .. md-tab-set:: + + .. md-tab-item:: Customized content + :class: custom-tab-item-style + + This content could be styled differently from other page content. + + .. md-tab-item:: Custom CSS + + .. literalinclude:: _static/extra_css.css + :language: css + :start-at: /* *********************** custom-tab-item-style + :end-before: /* ************************* inline icon stuff .. md-tab-set:: @@ -103,20 +138,6 @@ the sphinx-immaterial theme provides its own directives to make use of content t :start-at: /* *********************** custom-tab-item-style :end-before: /* ************************* inline icon stuff - .. md-tab-set:: - - .. md-tab-item:: Customized content - :class: custom-tab-item-style - - This content could be styled differently from other page content. - - .. md-tab-item:: Custom CSS - - .. literalinclude:: _static/extra_css.css - :language: css - :start-at: /* *********************** custom-tab-item-style - :end-before: /* ************************* inline icon stuff - Typical examples are seen in this documentations' `Custom admonitions `_ and :ref:`Version Information Structure ` sections. \ No newline at end of file diff --git a/docs/mermaid_diagrams.rst b/docs/mermaid_diagrams.rst index 6b60678f9..7401d0908 100644 --- a/docs/mermaid_diagrams.rst +++ b/docs/mermaid_diagrams.rst @@ -8,16 +8,29 @@ Mermaid diagrams The mkdocs-material theme is equipped to make use of diagrams generated (during page load time) with `mermaid.js`_. Although, its implementation relies on a markdown extension that does not get -used by this sphinx-immaterial theme. Thus, the sphinx-immaterial theme provides with an optional +used by this sphinx-immaterial theme. Thus, the sphinx-immaterial theme provides an optional directive that exposes the underlying implementation in mkdocs-material theme. -.. confval:: md-mermaid +.. rst:directive:: md-mermaid - The `md-mermaid` directive does support the ``:class:`` and ``:name:`` options which can used + .. rst:directive:option:: class + :type: string + + A space delimited list of qualified names that get used as the HTMl element's + ``class`` attribute. + + .. rst:directive:option:: name + :type: string + + A qualified name that get used as the HTML element's ``id`` attribute. + + Use the `ref` role to reference the element by name. + + The `md-mermaid` directive's ``:class:`` and ``:name:`` options can be used as respective class and id specifiers in custom CSS. - This theme comes with CSS styling that conforms to the chosen primary & accent colors - (based on the selected scheme). + This theme comes with CSS styling that conforms to the chosen `primary` & `accent` colors + (based on the selected `scheme`). .. md-admonition:: :class: missing diff --git a/setup.py b/setup.py index 10cec1824..605f0306e 100644 --- a/setup.py +++ b/setup.py @@ -17,13 +17,14 @@ # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +import setuptools # pylint: disable=wrong-import-order + import atexit import distutils.command.build import os import subprocess import tempfile -import setuptools import setuptools.command.build_py import setuptools.command.develop import setuptools.command.install From 6aa7e5e4df49b9ad707120578ed6ec0ae12070e5 Mon Sep 17 00:00:00 2001 From: 2bndy5 <2bndy5@gmail.com> Date: Fri, 18 Mar 2022 18:29:21 -0700 Subject: [PATCH 17/25] fix doc typos --- docs/content_tabs.rst | 2 +- docs/mermaid_diagrams.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/content_tabs.rst b/docs/content_tabs.rst index a0ba7a1ce..85a2cbd66 100644 --- a/docs/content_tabs.rst +++ b/docs/content_tabs.rst @@ -40,7 +40,7 @@ the sphinx-immaterial theme provides its own directives to make use of content t Use the `ref` role to reference the element by name. This directive supports ``:class:`` and ``:name:`` options to use custom CSS classes - and reference links (respectively) + and reference links (respectively). .. code-block:: rst diff --git a/docs/mermaid_diagrams.rst b/docs/mermaid_diagrams.rst index 7401d0908..59328cf65 100644 --- a/docs/mermaid_diagrams.rst +++ b/docs/mermaid_diagrams.rst @@ -16,7 +16,7 @@ directive that exposes the underlying implementation in mkdocs-material theme. .. rst:directive:option:: class :type: string - A space delimited list of qualified names that get used as the HTMl element's + A space delimited list of qualified names that get used as the HTML element's ``class`` attribute. .. rst:directive:option:: name From 61f0e5e406535e27d217474dfa7ff004843c1b2f Mon Sep 17 00:00:00 2001 From: 2bndy5 <2bndy5@gmail.com> Date: Fri, 18 Mar 2022 20:30:55 -0700 Subject: [PATCH 18/25] visit_tab_set is the transformer --- sphinx_immaterial/content_tabs.py | 117 +++++++++++++----------------- 1 file changed, 52 insertions(+), 65 deletions(-) diff --git a/sphinx_immaterial/content_tabs.py b/sphinx_immaterial/content_tabs.py index 98eaf393f..45f844fa5 100644 --- a/sphinx_immaterial/content_tabs.py +++ b/sphinx_immaterial/content_tabs.py @@ -5,17 +5,14 @@ from docutils import nodes from docutils.parsers.rst import directives from sphinx.util.docutils import SphinxDirective -from sphinx.util.nodes import NodeMatcher from sphinx.application import Sphinx from sphinx.util.logging import getLogger -from sphinx.transforms.post_transforms import SphinxPostTransform +from sphinx.writers.html import HTMLTranslator - -page_tab_set_counter = [0] LOGGER = getLogger(__name__) -def is_component(node: nodes.Node, name: str): +def is_md_tab_type(node: nodes.Node, name: str): """Check if a node is a certain tabbed component.""" try: return node.get("type") == name @@ -48,18 +45,17 @@ def run(self) -> List[nodes.Node]: self.set_source_info(tab_set) if self.options.get("name", ""): self.add_name(tab_set) - page_tab_set_counter[0] += 1 self.state.nested_parse(self.content, self.content_offset, tab_set) tab_total = 0 for item in tab_set.children: - if not is_component(item, "md-tab-item"): + if not is_md_tab_type(item, "md-tab-item"): LOGGER.warning( "All children of a 'md-tab-set' should be 'md-tab-item'", location=item, ) break tab_total += 1 - tab_set["data-tabs"] = f"{page_tab_set_counter[0]}:{tab_total}" + tab_set["data-tabs"] = f":{tab_total}" return [tab_set] @@ -87,7 +83,7 @@ class MaterialTabItemDirective(SphinxDirective): def run(self) -> List[nodes.Node]: """Run the directive.""" self.assert_has_content() - if not is_component(self.state_machine.node, "md-tab-set"): + if not is_md_tab_type(self.state_machine.node, "md-tab-set"): LOGGER.warning( "The parent of a 'md-tab-item' should be a 'md-tab-set'", location=(self.env.docname, self.lineno), @@ -147,74 +143,65 @@ def depart_tab_label(self, node): self.body.append("") -def visit_tab_set(self, node): - attributes = {"data-tabs": node["data-tabs"]} - self.body.append(self.starttag(node, "div", **attributes)) - +def visit_tab_set(self: HTMLTranslator, node: content_tab_set): + # increment tab set counter + self.tab_set_count = getattr(self, "tab_set_count", 0) + 1 -def depart_tab_set(self, node): - self.body.append("") + # configure tab set's div attributes + tab_set_identity = "__tabbed_" + str(self.tab_set_count) + attributes = {"data-tabs": str(self.tab_set_count) + node["data-tabs"]} + node["set_id"] = tab_set_identity + self.body.append(self.starttag(node, "div", **attributes)) + # walkabout the children + tab_total = 0 + children = [] + tab_label_div = nodes.container("", is_div=True, classes=["tabbed-labels"]) + tab_content_div = nodes.container("", is_div=True, classes=["tabbed-content"]) + for tab_item in node.children: + try: + tab_label, tab_block = tab_item.children + except ValueError as exc: + raise ValueError(f"md-tab-item has no children:\n{repr(tab_item)}") from exc + tab_total += 1 + tab_item_identity = tab_set_identity + f"_{tab_total}" + + # create: + input_node = content_tab_input( + "", + id=tab_item_identity, + set_id=tab_set_identity, + type="radio", + checked=(tab_total == 1), + ) + input_node.source, input_node.line = tab_item.source, tab_item.line + children.append(input_node) -class MaterialTabSetHtmlTransform(SphinxPostTransform): - """Transform tab-set to HTML specific AST structure.""" + # create: + label_node = content_tab_label( + "", + *tab_label.children, + input_id=tab_item_identity, + classes=tab_label["classes"], + ) + label_node.source, label_node.line = tab_item.source, tab_item.line + tab_label_div += label_node - default_priority = 200 - formats = ("html",) + # add content + tab_content_div += tab_block - def run(self, **kwargs: Any) -> None: - """Run the transform.""" - matcher = NodeMatcher(nodes.container, type="md-tab-set") - for set_numb, tab_set in enumerate(self.document.traverse(matcher)): - tab_set_identity = f"__tabbed_{set_numb + 1}" - children = [] - tab_label_div = nodes.container("", is_div=True, classes=["tabbed-labels"]) - tab_content_div = nodes.container( - "", is_div=True, classes=["tabbed-content"] - ) - tab_total = 0 - for tab_item in tab_set.children: - try: - tab_label, tab_block = tab_item.children - except ValueError: - print(tab_item) - raise - tab_total += 1 - tab_item_identity = tab_set_identity + f"_{tab_total}" - - # create: - input_node = content_tab_input( - "", - id=tab_item_identity, - set_id=tab_set_identity, - type="radio", - checked=(tab_total == 1), - ) - input_node.source, input_node.line = tab_item.source, tab_item.line - children.append(input_node) - - # create: - label_node = content_tab_label( - "", - *tab_label.children, - input_id=tab_item_identity, - classes=tab_label["classes"], - ) - label_node.source, label_node.line = tab_item.source, tab_item.line - tab_label_div += label_node + node.children = children + node += tab_label_div + node += tab_content_div - # add content - tab_content_div += tab_block - tab_set.children = children - tab_set += tab_label_div - tab_set += tab_content_div +def depart_tab_set(self, node): + self.body.append("") def setup(app: Sphinx) -> None: app.add_directive("md-tab-set", MaterialTabSetDirective) app.add_directive("md-tab-item", MaterialTabItemDirective) - app.add_post_transform(MaterialTabSetHtmlTransform) app.add_node(content_tab_input, html=(visit_tab_input, depart_tab_input)) app.add_node(content_tab_label, html=(visit_tab_label, depart_tab_label)) app.add_node(content_tab_set, html=(visit_tab_set, depart_tab_set)) From 67668911a7948e555ac88b5c4d4476d24e3d8450 Mon Sep 17 00:00:00 2001 From: 2bndy5 <2bndy5@gmail.com> Date: Fri, 18 Mar 2022 20:34:02 -0700 Subject: [PATCH 19/25] remove unused import --- sphinx_immaterial/content_tabs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx_immaterial/content_tabs.py b/sphinx_immaterial/content_tabs.py index 45f844fa5..df693f5ce 100644 --- a/sphinx_immaterial/content_tabs.py +++ b/sphinx_immaterial/content_tabs.py @@ -1,7 +1,7 @@ """ A special theme-specific extension to support "content tabs" from mkdocs-material. """ -from typing import List, Any +from typing import List from docutils import nodes from docutils.parsers.rst import directives from sphinx.util.docutils import SphinxDirective From 6900588a7ffd1894e8b10ac81037a70cc6d3f451 Mon Sep 17 00:00:00 2001 From: 2bndy5 <2bndy5@gmail.com> Date: Fri, 18 Mar 2022 22:43:14 -0700 Subject: [PATCH 20/25] use better pseudo latex output for mermaid graphs --- sphinx_immaterial/mermaid_diagrams.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/sphinx_immaterial/mermaid_diagrams.py b/sphinx_immaterial/mermaid_diagrams.py index e7068cfd5..530d39907 100644 --- a/sphinx_immaterial/mermaid_diagrams.py +++ b/sphinx_immaterial/mermaid_diagrams.py @@ -42,18 +42,22 @@ def visit_mermaid_node_html(self, node: mermaid_node): self.body.append(self.starttag(node, "pre", **attributes)) -def depart_mermaid_node(self, node: mermaid_node): +def depart_mermaid_node_html(self, node: mermaid_node): self.body.append("") def visit_mermaid_node_latex(self, node: mermaid_node): - self.body.append('
')
+    self.body.append("\n\\begin{sphinxVerbatim}[commandchars=\\\\\\{\\}]\n")
+
+
+def depart_mermaid_node_latex(self, node: mermaid_node):
+    self.body.append("\n\\end{sphinxVerbatim}\n")
 
 
 def setup(app: Sphinx):
     app.add_directive("md-mermaid", MermaidDirective)
     app.add_node(
         mermaid_node,
-        html=(visit_mermaid_node_html, depart_mermaid_node),
-        latex=(visit_mermaid_node_latex, depart_mermaid_node),
+        html=(visit_mermaid_node_html, depart_mermaid_node_html),
+        latex=(visit_mermaid_node_latex, depart_mermaid_node_latex),
     )

From 9225a4a66eb2c73254f94b45eda8e8341124ba5e Mon Sep 17 00:00:00 2001
From: 2bndy5 <2bndy5@gmail.com>
Date: Sat, 19 Mar 2022 13:49:40 -0700
Subject: [PATCH 21/25] append input elements literally; use len(children)

---
 sphinx_immaterial/content_tabs.py | 37 +++++++------------------------
 1 file changed, 8 insertions(+), 29 deletions(-)

diff --git a/sphinx_immaterial/content_tabs.py b/sphinx_immaterial/content_tabs.py
index df693f5ce..623d8d1c3 100644
--- a/sphinx_immaterial/content_tabs.py
+++ b/sphinx_immaterial/content_tabs.py
@@ -46,7 +46,6 @@ def run(self) -> List[nodes.Node]:
         if self.options.get("name", ""):
             self.add_name(tab_set)
         self.state.nested_parse(self.content, self.content_offset, tab_set)
-        tab_total = 0
         for item in tab_set.children:
             if not is_md_tab_type(item, "md-tab-item"):
                 LOGGER.warning(
@@ -54,8 +53,6 @@ def run(self) -> List[nodes.Node]:
                     location=item,
                 )
                 break
-            tab_total += 1
-        tab_set["data-tabs"] = f":{tab_total}"
         return [tab_set]
 
 
@@ -123,17 +120,6 @@ class content_tab_label(nodes.TextElement, nodes.General):
     pass
 
 
-def visit_tab_input(self, node):
-    attributes = {"ids": [node["id"]], "type": node["type"], "name": node["set_id"]}
-    if node["checked"]:
-        attributes["checked"] = "checked"
-    self.body.append(self.starttag(node, "input", **attributes))
-
-
-def depart_tab_input(self, node):
-    self.body.append("")
-
-
 def visit_tab_label(self, node):
     attributes = {"for": node["input_id"]}
     self.body.append(self.starttag(node, "label", **attributes))
@@ -148,14 +134,13 @@ def visit_tab_set(self: HTMLTranslator, node: content_tab_set):
     self.tab_set_count = getattr(self, "tab_set_count", 0) + 1
 
     # configure tab set's div attributes
-    tab_set_identity = "__tabbed_" + str(self.tab_set_count)
-    attributes = {"data-tabs": str(self.tab_set_count) + node["data-tabs"]}
+    tab_set_identity = f"__tabbed_{self.tab_set_count}"
+    attributes = {"data-tabs": f"{self.tab_set_count}:{len(node.children)}"}
     node["set_id"] = tab_set_identity
     self.body.append(self.starttag(node, "div", **attributes))
 
     # walkabout the children
     tab_total = 0
-    children = []
     tab_label_div = nodes.container("", is_div=True, classes=["tabbed-labels"])
     tab_content_div = nodes.container("", is_div=True, classes=["tabbed-content"])
     for tab_item in node.children:
@@ -163,19 +148,15 @@ def visit_tab_set(self: HTMLTranslator, node: content_tab_set):
             tab_label, tab_block = tab_item.children
         except ValueError as exc:
             raise ValueError(f"md-tab-item has no children:\n{repr(tab_item)}") from exc
-        tab_total += 1
-        tab_item_identity = tab_set_identity + f"_{tab_total}"
+        tab_item_identity = tab_set_identity + f"_{tab_total + 1}"
 
         # create: 
-        input_node = content_tab_input(
-            "",
-            id=tab_item_identity,
-            set_id=tab_set_identity,
-            type="radio",
-            checked=(tab_total == 1),
+        self.body.append(
+            "'
         )
-        input_node.source, input_node.line = tab_item.source, tab_item.line
-        children.append(input_node)
+        tab_total += 1
 
         # create: 
         label_node = content_tab_label(
@@ -190,7 +171,6 @@ def visit_tab_set(self: HTMLTranslator, node: content_tab_set):
         # add content
         tab_content_div += tab_block
 
-    node.children = children
     node += tab_label_div
     node += tab_content_div
 
@@ -202,6 +182,5 @@ def depart_tab_set(self, node):
 def setup(app: Sphinx) -> None:
     app.add_directive("md-tab-set", MaterialTabSetDirective)
     app.add_directive("md-tab-item", MaterialTabItemDirective)
-    app.add_node(content_tab_input, html=(visit_tab_input, depart_tab_input))
     app.add_node(content_tab_label, html=(visit_tab_label, depart_tab_label))
     app.add_node(content_tab_set, html=(visit_tab_set, depart_tab_set))

From a07a3b5b5c17f755abe175c88b78092938c5e581 Mon Sep 17 00:00:00 2001
From: 2bndy5 <2bndy5@gmail.com>
Date: Sat, 19 Mar 2022 14:12:06 -0700
Subject: [PATCH 22/25] remove setting useless `set_id` attribute

---
 sphinx_immaterial/content_tabs.py | 1 -
 1 file changed, 1 deletion(-)

diff --git a/sphinx_immaterial/content_tabs.py b/sphinx_immaterial/content_tabs.py
index 623d8d1c3..de866f645 100644
--- a/sphinx_immaterial/content_tabs.py
+++ b/sphinx_immaterial/content_tabs.py
@@ -136,7 +136,6 @@ def visit_tab_set(self: HTMLTranslator, node: content_tab_set):
     # configure tab set's div attributes
     tab_set_identity = f"__tabbed_{self.tab_set_count}"
     attributes = {"data-tabs": f"{self.tab_set_count}:{len(node.children)}"}
-    node["set_id"] = tab_set_identity
     self.body.append(self.starttag(node, "div", **attributes))
 
     # walkabout the children

From eb36f50876fee1fea1ef2e0ef9800a7dafee54c9 Mon Sep 17 00:00:00 2001
From: 2bndy5 <2bndy5@gmail.com>
Date: Sat, 19 Mar 2022 14:24:42 -0700
Subject: [PATCH 23/25] use SkipNode after rendering a tab set's divs

---
 sphinx_immaterial/content_tabs.py | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/sphinx_immaterial/content_tabs.py b/sphinx_immaterial/content_tabs.py
index de866f645..f91117439 100644
--- a/sphinx_immaterial/content_tabs.py
+++ b/sphinx_immaterial/content_tabs.py
@@ -170,8 +170,9 @@ def visit_tab_set(self: HTMLTranslator, node: content_tab_set):
         # add content
         tab_content_div += tab_block
 
-    node += tab_label_div
-    node += tab_content_div
+    tab_label_div.walkabout(self)
+    tab_content_div.walkabout(self)
+    raise nodes.SkipNode()
 
 
 def depart_tab_set(self, node):

From a0f1ef1f41cc96a70798164d47978a48376ddf36 Mon Sep 17 00:00:00 2001
From: 2bndy5 <2bndy5@gmail.com>
Date: Sat, 19 Mar 2022 14:55:26 -0700
Subject: [PATCH 24/25] remove artifact class

---
 sphinx_immaterial/content_tabs.py | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/sphinx_immaterial/content_tabs.py b/sphinx_immaterial/content_tabs.py
index f91117439..d1e909193 100644
--- a/sphinx_immaterial/content_tabs.py
+++ b/sphinx_immaterial/content_tabs.py
@@ -112,10 +112,6 @@ def run(self) -> List[nodes.Node]:
         return [tab_item]
 
 
-class content_tab_input(nodes.Element, nodes.General):
-    pass
-
-
 class content_tab_label(nodes.TextElement, nodes.General):
     pass
 

From 781a0d9ae8026cb42979fd89029ae69d8a50fbee Mon Sep 17 00:00:00 2001
From: 2bndy5 <2bndy5@gmail.com>
Date: Sat, 19 Mar 2022 18:05:39 -0700
Subject: [PATCH 25/25] 3rd round review changes

---
 sphinx_immaterial/content_tabs.py | 15 ++++++---------
 1 file changed, 6 insertions(+), 9 deletions(-)

diff --git a/sphinx_immaterial/content_tabs.py b/sphinx_immaterial/content_tabs.py
index d1e909193..15a809ad3 100644
--- a/sphinx_immaterial/content_tabs.py
+++ b/sphinx_immaterial/content_tabs.py
@@ -92,9 +92,7 @@ def run(self) -> List[nodes.Node]:
         # add tab label
         textnodes, _ = self.state.inline_text(self.arguments[0], self.lineno)
         tab_label = nodes.rubric(
-            self.arguments[0],
-            *textnodes,
-            classes=["tabbed-label"],
+            self.arguments[0], *textnodes, classes=["tabbed-label"]
         )
         self.add_name(tab_label)
         tab_item += tab_label
@@ -135,23 +133,22 @@ def visit_tab_set(self: HTMLTranslator, node: content_tab_set):
     self.body.append(self.starttag(node, "div", **attributes))
 
     # walkabout the children
-    tab_total = 0
     tab_label_div = nodes.container("", is_div=True, classes=["tabbed-labels"])
     tab_content_div = nodes.container("", is_div=True, classes=["tabbed-content"])
-    for tab_item in node.children:
+    for tab_count, tab_item in enumerate(node.children):
         try:
             tab_label, tab_block = tab_item.children
         except ValueError as exc:
             raise ValueError(f"md-tab-item has no children:\n{repr(tab_item)}") from exc
-        tab_item_identity = tab_set_identity + f"_{tab_total + 1}"
+        tab_item_identity = tab_set_identity + f"_{tab_count + 1}"
 
         # create: 
         self.body.append(
             "'
+            + ("checked " if not tab_count else "")
+            + f'type="radio" id="{self.attval(tab_item_identity)}"'
+            + f' name="{self.attval(tab_set_identity)}">'
         )
-        tab_total += 1
 
         # create: 
         label_node = content_tab_label(