From 5103cb9b99c6d2d7f51aeb4937797b08f561d4f9 Mon Sep 17 00:00:00 2001 From: Fabien Lelaquais Date: Sat, 16 Sep 2023 16:30:15 +0200 Subject: [PATCH 1/2] - Merge generic and Core controls - Add breadscumbs in element doc pages --- .../manuals/gui/corelements/index.md_template | 19 -- .../gui/viselements/controls.md_template | 20 +- docs/manuals/gui/viselements/index.md | 6 +- mkdocs.yml_template | 2 - taipy_theme/assets/stylesheets/avaiga.css | 37 ++- tools/_setup_generation/elmnts_generator.py | 227 ------------- tools/_setup_generation/setup.py | 19 +- tools/_setup_generation/step_corelements.py | 43 --- tools/_setup_generation/step_viselements.py | 299 +++++++++++++++--- tools/postprocess.py | 33 +- 10 files changed, 355 insertions(+), 350 deletions(-) delete mode 100644 docs/manuals/gui/corelements/index.md_template delete mode 100644 tools/_setup_generation/elmnts_generator.py delete mode 100644 tools/_setup_generation/step_corelements.py diff --git a/docs/manuals/gui/corelements/index.md_template b/docs/manuals/gui/corelements/index.md_template deleted file mode 100644 index 5652a0314..000000000 --- a/docs/manuals/gui/corelements/index.md_template +++ /dev/null @@ -1,19 +0,0 @@ -# Core elements - -!!! warning "Available in Taipy Community and Enterprise editions" - - The controls listed in this section are available only if the - [`taipy`](https://pypi.org/project/taipy/) Python package is installed. These controls are - **not** present if only [`taipy-gui`](https://pypi.org/project/taipy-gui/) is installed. - -Taipy provides a set of visual elements that are meant to simplify the use of the -[entities](../../core/entities/index.md) created with Taipy Core. - -These controls let users list the entities, create new ones, edit or delete them. - -## Elements list - -Here is the list of all the available Core elements in Taipy: - -[TOC] - diff --git a/docs/manuals/gui/viselements/controls.md_template b/docs/manuals/gui/viselements/controls.md_template index f5b1da600..0df4d1587 100644 --- a/docs/manuals/gui/viselements/controls.md_template +++ b/docs/manuals/gui/viselements/controls.md_template @@ -1,10 +1,24 @@ # Controls -Controls are user interface objects that represent data. +Controls are user interface objects that represent data and that users can interact with. -## Controls list +## Generic controls -Here is the list of all available controls in Taipy: +Here is the list of all available controls in Taipy GUI: [TOC] +## Scenario management controls + +Some controls are dedicated to Scenario Management. These controls let users select +Entities and interact with them. + +!!! warning "Available in Taipy Community and Enterprise editions" + + The controls listed in this section are available only if the + [`taipy`](https://pypi.org/project/taipy/) Python package is installed. These controls are + **not** present if only [`taipy-gui`](https://pypi.org/project/taipy-gui/) is installed. + +Here is the list of all the available controls that are available in Taipy: + +[core_TOC] diff --git a/docs/manuals/gui/viselements/index.md b/docs/manuals/gui/viselements/index.md index abdb85e35..30b9c65ce 100644 --- a/docs/manuals/gui/viselements/index.md +++ b/docs/manuals/gui/viselements/index.md @@ -20,8 +20,6 @@ may want to jump directly to the list of the available visual elements: [:material-arrow-right: List of available controls](controls.md) -[:material-arrow-right: List of available Core back-end controls](../corelements/index.md) - [:material-arrow-right: List of available blocks](blocks.md) ## Properties @@ -187,7 +185,7 @@ fragment similar to: ``` !!! example "The *default property* shortcut" - The default property name for the control type [`button`](button.md) is _label_. In Taipy, + The default property name for the control type [`button`](button.md) is *label*. In Taipy, the Markdown text ``` <|button|label=Some text|> @@ -326,7 +324,7 @@ the control. You may for example want to check the received value in the *on_cha callback and decide to use this new value or not.
This is the purpose of the *propagate* property. -When the _propagate_ property is set to True, then the application variable bound to a +When the *propagate* property is set to True, then the application variable bound to a control is updated when the user modifies the value represented by the control. !!! info diff --git a/mkdocs.yml_template b/mkdocs.yml_template index 846f2dfa7..6113fdacd 100644 --- a/mkdocs.yml_template +++ b/mkdocs.yml_template @@ -26,8 +26,6 @@ nav: - "Visual Elements": - manuals/gui/viselements/index.md - "Controls": manuals/gui/viselements/controls.md - - "Core Back-end Controls": - - manuals/gui/corelements/index.md - "Blocks": manuals/gui/viselements/blocks.md - "Binding variables": manuals/gui/binding.md - "Callbacks": manuals/gui/callbacks.md diff --git a/taipy_theme/assets/stylesheets/avaiga.css b/taipy_theme/assets/stylesheets/avaiga.css index 6b2a9f612..e99503c36 100644 --- a/taipy_theme/assets/stylesheets/avaiga.css +++ b/taipy_theme/assets/stylesheets/avaiga.css @@ -677,4 +677,39 @@ a.md-content__button{ .tp-btn-parent:hover, .md-content .tp-btn-parent:hover{ color: var(--md-primary-fg-color); -} \ No newline at end of file +} + +/* ---------- ---------- ---------- Breadcrumbs ---------- ---------- ---------- */ +ul.tp-bc { + padding: 4px 4px; + list-style: none; + background-color: var(--md-default-bg-color--lighter); + margin-left: auto; +} + +ul.tp-bc:before { + padding: 8px; + color: var(--md-primary-fg-color--light); + content: 'Back to:'; + font-size: var(--md-body-font-size--small) +} +ul.tp-bc li { + display: inline; + font-size: var(--md-body-font-size--small) +} + +ul.tp-bc li+li:before { + padding: 1em; + color: var(--md-primary-fg-color); + content: '>'; +} + +ul.tp-bc li a { + color: var(--md-primary-fg-color--dark); + text-decoration: none; +} + +ul.tp-bc li a:hover { + color: var(--md-primary-fg-color--dark); + text-decoration: underline; +} diff --git a/tools/_setup_generation/elmnts_generator.py b/tools/_setup_generation/elmnts_generator.py deleted file mode 100644 index 725081499..000000000 --- a/tools/_setup_generation/elmnts_generator.py +++ /dev/null @@ -1,227 +0,0 @@ -import json -import os -import re -from typing import Dict, List, Optional -from .setup import SetupStep - -class ElementsGenerator(SetupStep): - DEFAULT_PROPERTY = "default_property" - PROPERTIES = "properties" - NAME = "name" - INHERITS = "inherits" - - # Load elements, test validity of doc and resolve inheritance - def load_elements(self, elements_json_path: str, categories: List[str]) -> None: - with open(elements_json_path) as elements_json_file: - loaded_elements = json.load(elements_json_file) - - self.elements = {} - self.categories = {} - for category, elements in loaded_elements.items(): - self.categories[category] = [] - for element in elements: - element_type = element[0] - self.categories[category].append(element_type) - if element_type in self.elements: - raise ValueError( - f"FATAL - Duplicate element type '{element_type}' in {elements_json_path}" - ) - element_desc = element[1] - if not __class__.PROPERTIES in element_desc and not __class__.INHERITS in element_desc: - raise ValueError( - f"FATAL - No properties in element type '{element_type}' in {elements_json_path}" - ) - self.elements[element_type] = element_desc - # Find default property for all element types - for element_type, element_desc in self.elements.items(): - default_property = None - if properties := element_desc.get(__class__.PROPERTIES, None): - for property in properties: - if __class__.DEFAULT_PROPERTY in property: - if property[__class__.DEFAULT_PROPERTY]: - default_property = property[__class__.NAME] - del property[__class__.DEFAULT_PROPERTY] - element_desc[__class__.DEFAULT_PROPERTY] = default_property - # Resolve inheritance - def merge(element_desc, parent_element_desc, default_property) -> Optional[str]: - element_properties = element_desc.get(__class__.PROPERTIES, []) - element_property_names = [p[__class__.NAME] for p in element_properties] - for property in parent_element_desc.get(__class__.PROPERTIES, []): - property_name = property[__class__.NAME] - if property_name in element_property_names: - element_property = element_properties[element_property_names.index(property_name)] - for n in ["type", "default_value", "doc"]: - if not n in element_property and n in property: - element_property[n] = property[n] - else: - element_property_names.append(property_name) - element_properties.append(property) - element_desc[__class__.PROPERTIES] = element_properties - if not default_property and parent_element_desc.get(__class__.DEFAULT_PROPERTY, False): - default_property = parent_element_desc[__class__.DEFAULT_PROPERTY] - return default_property - - for element_type, element_desc in self.elements.items(): - if parent_types := element_desc.get(__class__.INHERITS, None): - del element_desc[__class__.INHERITS] - default_property = element_desc[__class__.DEFAULT_PROPERTY] - for parent_type in parent_types: - parent_desc = self.elements[parent_type] - default_property = merge(element_desc, parent_desc, default_property) - element_desc[__class__.DEFAULT_PROPERTY] = default_property - # Check that documented elements have a default property and a doc file, - # and that their properties have the mandatory settings. - for element in [elm for cat in categories for elm in self.categories[cat]]: - element_desc = self.elements[element] - if not __class__.DEFAULT_PROPERTY in element_desc: - raise ValueError( - f"FATAL - No default property for element type '{element}'" - ) - if not __class__.PROPERTIES in element_desc: - raise ValueError( - f"FATAL - No properties for element type '{element}'" - ) - template_path = self.get_element_md_path(element) + "_template" - if not os.access(template_path, os.R_OK): - raise FileNotFoundError( - f"FATAL - Could not find template doc file for element type '{element}' at {template_path}" - ) - # Check completeness - for property in element_desc[__class__.PROPERTIES]: - for n in ["type", "doc"]: - if not n in property: - raise ValueError( - f"FATAL - No value for '{n}' in the '{property[__class__.NAME]}' properties of element type '{element_typ}' in {viselements_json_path}" - ) - - FIRST_PARA_RE = re.compile(r"(^.*?)(:?\n\n)", re.MULTILINE | re.DOTALL) - FIRST_HEADER1_RE = re.compile(r"(^.*?)(\n#\s+)", re.MULTILINE | re.DOTALL) - # Find first level 2 or 3 header - FIRST_HEADER2_RE = re.compile(r"(^.*?)(\n###?\s+)", re.MULTILINE | re.DOTALL) - - def has_category(self) -> bool: - raise NotImplementedError(f"has_category() not implemented.") - - def get_element_md_path(self, element_type: str) -> str: - raise NotImplementedError(f"get_element_md_path() not implemented (element was {element_type}).") - - # Returns before_properties and after_properties if needed - # Returned tuple would be: (new_before_properties, after_properties) where each can be None, indicating - # we don't want to change them - def element_page_hook(self, element_type:str, doc:str, before_properties: str, after_properties: str) -> tuple[str, str]: - return (None, None) - - # Generate element doc pages for that category - def generate_pages(self, category: str, md_path: str) -> None: - def generate_element_doc(element_type: str, element_desc: Dict): - """ - Returns the entry for the Table of Contents that is inserted - in the global Visual Elements or Core Elements doc page. - """ - template_doc_path = self.get_element_md_path(element_type) + "_template" - with open(template_doc_path, "r") as template_doc_file: - element_documentation = template_doc_file.read() - # Retrieve first paragraph from element documentation - match = ElementsGenerator.FIRST_PARA_RE.match(element_documentation) - if not match: - raise ValueError( - f"Couldn't locate first paragraph in documentation for element '{element_type}'" - ) - first_documentation_paragraph = match.group(1) - - # Build properties table - properties_table = """ -## Properties\n\n - - - - - - - - - - -""" - STAR = "(★)" - default_property_name = element_desc[__class__.DEFAULT_PROPERTY] - for property in element_desc[__class__.PROPERTIES]: - name = property[__class__.NAME] - type = property["type"] - default_value = property.get("default_value", None) - doc = property.get("doc", None) - if not default_value: - default_value = "Required" if property.get("required", False) else "" - full_name = f"]+>', '', name)}\">" - if name == default_property_name: - full_name += f"{name}{STAR}" - else: - full_name += f"{name}" - properties_table += ( - "\n" - + f"\n" - + f"\n" - + f"\n" - + f"\n" - + "\n" - ) - properties_table += " \n
NameTypeDefaultDescription
{full_name}{type}{default_value}

