Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: created section for extension list-of-values #328

Merged
merged 12 commits into from
Dec 6, 2024
Binary file added docs/userman/gui/extension/chess_game-d.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/userman/gui/extension/chess_game-l.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
2 changes: 1 addition & 1 deletion docs/userman/gui/extension/dynamic_element/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -355,5 +355,5 @@ Here are the sections that address the different use cases that your custom
element may need:

- [Scalar properties](scalar_props.md)
- [Tabular data properties](tabular_data_props.md)
- [Tabular data properties](../extension_tabular_data)

155 changes: 155 additions & 0 deletions docs/userman/gui/extension/dynamic_element/list_of_value_props.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
In the previous section, we discussed creating dynamic elements. In Taipy GUI, lists can be bound to Python variables or
expressions, allowing real-time updates in the UI. Custom elements that handle lists should define their properties using
the `PropertyType^` class to specify the format of list values. This setup supports multidimensional data binding, ensuring
that any changes to the list automatically refresh the display.

# Using list of values

In this section, we'll design a visual component that represents a list of programming languages. Each language will be
displayed with its name and an icon.

This dynamic component will take a property containing a list of values and render them as a list.
namnguyen20999 marked this conversation as resolved.
Show resolved Hide resolved
When a Python variable is bound to this property,
any updates to the variable will instantly reflect in the displayed list, enabling real-time synchronization.

## Declaring a dynamic element {data-source="gui:doc/extension/example_library/example_library.py#L45"}

Starting from the code mentioned above, here is how you would declare this new element:
```python title="example_library.py"
from taipy.gui.extension import Element, ElementLibrary, ElementProperty, PropertyType

class ExampleLibrary(ElementLibrary):
def __init__(self) -> None:
# Initialize the set of visual elements for this extension library
self.elements = {
"visual_label_list": Element(
"lov",
{
"lov": ElementProperty(PropertyType.lov),
"sort": ElementProperty(PropertyType.string),
},
# The name of the React component (VisualLabelList) that implements this custom
# element, exported as LabeledItemList in front-end/src/index.ts
react_component="VisualLabelList",
)
}
```
The detailed explanation of the code is as follows:

- The *visual_label_list* element includes two properties: *lov* and *sort*.
- The *lov* property has the type `PropertyType.lov^`, meaning it holds a list of values and is dynamic.
- The *sort* property has the type `PropertyType.string^`, meaning it holds a string value. Because this property is
static, it is not updated automatically should the bound variable be changed.
- *get_name()* and *get_elements()* remain the same as in the previous stages of building the extension library example.

## Creating the React component {data-source="gui:doc/extension/example_library/front-end/src/VisualLabelList.tsx"}

The React component for the *visual_label_list* element is defined as follows:
```tsx title="VisualLabelList.tsx" linenums="1"
import React, { useMemo } from "react";
import { LoV, useLovListMemo } from "taipy-gui";

interface VisualLabelListProps {
lov?: LoV;
defaultLov?: string;
sort?: "asc" | "desc";
}

const styles = {
listItem: {
display: "flex",
alignItems: "center",
},
image: {
marginRight: "8px",
width: "1em",
height: "1em",
},
};

const VisualLabelList: React.FC<VisualLabelListProps> = ({ lov, defaultLov = "", sort }) => {
const lovList = useLovListMemo(lov, defaultLov);

const sortedLovList = useMemo(() => {
if (sort) {
return lovList.slice().sort((a, b) => {
return sort === "asc" ? a.id.localeCompare(b.id) : b.id.localeCompare(a.id);
});
}
return lovList;
}, [lovList, sort]);

return (
<div>
<ul>
{sortedLovList.map((item, index) => (
<li key={index} style={styles.listItem}>
{typeof item.item === "string" ? null : (
<img src={item.item.path} alt={item.item.text} style={styles.image} />
)}
{item.id}
</li>
))}
</ul>
</div>
);
};

export default VisualLabelList;
```

The detailed explanation of the code is as follows:

- The *lov* property, defined as `PropertyType.lov^`, expects a value of type
[`LoV`](../../../../refmans/reference_guiext/type-aliases/LoV.md).
- Using Taipy GUI’s [`useLovListMemo()`](../../../../refmans/reference_guiext/functions/useLovListMemo.md) hook, we
create a memoized list of values based on this *lov* property, efficiently managing the component’s list of values.
- To enhance this list, we can apply sorting using the *sort* property, which accepts either “asc” for ascending or
“desc” for descending order. Since *sort* is static, a default value is unnecessary. When set to "asc" or "desc," the
list is sorted accordingly. If *sort* is not defined in the script, the list displays values in their original order.

