diff --git a/.gitignore b/.gitignore index 1722c7d6..d0d8d593 100755 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,7 @@ js/lib # Sphinx documentation docs/_build/ +docs/autoapi/ # Jupyter Notebook .ipynb_checkpoints diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0207538a..3fc189f0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,27 +1,32 @@ +default_install_hook_types: [pre-commit, commit-msg] + repos: - repo: "https://github.com/psf/black" rev: "22.3.0" hooks: - id: black - stages: [commit] + stages: [pre-commit] + - repo: "https://github.com/commitizen-tools/commitizen" rev: "v2.18.0" hooks: - id: commitizen stages: [commit-msg] + - repo: "https://github.com/kynan/nbstripout" rev: "0.5.0" hooks: - id: nbstripout - stages: [commit] + stages: [pre-commit] + - repo: "https://github.com/pre-commit/mirrors-prettier" rev: "v2.7.1" hooks: - id: prettier - stages: [commit] + stages: [pre-commit] - repo: https://github.com/charliermarsh/ruff-pre-commit rev: "v0.0.213" hooks: - id: ruff - stages: [commit] + stages: [pre-commit] diff --git a/docs/_extension/aknowledgement.py b/docs/_extension/aknowledgement.py new file mode 100644 index 00000000..162f89bf --- /dev/null +++ b/docs/_extension/aknowledgement.py @@ -0,0 +1,103 @@ +"""A directive to generate an alert admonition.""" +from __future__ import annotations + +from typing import ClassVar + +from docutils import nodes +from docutils.nodes import Node +from sphinx import addnodes +from sphinx.application import Sphinx +from sphinx.util import logging +from sphinx.util.docutils import SphinxDirective +from sphinx.util.typing import OptionSpec +from sphinx.writers.html5 import HTML5Translator + +logger = logging.getLogger(__name__) + + +class alert_node(nodes.Admonition, nodes.Element): + pass + + +def visit_aknowledgement_node(self: HTML5Translator, node: alert_node) -> None: + self.visit_admonition(node) + + +def depart_aknowledgement_node(self: HTML5Translator, node: alert_node) -> None: + self.depart_admonition(node) + + +class AknowledgementDirective(SphinxDirective): + """Directive to reference the original source of the documentation.""" + + has_content = True + required_arguments = 0 + optional_arguments = 0 + final_argument_whitespace = True + option_spec: ClassVar[OptionSpec] = {} + + def run(self) -> list[Node]: + node = addnodes.versionmodified() + node.document = self.state.document + self.set_source_info(node) + node["type"] = "versionchanged" + node["version"] = "" + text = "Aknowledgement" + messages = [] + if self.content: + node += self.parse_content_to_nodes() + classes = ["versionmodified", "changed"] + if len(node) > 0 and isinstance(node[0], nodes.paragraph): + # the contents start with a paragraph + if node[0].rawsource: + # make the first paragraph translatable + content = nodes.inline(node[0].rawsource, translatable=True) + content.source = node[0].source + content.line = node[0].line + content += node[0].children + node[0].replace_self(nodes.paragraph("", "", content, translatable=False)) + para = node[0] + para.insert(0, nodes.inline("", "%s: " % text, classes=classes)) + elif len(node) > 0: + # the contents do not starts with a paragraph + para = nodes.paragraph( + "", + "", + nodes.inline("", "%s: " % text, classes=classes), + translatable=False, + ) + node.insert(0, para) + else: + # the contents are empty + para = nodes.paragraph( + "", + "", + nodes.inline("", "%s." % text, classes=classes), + translatable=False, + ) + node.append(para) + + domain = self.env.domains.changeset_domain + domain.note_changeset(node) + + ret: list[Node] = [node] + ret += messages + return ret + + +def setup(app: Sphinx) -> dict[str, object]: + """Add custom configuration to sphinx app. + + Args: + app: the Sphinx application + + Returns: + the 2 parallel parameters set to ``True``. + """ + app.add_directive("aknowledgement", AknowledgementDirective) + app.add_node(alert_node, html=(visit_aknowledgement_node, depart_aknowledgement_node)) + + return { + "parallel_read_safe": True, + "parallel_write_safe": True, + } diff --git a/docs/_extension/api_admonition.py b/docs/_extension/api_admonition.py new file mode 100644 index 00000000..7e096053 --- /dev/null +++ b/docs/_extension/api_admonition.py @@ -0,0 +1,73 @@ +"""A directive to generate an API admonition.""" +from __future__ import annotations + +from docutils import nodes +from docutils.parsers.rst import directives +from docutils.parsers.rst.directives.admonitions import BaseAdmonition +from sphinx.application import Sphinx +from sphinx.util import logging +from sphinx.util.docutils import SphinxDirective +from sphinx.writers.html5 import HTML5Translator + +logger = logging.getLogger(__name__) + + +class api_node(nodes.Admonition, nodes.Element): + pass + + +def visit_api_node(self: HTML5Translator, node: api_node) -> None: + self.visit_admonition(node) + + +def depart_api_node(self: HTML5Translator, node: api_node) -> None: + self.depart_admonition(node) + + +class APIAdmonitionDirective(BaseAdmonition, SphinxDirective): + """An API entry, displayed (if configured) in the form of an admonition.""" + + node_class = api_node + has_content = True + required_arguments = 0 + optional_arguments = 0 + final_argument_whitespace = False + option_spec = { + "class": directives.class_option, + "name": directives.unchanged, + } + + def run(self) -> list[nodes.Node]: + if not self.options.get("class"): + self.options["class"] = ["admonition-api"] + + (api,) = super().run() + if isinstance(api, nodes.system_message): + return [api] + elif isinstance(api, api_node): + api.insert(0, nodes.title(text="See API")) + api["docname"] = self.env.docname + self.add_name(api) + self.set_source_info(api) + self.state.document.note_explicit_target(api) + return [api] + else: + raise RuntimeError # never reached here + + +def setup(app: Sphinx) -> dict[str, object]: + """Add custom configuration to sphinx app. + + Args: + app: the Sphinx application + + Returns: + the 2 parallel parameters set to ``True``. + """ + app.add_directive("api", APIAdmonitionDirective) + app.add_node(api_node, html=(visit_api_node, depart_api_node)) + + return { + "parallel_read_safe": True, + "parallel_write_safe": True, + } diff --git a/docs/_static/banner.html b/docs/_static/banner.html new file mode 100644 index 00000000..ddb197ec --- /dev/null +++ b/docs/_static/banner.html @@ -0,0 +1,6 @@ + diff --git a/docs/_static/custom.css b/docs/_static/custom.css index 8744437f..5bbc1543 100644 --- a/docs/_static/custom.css +++ b/docs/_static/custom.css @@ -38,3 +38,65 @@ html[data-theme="light"] { html[data-theme="dark"] { --pst-color-primary: #1697f6; } + +/******************************************************************************* +* Create a custom api admonition +*/ +div.admonition.admonition-api > .admonition-title::after { + content: "\f121"; /* the fa-code icon */ +} + +/******************************************************************************* +* Customize the font used by the title to make it look like the original lib +*/ +.title.logo__title { + font-family: "Exo", sans-serif; + font-optical-sizing: auto; + font-weight: 700; + font-style: normal; +} + +/******************************************************************************* +* custom article footer rendering +*/ +footer.bd-footer-article { + background-color: transparent; + border-top: 1px solid var(--pst-color-border); + margin-top: 2em; +} + +.bd-footer-article .footer-article-items { + flex-direction: row; +} + +.bd-footer-article .footer-article-items .footer-article-item { + flex-grow: 1; +} + +.bd-footer-article .last-updated { + color: var(--pst-color-text-muted); + text-align: right; +} + +/******************************************************************************* +* add dollar sign in console code-block +*/ +div.highlight-console pre span.go::before { + content: "$"; + margin-right: 10px; + margin-left: 5px; +} + +/******************************************************************************* +* custom loader animation +*/ +@keyframes spin { + to { + transform: rotate(360deg); + } +} + +.custom-loader { + display: flex; + animation: spin 1s linear infinite; +} diff --git a/docs/_static/logo.png b/docs/_static/logo.png new file mode 100644 index 00000000..717b3d4c Binary files /dev/null and b/docs/_static/logo.png differ diff --git a/docs/_templates/python/class.rst b/docs/_templates/python/class.rst new file mode 100644 index 00000000..77963fbc --- /dev/null +++ b/docs/_templates/python/class.rst @@ -0,0 +1,106 @@ +{% if obj.display %} + {% if is_own_page %} +:html_theme.sidebar_secondary.remove: + +{{ obj.id.split(".")[-1] }} +{{ "=" * obj.id.split(".")[-1] | length }} + + {% endif %} + {% set visible_children = obj.children|selectattr("display")|list %} + {% set own_page_children = visible_children|selectattr("type", "in", own_page_types)|list %} + {% if is_own_page and own_page_children %} +.. toctree:: + :hidden: + + {% for child in own_page_children %} + {{ child.include_path }} + {% endfor %} + + {% endif %} +.. py:{{ obj.type }}:: {% if is_own_page %}{{ obj.id }}{% else %}{{ obj.short_name }}{% endif %}{% if obj.args %}({{ obj.args }}){% endif %} + + {% for (args, return_annotation) in obj.overloads %} + {{ " " * (obj.type | length) }} {{ obj.short_name }}{% if args %}({{ args }}){% endif %} + + {% endfor %} + {% if obj.bases %} + {% if "show-inheritance" in autoapi_options %} + + Bases: {% for base in obj.bases %}{{ base|link_objs }}{% if not loop.last %}, {% endif %}{% endfor %} + {% endif %} + + + {% if "show-inheritance-diagram" in autoapi_options and obj.bases != ["object"] %} + .. autoapi-inheritance-diagram:: {{ obj.obj["full_name"] }} + :parts: 1 + {% if "private-members" in autoapi_options %} + :private-bases: + {% endif %} + + {% endif %} + {% endif %} + {% if obj.docstring %} + + {{ obj.docstring|indent(3) }} + {% endif %} + {% for obj_item in visible_children %} + {% if obj_item.type not in own_page_types %} + + {{ obj_item.render()|indent(3) }} + {% endif %} + {% endfor %} + {% if is_own_page and own_page_children %} + {% set visible_attributes = own_page_children|selectattr("type", "in", ("attribute", "property"))|list %} + {% if visible_attributes %} +Attributes +---------- + +.. autoapisummary:: + + {% for attribute in visible_attributes %} + {{ attribute.id }} + {% endfor %} + + + {% endif %} + {% set visible_exceptions = own_page_children|selectattr("type", "equalto", "exception")|list %} + {% if visible_exceptions %} +Exceptions +---------- + +.. autoapisummary:: + + {% for exception in visible_exceptions %} + {{ exception.id }} + {% endfor %} + + + {% endif %} + {% set visible_classes = own_page_children|selectattr("type", "equalto", "class")|list %} + {% if visible_classes %} +Classes +------- + +.. autoapisummary:: + + {% for klass in visible_classes %} + {{ klass.id }} + {% endfor %} + + + {% endif %} + {% set visible_methods = own_page_children|selectattr("type", "equalto", "method")|list %} + {% if visible_methods %} +Methods +------- + +.. autoapisummary:: + + {% for method in visible_methods %} + {{ method.id }} + {% endfor %} + + + {% endif %} + {% endif %} +{% endif %} \ No newline at end of file diff --git a/docs/advanced_usage.rst b/docs/advanced_usage.rst deleted file mode 100644 index 890fcfa2..00000000 --- a/docs/advanced_usage.rst +++ /dev/null @@ -1,181 +0,0 @@ -Advanced Usage -============== - -(Scoped) Slots --------------- - -Slots are used to add content at a certain location in a widget. You can find out what slots a widget supports by using -the Vuetify documentation. If you want to know what slots :code:`Select` has, search for :code:`v-select` on the -`Vuetify API explorer `_ or for this example use the `direct link -`_. On the left-hand side of list of attributes you will find a tab -'slots'. - -An example for using the slot 'no-data', which changes what the Select widget shows when it has no items: - -Vuetify: - -.. code-block:: html - - - - - -ipyvuetify: - -.. jupyter-execute:: - :hide-output: - :hide-code: - - import ipyvuetify as v - -.. jupyter-execute:: - - v.Select(v_slots=[{ - 'name': 'no-data', - 'children': [ - v.ListItem(children=[ - v.ListItemTitle(children=['My custom no data message'])])] - }]) - -Scoped slots are used if the parent widget needs to share its scope with the content. In the example below the events -of the parent widget are used in the slot content. - -Vuetify: - -.. code-block:: html - - - - Insert tooltip text here - - -ipyvuetify: - -.. jupyter-execute:: - - v.Container(children=[ - v.Tooltip(bottom=True, v_slots=[{ - 'name': 'activator', - 'variable': 'tooltip', - 'children': v.Btn(v_on='tooltip.on', color='primary', children=[ - 'button with tooltip' - ]), - }], children=['Insert tooltip text here']) - ]) - -In the Vuetify examples you will actually see: - -.. code-block:: html - - ... -