{doc}

\n\n" - if default_property_name: - properties_table += ( - f'

{STAR}' - + f'' - + f"{default_property_name}" - + " is the default property for this visual element.

\n" - ) - - # Insert title and properties in element documentation - match = ElementsGenerator.FIRST_HEADER2_RE.match(element_documentation) - if not match: - raise ValueError( - f"Couldn't locate first header2 in documentation for element '{element_type}'" - ) - before_properties = match.group(1) - after_properties = match.group(2) + element_documentation[match.end() :] - - # Process element hook - hook_values = self.element_page_hook(element_type, element_documentation, before_properties, after_properties) - if hook_values[0]: - before_properties = hook_values[0] - if hook_values[1]: - after_properties = hook_values[1] - - with open(self.get_element_md_path(element_type), "w") as md_file: - md_file.write( - "---\nhide:\n - navigation\n---\n\n" - + f"# {element_type}\n\n" - + before_properties - + properties_table - + after_properties - ) - e = element_type # Shortcut - d = f"../{e}" if self.has_category() else e - return ( - f'\n' - + f"
{e}
\n" - + f'\n' - + f'\n' - + f'\n' - + f'\n' - + f"

{first_documentation_paragraph}

\n" - + "
\n" - ) - # If you want a simple list, use - # f"
  • {e}: {first_documentation_paragraph}
  • \n" - # The toc header and footer must then be "" and "" respectively. - - md_template = "" - with open(f"{md_path}_template") as template_file: - md_template = template_file.read() - if not md_template: - raise FileNotFoundError(f"FATAL - Could not read {md_path}_template markdown template") - toc = '
    \n' - for element_type in self.categories[category]: - toc += generate_element_doc(element_type, self.elements[element_type]) - toc += "
    \n" - with open(md_path, "w") as md_file: - md_file.write(md_template.replace("[TOC]", toc)) diff --git a/tools/_setup_generation/setup.py b/tools/_setup_generation/setup.py index bb0ea104c..72ace3aa9 100644 --- a/tools/_setup_generation/setup.py +++ b/tools/_setup_generation/setup.py @@ -111,20 +111,17 @@ def setup(self, setup: Setup): pass -from .step_viselements import VisElementsStep -from .step_corelements import CoreElementsStep -from .step_refman import RefManStep -from .step_rest_refman import RestRefManStep -from .step_gui_ext_refman import GuiExtRefManStep -from .step_getting_started import GettingStartedStep -from .step_contributors import ContributorsStep - - def run_setup(root_dir: str, steps: List[SetupStep] = None): - if steps is None: + if not steps: + from .step_viselements import VisElementsStep + from .step_refman import RefManStep + from .step_rest_refman import RestRefManStep + from .step_gui_ext_refman import GuiExtRefManStep + from .step_getting_started import GettingStartedStep + from .step_contributors import ContributorsStep + steps = [ VisElementsStep(), - CoreElementsStep(), RefManStep(), RestRefManStep(), GuiExtRefManStep(), diff --git a/tools/_setup_generation/step_corelements.py b/tools/_setup_generation/step_corelements.py deleted file mode 100644 index 09b37650a..000000000 --- a/tools/_setup_generation/step_corelements.py +++ /dev/null @@ -1,43 +0,0 @@ -# ################################################################################ -# Taipy GUI Core Elements documentation. -# -# This includes the update of the Table of Contents of the controls -# document pages. -# -# For each element, this script combines its property list and -# documentation, and generates full arkdown files in [CORELEMENTS_DIR_PATH]. All -# these files ultimately get integrated in the global doc set. -# -# The template documentation file [CORELEMENTS_DIR_PATH]/index.md_template -# is also completed with generated table of contents. -# ################################################################################ -from .setup import Setup -from .elmnts_generator import ElementsGenerator -import os - -class CoreElementsStep(ElementsGenerator): - - def get_id(self) -> str: - return "corelements" - - def get_description(self) -> str: - return "Extraction of the Core elements documentation" - - def enter(self, setup: Setup): - self.CORELEMENTS_DIR_PATH = setup.manuals_dir + "/gui/corelements" - self.index_path = self.CORELEMENTS_DIR_PATH + "/index.md" - if not os.access(f"{self.index_path}_template", os.R_OK): - raise FileNotFoundError( - f"FATAL - Could not read {self.index_path}_template markdown template" - ) - self.load_elements(setup.root_dir + "/taipy/gui_core/viselements.json", - ["controls"]) - - def has_category(self) -> bool: - return False - - def get_element_md_path(self, element_type: str) -> str: - return f"{self.CORELEMENTS_DIR_PATH}/{element_type}.md" - - def setup(self, setup: Setup) -> None: - self.generate_pages("controls", self.index_path) diff --git a/tools/_setup_generation/step_viselements.py b/tools/_setup_generation/step_viselements.py index c7adf5b26..4a8e33784 100644 --- a/tools/_setup_generation/step_viselements.py +++ b/tools/_setup_generation/step_viselements.py @@ -12,28 +12,34 @@ # [VISELEMENTS_DIR_PATH]/[controls|blocks].md_template # are also completed with generated table of contents. # ################################################################################ -from .setup import Setup -from .elmnts_generator import ElementsGenerator +from .setup import Setup, SetupStep +import json import os import re +from typing import Dict, List, Optional +class VisElementsStep(SetupStep): + DEFAULT_PROPERTY = "default_property" + PROPERTIES = "properties" + NAME = "name" + INHERITS = "inherits" -class VisElementsStep(ElementsGenerator): def get_id(self) -> str: return "viselements" def get_description(self) -> str: return "Extraction of the visual elements documentation" - + def enter(self, setup: Setup): self.VISELEMENTS_DIR_PATH = setup.manuals_dir + "/gui/viselements" - self.controls_path = self.get_element_md_path("controls") + self.CORELEMENTS_DIR_PATH = setup.manuals_dir + "/gui/corelements" + self.controls_path = f"{self.VISELEMENTS_DIR_PATH}/controls.md" template_path = f"{self.controls_path}_template" if not os.access(template_path, os.R_OK): raise FileNotFoundError( f"FATAL - Could not read {template_path} Markdown template" ) - self.blocks_path = self.get_element_md_path("blocks") + self.blocks_path = f"{self.VISELEMENTS_DIR_PATH}/blocks.md" template_path = f"{self.blocks_path}_template" if not os.access(template_path, os.R_OK): raise FileNotFoundError( @@ -44,47 +50,262 @@ def enter(self, setup: Setup): raise FileNotFoundError( f"FATAL - Could not read {self.charts_home_html_path} html fragment" ) - self.load_elements(setup.root_dir + "/taipy/gui/viselements.json", - ["controls", "blocks"]) + # Load Taipy GUI and Taipy elements + # ----------------------------------------------------------- + # Load elements, check basic features and resolve inheritance + def load_elements(self, elements_json_path: str, prefix: str, doc_pages_path: str) -> None: + with open(elements_json_path) as elements_json_file: + loaded_elements = json.load(elements_json_file) - def has_category(self) -> bool: - return True + for category, elements in loaded_elements.items(): + if category not in self.categories: + self.categories[category] = [] + for element in elements: + element_type = element[0] + self.categories[category].append(element_type) + if element_type in self.elements: + raise ValueError( + f"FATAL - Duplicate element type '{element_type}' in {elements_json_path}" + ) + element_desc = element[1] + if not __class__.PROPERTIES in element_desc and not __class__.INHERITS in element_desc: + raise ValueError( + f"FATAL - No properties in element type '{element_type}' in {elements_json_path}" + ) + element_desc["prefix"] = prefix + element_desc["doc_path"] = doc_pages_path + element_desc["source"] = elements_json_path + self.elements[element_type] = element_desc + # Find default property for all element types + for element_type, element_desc in self.elements.items(): + default_property = None + if properties := element_desc.get(__class__.PROPERTIES, None): + for property in properties: + if __class__.DEFAULT_PROPERTY in property: + if property[__class__.DEFAULT_PROPERTY]: + default_property = property[__class__.NAME] + del property[__class__.DEFAULT_PROPERTY] + element_desc[__class__.DEFAULT_PROPERTY] = default_property + # Resolve inheritance + def merge(element_desc, parent_element_desc, default_property) -> Optional[str]: + element_properties = element_desc.get(__class__.PROPERTIES, []) + element_property_names = [p[__class__.NAME] for p in element_properties] + for property in parent_element_desc.get(__class__.PROPERTIES, []): + property_name = property[__class__.NAME] + if property_name in element_property_names: + element_property = element_properties[element_property_names.index(property_name)] + for n in ["type", "default_value", "doc"]: + if not n in element_property and n in property: + element_property[n] = property[n] + else: + element_property_names.append(property_name) + element_properties.append(property) + element_desc[__class__.PROPERTIES] = element_properties + if not default_property and parent_element_desc.get(__class__.DEFAULT_PROPERTY, False): + default_property = parent_element_desc[__class__.DEFAULT_PROPERTY] + return default_property + for element_type, element_desc in self.elements.items(): + if parent_types := element_desc.get(__class__.INHERITS, None): + del element_desc[__class__.INHERITS] + default_property = element_desc[__class__.DEFAULT_PROPERTY] + for parent_type in parent_types: + parent_desc = self.elements[parent_type] + default_property = merge(element_desc, parent_desc, default_property) + element_desc[__class__.DEFAULT_PROPERTY] = default_property - def get_element_md_path(self, element_type: str) -> str: - return f"{self.VISELEMENTS_DIR_PATH}/{element_type}.md" + self.elements = {} + self.categories = {} + load_elements(self, setup.root_dir + "/taipy/gui/viselements.json", "", self.VISELEMENTS_DIR_PATH) + load_elements(self, setup.root_dir + "/taipy/gui_core/viselements.json", "core_", self.CORELEMENTS_DIR_PATH) - def setup(self, setup: Setup) -> None: - self.generate_pages("controls", self.controls_path) - self.generate_pages("blocks", self.blocks_path) + # Check that documented elements have a default property and a doc file, + # and that their properties have the mandatory settings. + def check_elements(self) -> None: + for category, element_type in [(c, e) for c,elts in self.categories.items() for e in elts]: + if category == "undocumented": + continue + element_desc = self.elements[element_type] + if not __class__.DEFAULT_PROPERTY in element_desc: + raise ValueError( + f"FATAL - No default property for element type '{element_type}'" + ) + if not __class__.PROPERTIES in element_desc: + raise ValueError( + f"FATAL - No properties for element type '{element_type}'" + ) + template_path = f"{element_desc['doc_path']}/{element_type}.md_template" + if not os.access(template_path, os.R_OK): + raise FileNotFoundError( + f"FATAL - Could not find template doc file for element type '{element_type}' at {template_path}" + ) + # Check completeness + for property in element_desc[__class__.PROPERTIES]: + for n in ["type", "doc"]: + if not n in property: + raise ValueError( + f"FATAL - No value for '{n}' in the '{property[__class__.NAME]}' properties of element type '{element_type}' in {element_desc['source']}" + ) + + check_elements(self) + + # Generate element doc pages for that category + # Find first level 2 or 3 header + def generate_pages(self, category: str, md_path: str) -> None: + FIRST_PARA_RE = re.compile(r"(^.*?)(:?\n\n)", re.MULTILINE | re.DOTALL) + # Find first level 2 or 3 header + FIRST_HEADER2_RE = re.compile(r"(^.*?)(\n###?\s+)", re.MULTILINE | re.DOTALL) + + md_template = "" + with open(f"{md_path}_template") as template_file: + md_template = template_file.read() + if not md_template: + raise FileNotFoundError(f"FATAL - Could not read {md_path}_template markdown template") + prefixes = set([desc["prefix"] for desc in self.elements.values()]) + toc = {} + for prefix in prefixes: + toc[prefix] = '
    \n' - def element_page_hook(self, element_type:str, element_documentation: str, before: str, after: str) -> tuple[str, str]: - # Special case for charts: we want to insert the chart gallery that is stored in the - # file whose path is in self.charts_home_html_path - # This should be inserted before the first level 1 header - if element_type == "chart": - with open(self.charts_home_html_path, "r") as html_fragment_file: - chart_gallery = html_fragment_file.read() - # The chart_gallery begins with a comment where all sub-sections - # are listed. - SECTIONS_RE = re.compile(r"^(?:\s*)", re.MULTILINE | re.DOTALL) - match = SECTIONS_RE.match(chart_gallery) + def generate_element_doc(element_type: str, element_desc: Dict, prefix: str): + """ + Returns the entry for the Table of Contents that is inserted + in the global Visual Elements or Core Elements doc page. + """ + template_doc_path = f"{element_desc['doc_path']}/{element_type}.md_template" + with open(template_doc_path, "r") as template_doc_file: + element_documentation = template_doc_file.read() + # Retrieve first paragraph from element documentation + match = FIRST_PARA_RE.match(element_documentation) if not match: raise ValueError( - f"{self.charts_home_html_path} should begin with an HTML comment that lists the chart types" + f"Couldn't locate first paragraph in documentation for element '{element_type}'" ) - chart_gallery = "\n" + chart_gallery[match.end() :] - SECTION_RE = re.compile(r"^([\w-]+):(.*)$") - chart_sections = "" - for line in match.group(1).splitlines(): - match = SECTION_RE.match(line) - if match: - chart_sections += f"- [{match.group(2)}](charts/{match.group(1)}.md)\n" - - match = ElementsGenerator.FIRST_HEADER1_RE.match(element_documentation) + first_documentation_paragraph = match.group(1) + + # Build properties table + properties_table = """ +## Properties\n\n + + + + + + + + + + +""" + STAR = "(★)" + default_property_name = element_desc[__class__.DEFAULT_PROPERTY] + for property in element_desc[__class__.PROPERTIES]: + name = property[__class__.NAME] + type = property["type"] + default_value = property.get("default_value", None) + doc = property.get("doc", None) + if not default_value: + default_value = "Required" if property.get("required", False) else "" + full_name = f"]+>', '', name)}\">" + if name == default_property_name: + full_name += f"{name}{STAR}" + else: + full_name += f"{name}" + properties_table += ( + "\n" + + f"\n" + + f"\n" + + f"\n" + + f"\n" + + "\n" + ) + properties_table += " \n
    NameTypeDefaultDescription
    {full_name}{type}{default_value}

    {doc}

    \n\n" + if default_property_name: + properties_table += ( + f'

    {STAR}' + + f'' + + f"{default_property_name}" + + " is the default property for this visual element.

    \n" + ) + + # Insert title and properties in element documentation + match = FIRST_HEADER2_RE.match(element_documentation) if not match: raise ValueError( - f"Couldn't locate first header1 in documentation for element 'chart'" + f"Couldn't locate first header2 in documentation for element '{element_type}'" ) - return (match.group(1) + chart_gallery + match.group(2) + before[match.end() :], after + chart_sections) + before_properties = match.group(1) + after_properties = match.group(2) + element_documentation[match.end() :] + + # Chart hook + if element_type == "chart": + values = self.chart_page_hook(element_documentation, before_properties, after_properties) + before_properties = values[0] + after_properties = values[1] - return super().element_page_hook(element_type, element_documentation, before, after) + with open(f"{element_desc['doc_path']}/{element_type}.md", "w") as md_file: + md_file.write( + "---\nhide:\n - navigation\n---\n\n" + + f"\n" + + f"# {element_type}\n\n" + + before_properties + + properties_table + + after_properties + ) + e = element_type # Shortcut + d = "../corelements/" if prefix == "core_" else "" + s = " style=\"font-size: .8em;\"" if e == "scenario_selector" or e == "data_node_selector" else "" + return ( + f'\n' + + f"{e}
    \n" + + f'\n' + + f'\n' + + f"

    {first_documentation_paragraph}

    \n" + + "
    \n" + ) + # If you want a simple list, use + # f"
  • {e}: {first_documentation_paragraph}
  • \n" + # The toc header and footer must then be "" and "" respectively. + + for element_type in self.categories[category]: + element_desc = self.elements[element_type] + prefix = element_desc["prefix"] + toc[prefix] += generate_element_doc(element_type, element_desc, prefix) + + with open(md_path, "w") as md_file: + for prefix in prefixes: + md_template = md_template.replace(f"[{prefix}TOC]", toc[prefix]+"\n") + md_file.write(md_template) + + def setup(self, setup: Setup) -> None: + self.generate_pages("controls", self.controls_path) + self.generate_pages("blocks", self.blocks_path) + + # Special case for charts: we want to insert the chart gallery that is stored in the + # file whose path is in self.charts_home_html_path + # This should be inserted before the first level 1 header + def chart_page_hook(self, element_documentation: str, before: str, after: str) -> tuple[str, str]: + FIRST_HEADER1_RE = re.compile(r"(^.*?)(\n#\s+)", re.MULTILINE | re.DOTALL) + + with open(self.charts_home_html_path, "r") as html_fragment_file: + chart_gallery = html_fragment_file.read() + # The chart_gallery begins with a comment where all sub-sections + # are listed. + SECTIONS_RE = re.compile(r"^(?:\s*)", re.MULTILINE | re.DOTALL) + match = SECTIONS_RE.match(chart_gallery) + if not match: + raise ValueError( + f"{self.charts_home_html_path} should begin with an HTML comment that lists the chart types" + ) + chart_gallery = "\n" + chart_gallery[match.end() :] + SECTION_RE = re.compile(r"^([\w-]+):(.*)$") + chart_sections = "" + for line in match.group(1).splitlines(): + match = SECTION_RE.match(line) + if match: + chart_sections += f"- [{match.group(2)}](charts/{match.group(1)}.md)\n" + + match = FIRST_HEADER1_RE.match(element_documentation) + if not match: + raise ValueError( + f"Couldn't locate first header1 in documentation for element 'chart'" + ) + return (match.group(1) + chart_gallery + match.group(2) + before[match.end() :], after + chart_sections) diff --git a/tools/postprocess.py b/tools/postprocess.py index 4bb0e1bdc..842f858fe 100644 --- a/tools/postprocess.py +++ b/tools/postprocess.py @@ -27,7 +27,7 @@ def define_env(env): Mandatory to make this a proper MdDocs macro """ match = re.search(r"/en/(develop|(?:release-(\d+\.\d+)))/$", env.conf["site_url"]) - env.conf["branch"] = (f"release/{match.group(2)}" if match.group(2) else match.group(1)) if match else "unknown" + env.conf["branch"] = (f"release/{match[2]}" if match[2] else match[1]) if match else "unknown" TOC_ENTRY_PART1 = r"\s*", html_content): + element_category = category_match[1] + elif re.match(r"^charts(/|\\).*$", fn_match[3]): + element_category = "chart" + if element_category: + # Insert breadcrumbs + ARTICLE_RE = re.compile(r"()(\s*" + if fn_match[2] == "cor": + repl += f"
  • Visual Elements
  • " + repl += "
  • Scenario management controls
  • " + else: + chart_part = "../" if element_category == "chart" else "" + repl += f"
  • Visual Elements
  • " + repl += (f"
  • Blocks
  • " if element_category == "blocks" + else f"
  • Generic controls
  • ") + if chart_part: + repl += f"
  • Charts
  • " + repl += "" + html_content = (html_content[:article_match.start()] + + article_match.group(1) + + repl + + article_match.group(2) + + html_content[article_match.end():]) + file_was_changed = True + if file_was_changed: with open(filename, "w") as html_file: html_file.write(html_content) From c665da62ff6ebe9359c8c27e7be12936b828cdd8 Mon Sep 17 00:00:00 2001 From: Fabien Lelaquais Date: Tue, 19 Sep 2023 08:20:28 +0200 Subject: [PATCH 2/2] Slight rephrasing --- .../gui/viselements/controls.md_template | 19 +++++++++++++++---- tools/postprocess.py | 2 +- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/docs/manuals/gui/viselements/controls.md_template b/docs/manuals/gui/viselements/controls.md_template index 0df4d1587..4705de2bd 100644 --- a/docs/manuals/gui/viselements/controls.md_template +++ b/docs/manuals/gui/viselements/controls.md_template @@ -1,10 +1,21 @@ # Controls -Controls are user interface objects that represent data and that users can interact with. +Controls are user interface objects representing data. Users can interact with these controls. -## Generic controls +## Standard controls -Here is the list of all available controls in Taipy GUI: +Standard controls can be used in all situations when a user interface is needed. +These controls are elements representing generic data.
    +Here, you can find common graphical controls such as push buttons and sliders, as well as more +advanced graphical controls such as selectors, tables, and charts. + +!!! note "Available in Taipy GUI, Taipy Community, and Enterprise editions" + + The controls listed in this section are provided in the + [`taipy-gui`](https://pypi.org/project/taip-gui/) Python package. These controls are + also present when [`taipy`](https://pypi.org/project/taipy/) is installed. + +Here is the list of all available controls in Taipy: [TOC] @@ -19,6 +30,6 @@ Entities and interact with them. [`taipy`](https://pypi.org/project/taipy/) Python package is installed. These controls are **not** present if only [`taipy-gui`](https://pypi.org/project/taipy-gui/) is installed. -Here is the list of all the available controls that are available in Taipy: +Here is the list of all the scenario management-related controls that are available in Taipy: [core_TOC] diff --git a/tools/postprocess.py b/tools/postprocess.py index 842f858fe..3ba826944 100644 --- a/tools/postprocess.py +++ b/tools/postprocess.py @@ -412,7 +412,7 @@ def on_post_build(env): chart_part = "../" if element_category == "chart" else "" repl += f"
  • Visual Elements
  • " repl += (f"
  • Blocks
  • " if element_category == "blocks" - else f"
  • Generic controls
  • ") + else f"
  • Standard controls
  • ") if chart_part: repl += f"
  • Charts
  • " repl += ""