## Exporting the React component {data-source="gui:doc/extension/example_library/front-end/src/index.ts"}

When the component is entirely defined, it must be exported by the library's JavaScript bundle.
This is done by adding the export directive in the file *<project dir>/<package dir>front-end/src/index.ts*.

```js title="index.ts"
import VisualLabelList from "./VisualLabelList";

export { VisualLabelList };
```

## Using the element {data-source="gui:doc/extension/visual_label_list.py"}

To use the *visual_label_list* element in a Python script, you can follow the example below:
```py title="List of items"
languages = [
["Python", Icon("images/python.png", "Python logo")],
["JavaScript", Icon("images/javascript.png", "JavaScript logo")],
["TypeScript", Icon("images/typescript.png", "TypeScript logo")],
["Java", Icon("images/java.png", "Java logo")],
["C++", Icon("images/cpp.png", "C++ logo")],
]

page = """
<|{languages}|example.visual_label_list|sort=asc|>
"""
```

In this example, the *languages* list contains a [`list of value`](../../binding.md#list-of-values),
each with a name and an icon. The *page* variable contains a string that uses the *visual_label_list* element to display
the list of languages in ascending order. The *sort* property is set to “asc” to sort the list alphabetically by
language name. When the page is rendered, the list will display the languages in the specified order.

By following these steps, you can create a dynamic element that displays a list of values and updates in real time as
the underlying data changes. This approach allows you to build custom elements that can manage and display collections
of data, providing a more interactive and responsive user interface.

When you run this application, the page displays the element like this:

<figure>
<img src="../visual_label_list-d.png" class="visible-dark"/>
<img src="../visual_label_list-l.png" class="visible-light"/>
<figcaption>Visual labeled list</figcaption>
</figure>

50 changes: 35 additions & 15 deletions docs/userman/gui/extension/extension_assets.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,41 @@
# Accessing the library assets

Say we want to display a small image next to the text. That would be useful in the
situation where we apply our *caption* control to represent a company name along with
its logo.
In certain scenarios, you might want to enrich your user interface by displaying a small image alongside text.
For example, when using a caption control to represent a company name along with its logo,
adding an image can enhance visual context and usability.

In HTML, you would create an `img` tag, where the source URL is set to the path of the
image. However, in order to protect the application from attacks, Taipy provides the
method `ElementLibrary.get_resource()` that let the application filter what resources
are requested, and return the actual files according to the application setting.
Traditionally, in HTML, you would use an `img` tag with the src attribute pointing to the image’s file path.
namnguyen20999 marked this conversation as resolved.
Show resolved Hide resolved
However, directly referencing resources in this way can expose your application to potential security vulnerabilities,
such as unauthorized access or malicious resource requests.

To mitigate these risks, Taipy introduces the `ElementLibrary.get_resource()^` method.
This method acts as a secure gateway for resource handling,
allowing the application to validate and filter resource requests based on predefined settings.
It ensures that only authorized and properly configured files are served, protecting your application while maintaining
functionality.

## Declaring element {data-source="gui:doc/extension/example_library/example_library.py#L62"}

!!! warning "Work in Progress"
This section still requires significant work, which is in progress.
At this time, Taipy GUI provides a custom element library example
with lengthy explanations on how to build it.<br/>
Please look into the `doc/extension` directory where Taipy GUI is
installed for more information.<br/>
You can also look at this example directly on
[GitHub](https://github.com/Avaiga/taipy/tree/[BRANCH]/doc/gui/extension).
```python title="example_library.py"
import base64

from taipy.gui.extension import Element, ElementLibrary, ElementProperty, PropertyType


class ExampleLibrary(ElementLibrary):
def __init__(self) -> None:
# Initialize the set of visual elements for this extension library
logo_path = self.get_resource("assets/logo.png")
with open(logo_path, "rb") as f:
logo_base64 = base64.b64encode(f.read()).decode("utf-8")

self.elements = {
"logo_with_text": Element(
"text",
{
"text": ElementProperty(PropertyType.string),
"logo_path": ElementProperty(PropertyType.string, default_value=logo_base64),
},
)
}
```
Loading
Loading