diff --git a/docs/userman/gui/extension/dynamic_element/chess_game-d.png b/docs/userman/gui/extension/chess_game-d.png similarity index 100% rename from docs/userman/gui/extension/dynamic_element/chess_game-d.png rename to docs/userman/gui/extension/chess_game-d.png diff --git a/docs/userman/gui/extension/dynamic_element/chess_game-l.png b/docs/userman/gui/extension/chess_game-l.png similarity index 100% rename from docs/userman/gui/extension/dynamic_element/chess_game-l.png rename to docs/userman/gui/extension/chess_game-l.png diff --git a/docs/userman/gui/extension/dynamic_element/index.md b/docs/userman/gui/extension/dynamic_element/index.md index 72ee1ad73..5893112a8 100644 --- a/docs/userman/gui/extension/dynamic_element/index.md +++ b/docs/userman/gui/extension/dynamic_element/index.md @@ -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) diff --git a/docs/userman/gui/extension/extension_data.md b/docs/userman/gui/extension/extension_data.md deleted file mode 100644 index adb7694bd..000000000 --- a/docs/userman/gui/extension/extension_data.md +++ /dev/null @@ -1,211 +0,0 @@ -# Using tabular data - -In this section, we will expand the custom element library, initially created in the Static Elements section, by -adding a dynamic custom element. - -This dynamic element will accept a property containing tabular data and display it within a table. When a Python -variable is bound to this property, updates to the variable will immediately reflect in the table content shown on -the front end, ensuring real-time synchronization. - -## Declaring a dynamic element {data-source="gui:doc/extension/example_library/example_library.py#L36"} - -```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 = { - "game_table": Element( - "data", - { - "data": ElementProperty(PropertyType.data), - }, - # The name of the React component (GameTable) that implements this custom - # element, exported as GameTable in front-end/src/index.ts - # react_component="GameTable", - ), - } - def get_name(self) -> str: - return "example" - - def get_elements(self) -> dict: - return self.elements - - def get_scripts(self) -> list[str]: - # Only one JavaScript bundle for this library. - return ["front-end/dist/exampleLibrary.js"] -``` - -The detailed explanation of the code is as follows: - -- The `game_table` element includes a single property: `data`. -- The `data` property has the type `PropertyType.data`, meaning it holds a data value and is dynamic. -- The `get_name` method in the `ExampleLibrary` class returns the name of the library as a string. This name is used - to identify the library within the Taipy GUI framework. -- The `get_elements` method in the `ExampleLibrary` class returns a dictionary of elements that are part of this - library. Each element is defined with its properties and associated React component. - -## Creating the React component {data-source="gui:doc/extension/example_library/front-end/src/GameTable.tsx"} - -The React component for the `game_table` element is defined as follows: - -```jsx -import React, { useEffect, useMemo, useState } from "react"; -import { - createRequestDataUpdateAction, - useDispatch, - useDispatchRequestUpdateOnFirstRender, - useModule, - TaipyDynamicProps, - TableValueType, - RowType, - RowValue, -} from "taipy-gui"; - -interface GameTableProps extends TaipyDynamicProps { - data: TableValueType; -} - -const pageKey = "no-page-key"; - -const GameTable = (props: GameTableProps) => { - const { data, updateVarName = "", updateVars = "", id } = props; - const [value, setValue] = useState>>({}); - const dispatch = useDispatch(); - const module = useModule(); - const refresh = data?.__taipy_refresh !== undefined; - useDispatchRequestUpdateOnFirstRender(dispatch, id, module, updateVars); - - const colsOrder = useMemo(() => { - return Object.keys(value); - }, [value]); - - const rows = useMemo(() => { - const rows: RowType[] = []; - if (value) { - Object.entries(value).forEach(([col, colValues]) => { - colValues.forEach((val, idx) => { - rows[idx] = rows[idx] || {}; - rows[idx][col] = val; - }); - }); - } - return rows; - }, [value]); - - useEffect(() => { - if (refresh || !data || data[pageKey] === undefined) { - dispatch( - createRequestDataUpdateAction( - updateVarName, - id, - module, - colsOrder, - pageKey, - {}, - true, - "ExampleLibrary", - ), - ); - } else { - setValue(data[pageKey]); - } - }, [refresh, data, colsOrder, updateVarName, id, dispatch, module]); - - return ( -
- - - {rows.map((row, index) => ( - - {colsOrder.map((col, cidx) => ( - - ))} - - ))} - -
{row[col]}
-
- ); -}; - -export default GameTable; -``` - -The detailed explanation of the code is as follows: - -- We use the [useDispatch](https://docs.taipy.io/en/latest/refmans/reference_guiext/functions/useDispatch/) hook to - dispatch actions to the store and initiate backend communications. -- Additionally, the [useModule](https://docs.taipy.io/en/latest/refmans/reference_guiext/functions/useModule/) hook - retrieves the page module, enabling correct execution of backend functions. -- To request an update for every dynamic property of an element on initial render, we use the - [useDispatchRequestUpdateOnFirstRender](https://docs.taipy.io/en/latest/refmans/reference_guiext/functions/useDispatchRequestUpdateOnFirstRender/#function-usedispatchrequestupdateonfirstrender) - hook provided by the Taipy GUI Extension API. This hook takes five parameters: - - `dispatch`: The React dispatcher associated with the context. - - `id`: The identifier of the element. - - `context`: The execution context. - - `updateVars`: The content of the `updateVars` property. -- We also dispatch the - [createRequestDataUpdateAction](https://docs.taipy.io/en/latest/refmans/reference_guiext/functions/createRequestUpdateAction/) - hook to create a request data update action, which updates the context by invoking the - [get_data](https://docs.taipy.io/en/latest/refmans/reference/pkg_taipy/pkg_gui/pkg_extension/ElementLibrary/#taipy.gui.extension.ElementLibrary.get_data) - method of the backend library. This invocation triggers an update of front-end elements holding the data. - -The [createRequestDataUpdateAction](https://docs.taipy.io/en/latest/refmans/reference_guiext/functions/createRequestUpdateAction/) -hook accepts eight parameters: - -- `name`: The name of the variable containing the requested data, as received in the property. -- `id`: The identifier of the visual element. -- `context`: The execution context. -- `columns`: The list of columns required by the element emitting this action. -- `pageKey`: The unique identifier for the data received from this action. -- `payload`: The payload, specific to the component type (e.g., table, chart). -- `allData`: A flag indicating if all data is requested. -- `library`: The name of the extension library. - -## 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 JavaScript library. -This is done by adding the export directive in the file `//front-end/src/index.ts`. - -```js -import GameTable from "./GameTable"; - -export { GameTable }; -``` - -## Using the element in the application {data-source="gui:doc/extension/table_chess_game.py"} - -In the example below, we use the `game_table` element to display a chess game board. The board is represented as a -two-dimensional list of strings, where each string represents a chess piece. The board is displayed in a table format -using the `game_table` element. - -```py -from example_library import ExampleLibrary - -from taipy.gui import Gui - -data = [ - ["♜", "♞", "♝", "♛", "♚", "♝", "♞", "♜"] - ["♟", "♟", "♟", "♟", "♟", "♟", "♟", "♟"], - ["", "", "", "", "", "", "", ""], - ["", "", "", "", "", "", "", ""], - ["", "", "", "", "", "", "", ""], - ["", "", "", "", "", "", "", ""], - ["♙", "♙", "♙", "♙", "♙", "♙", "♙", "♙"], - ["♖", "♘", "♗", "♕", "♔", "♗", "♘", "♖"], -] - -page = """ -## Chess Game -<|{data}|example.game_table|> -""" - -if __name__ == "__main__": - Gui(page, libraries=[ExampleLibrary()]).run(title="Chess Game") -``` - -When you run this application, the page displays the element like this: - - diff --git a/docs/userman/gui/extension/extension_list_of_values.md b/docs/userman/gui/extension/extension_list_of_values.md new file mode 100644 index 000000000..91b0eb9b3 --- /dev/null +++ b/docs/userman/gui/extension/extension_list_of_values.md @@ -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 component will take a property containing a list of values and render them as a list. +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 the element {data-source="gui:doc/extension/example_library/example_library.py#L52"} + +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" +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 = ({ 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 ( +
+
    + {sortedLovList.map((item, index) => ( +
  • + {typeof item.item === "string" ? null : ( + {item.item.text} + )} + {item.id} +
  • + ))} +
+
+ ); +}; + +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 */front-end/src/index.ts*. + +```ts 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: +```python 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 the 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: + +
+ + +
Visual labeled list
+
+ diff --git a/docs/userman/gui/extension/dynamic_element/tabular_data_props.md b/docs/userman/gui/extension/extension_tabular_data.md similarity index 82% rename from docs/userman/gui/extension/dynamic_element/tabular_data_props.md rename to docs/userman/gui/extension/extension_tabular_data.md index b0dd7733e..4b1919ab9 100644 --- a/docs/userman/gui/extension/dynamic_element/tabular_data_props.md +++ b/docs/userman/gui/extension/extension_tabular_data.md @@ -1,4 +1,4 @@ -The previous section on [Scalar properties](scalar_props.md) shows how to create a dynamic element +The previous section on [Scalar properties](dynamic_element/scalar_props.md) shows how to create a dynamic element that holds a scalar value. However, when dealing with collections of data, such as tabular data, we need a more complex approach to support arrays or tables that can be dynamically updated. @@ -23,15 +23,15 @@ this approach. # Using tabular data In this section, we will expand the dynamic element library, initially created in the -[Scalar properties](scalar_props.md) section, by adding a new dynamic custom element. +[Scalar properties](dynamic_element/scalar_props.md) section, by adding a chessboard element. -This dynamic element will accept a property containing tabular data and display it within a table. -When a Python variable is bound to this property, updates to the variable will immediately reflect -in the table content shown on the page, ensuring real-time synchronization. +This element will accept a property containing the state of a chess game +and display it as a visually interactive chessboard. When a Python variable is bound to this property, +updates to the variable will immediately update the chessboard displayed on the page, ensuring real-time synchronization. -## Declaring a dynamic element {data-source="gui/extension/example_library/example_library.py#L36"} +## Declaring a dynamic element {data-source="gui:doc/extension/example_library/example_library.py#L43"} Starting from the code mentioned above, here is how you would declare this new element: -```py title="example_library.py" +```python title="example_library.py" from taipy.gui.extension import Element, ElementLibrary, ElementProperty, PropertyType class ExampleLibrary(ElementLibrary): @@ -49,8 +49,8 @@ class ExampleLibrary(ElementLibrary): ), } ``` -The declaration of this dynamic element is very similar to what we created in the -[Scalar properties](scalar_props.md). +The declaration of this element is very similar to what we created in the +[Scalar properties](dynamic_element/scalar_props.md). The detailed explanation of the code is as follows: @@ -63,11 +63,11 @@ The detailed explanation of the code is as follows: that are part of this library. Each element is defined with its properties and associated React component. -## Creating the React component {data-source="gui/extension/example_library/front-end/src/GameTable.tsx"} +## Creating the React component {data-source="gui:doc/extension/example_library/front-end/src/GameTable.tsx"} The React component for the *game_table* element is defined as follows: -```tsx title="GameTable.tsx" linenums="1" +```tsx title="GameTable.tsx" import React, { useEffect, useMemo, useState } from "react"; import { createRequestDataUpdateAction, @@ -152,24 +152,24 @@ export default GameTable; The detailed explanation of the code is as follows: -- We use the [`useDispatch()`](../../../../refmans/reference_guiext/functions/useDispatch.md) hook +- We use the [`useDispatch()`](../../refmans/reference_guiext/functions/useDispatch.md) hook to dispatch actions to the store and initiate backend communications. -- Additionally, the [`useModule()`](../../../../refmans/reference_guiext/functions/useModule.md) +- Additionally, the [`useModule()`](../../refmans/reference_guiext/functions/useModule.md) hook retrieves the page module, enabling correct execution of backend functions. - To request an update for every dynamic property of an element on initial render, we use the - [`useDispatchRequestUpdateOnFirstRender()`](../../../../refmans/reference_guiext/functions/useDispatchRequestUpdateOnFirstRender.md) + [`useDispatchRequestUpdateOnFirstRender()`](../../refmans/reference_guiext/functions/useDispatchRequestUpdateOnFirstRender.md) hook provided by the Taipy GUI Extension API. This hook takes five parameters: - *dispatch*: The React dispatcher associated with the context. - *id*: The identifier of the element. - *context*: The execution context. - *updateVars*: The content of the *updateVars* property. - We also dispatch the - [`createRequestDataUpdateAction()`](../../../../refmans/reference_guiext/functions/createRequestDataUpdateAction.md) + [`createRequestDataUpdateAction()`](../../refmans/reference_guiext/functions/createRequestDataUpdateAction.md) hook to create a request data update action, which updates the context by invoking the `(ElementLibrary.)get_data()^` method of the backend library. This invocation triggers an update of front-end elements holding the data. -The [`createRequestDataUpdateAction()`](../../../../refmans/reference_guiext/functions/createRequestUpdateAction.md) +The [`createRequestDataUpdateAction()`](../../refmans/reference_guiext/functions/createRequestUpdateAction.md) hook accepts eight parameters: - *name*: The name of the variable containing the requested data, as received in the property. @@ -181,18 +181,18 @@ hook accepts eight parameters: - *allData*: A flag indicating if all the data is requested. - *library*: The name of the extension library. -## Exporting the React component {data-source="gui/extension/example_library/front-end/src/index.ts"} +## 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 JavaScript library. This is done by adding the export directive in the file */front-end/src/index.ts*. -```js title="index.ts" +```ts title="index.ts" import GameTable from "./GameTable"; export { GameTable }; ``` -## Using the element {data-source="gui/extension/table_chess_game.py"} +## Using the element {data-source="gui:doc/extension/table_chess_game.py"} In the example below, we use the *game_table* element to display a chess game board. The data is represented as a two-dimensional list of strings, where each string represents a chess @@ -201,7 +201,7 @@ The board is displayed in a table format using the *game_table* element.
We can see how the data property of the control is bound to the Python variable *data*, using the default property syntax. -```py +```python title="table_chess_game.py" data = [ ["♜", "♞", "♝", "♛", "♚", "♝", "♞", "♜"] ["♟", "♟", "♟", "♟", "♟", "♟", "♟", "♟"], diff --git a/docs/userman/gui/extension/index.md b/docs/userman/gui/extension/index.md index 98f9e21a4..888aada6b 100644 --- a/docs/userman/gui/extension/index.md +++ b/docs/userman/gui/extension/index.md @@ -230,7 +230,7 @@ make your way from one example to the next. - [Custom static elements](static_element.md) - [Custom dynamic elements](dynamic_element/index.md) - [Using scalar properties](dynamic_element/scalar_props.md) -- [Using List of Values](dynamic_element/list_of_value_props.md) -- [Using tabular data](dynamic_element/tabular_data_props.md) +- [Using List of Values](extension_list_of_values.md) +- [Using tabular data](extension_tabular_data.md) - [Accessing assets](extension_assets.md) - [Packaging an element library](extension_packaging.md) diff --git a/docs/userman/gui/extension/dynamic_element/visual_label_list-d.png b/docs/userman/gui/extension/visual_label_list-d.png similarity index 100% rename from docs/userman/gui/extension/dynamic_element/visual_label_list-d.png rename to docs/userman/gui/extension/visual_label_list-d.png diff --git a/docs/userman/gui/extension/dynamic_element/visual_label_list-l.png b/docs/userman/gui/extension/visual_label_list-l.png similarity index 100% rename from docs/userman/gui/extension/dynamic_element/visual_label_list-l.png rename to docs/userman/gui/extension/visual_label_list-l.png diff --git a/mkdocs.yml_template b/mkdocs.yml_template index 8d78a580f..a90313384 100644 --- a/mkdocs.yml_template +++ b/mkdocs.yml_template @@ -100,8 +100,8 @@ nav: - "Dynamic Elements": - userman/gui/extension/dynamic_element/index.md - "Scalar properties": userman/gui/extension/dynamic_element/scalar_props.md - - "List of values properties": userman/gui/extension/dynamic_element/list_of_value_props.md - - "Tabular data properties": userman/gui/extension/dynamic_element/tabular_data_props.md + - "List of Values Elements": userman/gui/extension/extension_list_of_values.md + - "Tabular Elements": userman/gui/extension/extension_tabular_data.md - "Scenario features": - "Data integration":