Преглед на файлове

example for tabular data

namnguyen преди 6 месеца
родител
ревизия
23720c67e4

+ 8 - 0
doc/gui/extension/example_library/example_library.py

@@ -33,6 +33,14 @@ class ExampleLibrary(ElementLibrary):
                 # element, exported as ExampleLabel in front-end/src/index.ts
                 react_component="ExampleLabel",
             ),
+            "basic_table": Element(
+                "data",
+                {
+                    "data": ElementProperty(PropertyType.data),
+                    "rows_per_page": ElementProperty(PropertyType.number),
+                },
+                react_component="BasicTable",
+            ),
         }
 
     # The implementation of the rendering for the "fraction" static element

+ 149 - 0
doc/gui/extension/example_library/front-end/src/BasicTable.tsx

@@ -0,0 +1,149 @@
+import React, { useEffect, useMemo, useState, useCallback, ReactNode } from "react";
+import {
+    createRequestDataUpdateAction,
+    RowType,
+    RowValue,
+    useDispatch,
+    useDispatchRequestUpdateOnFirstRender,
+    useDynamicProperty,
+    useModule,
+} from "taipy-gui";
+
+interface BasicTableProps {
+    id?: string;
+    updateVarName?: string;
+    updateVars?: string;
+    data: Record<string, Record<string, unknown>>;
+    rowsPerPage?: number;
+}
+
+const BasicTable = (props: BasicTableProps) => {
+    const [value, setValue] = useState<Record<string, unknown>>({});
+    const [currentPage, setCurrentPage] = useState(1);
+    const dispatch = useDispatch();
+    const module = useModule();
+    const refresh = props.data?.__taipy_refresh !== undefined;
+    useDispatchRequestUpdateOnFirstRender(dispatch, props.id, module, props.updateVars, props.updateVarName);
+
+    // Memoize column order and columns
+    const [colsOrder, columns] = useMemo(() => {
+        const colsOrder = Object.keys(value || {}).sort();
+        return [colsOrder, value || {}];
+    }, [value]);
+
+    // Memoize rows
+    const rows = useMemo(() => {
+        const rows: RowType[] = [];
+        if (value) {
+            colsOrder.forEach(
+                (col) =>
+                    value[col] &&
+                    (value[col] as RowValue[]).forEach(
+                        (val, idx) => (rows[idx] = rows[idx] || {}) && (rows[idx][col] = val),
+                    ),
+            );
+        }
+        return rows;
+    }, [value, colsOrder]);
+
+    // Memoize paginated rows
+    const paginatedRows = useMemo(() => {
+        if (props.rowsPerPage === undefined) {
+            return rows; // Show all rows if itemsPerPage is undefined
+        }
+        const itemsPerPageValue = props.rowsPerPage ?? 10;
+        const startIndex = (currentPage - 1) * itemsPerPageValue;
+        return rows.slice(startIndex, startIndex + itemsPerPageValue);
+    }, [rows, currentPage, props.rowsPerPage]);
+
+    // Update data callback
+    const updateData = useCallback(() => {
+        if (refresh || !props.data) {
+            dispatch(
+                createRequestDataUpdateAction(
+                    props.updateVarName,
+                    props.id,
+                    module,
+                    colsOrder,
+                    "",
+                    {},
+                    true,
+                    "TabularLibrary",
+                ),
+            );
+        } else {
+            setValue(props.data);
+        }
+    }, [refresh, props.data, colsOrder, props.updateVarName, props.id, dispatch, module]);
+
+    // Effect to update data on mount and when dependencies change
+    useEffect(() => {
+        updateData();
+    }, [updateData]);
+
+    // Render cell content
+    const renderCell = useCallback((cellValue: unknown): ReactNode => {
+        const isDateString =
+            typeof cellValue === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(cellValue);
+        const dateValue = isDateString && !isNaN(Date.parse(cellValue)) ? new Date(cellValue) : null;
+        return dateValue
+            ? dateValue.toLocaleDateString("en-US", {
+                  year: "numeric",
+                  month: "2-digit",
+                  day: "2-digit",
+              })
+            : (cellValue as ReactNode);
+    }, []);
+
+    // Handle next page
+    const handleNextPage = () => {
+        const itemsPerPageValue = props.rowsPerPage ?? 10;
+        setCurrentPage((prevPage) => Math.min(prevPage + 1, Math.ceil(rows.length / itemsPerPageValue)));
+    };
+
+    // Handle previous page
+    const handlePreviousPage = () => {
+        setCurrentPage((prevPage) => Math.max(prevPage - 1, 1));
+    };
+
+    return (
+        <div>
+            <h2>Paginated table</h2>
+            <table border={1} cellPadding={10} cellSpacing={0}>
+                <thead>
+                    {colsOrder.map((col, idx) => (
+                        <td key={col + idx}>{col}</td>
+                    ))}
+                </thead>
+                <tbody>
+                    {paginatedRows.map((row, index) => (
+                        <tr key={"row" + index}>
+                            {colsOrder.map((col, cidx) => (
+                                <td key={"val" + index + "-" + cidx}>{renderCell(row[col])}</td>
+                            ))}
+                        </tr>
+                    ))}
+                </tbody>
+            </table>
+            {props.rowsPerPage !== undefined && (
+                <div>
+                    <button onClick={handlePreviousPage} disabled={currentPage === 1}>
+                        Previous
+                    </button>
+                    <span>
+                        {" "}
+                        Page {currentPage} of {Math.ceil(rows.length / (props.rowsPerPage ?? 10))}{" "}
+                    </span>
+                    <button
+                        onClick={handleNextPage}
+                        disabled={currentPage === Math.ceil(rows.length / (props.rowsPerPage ?? 10))}
+                    >
+                        Next
+                    </button>
+                </div>
+            )}
+        </div>
+    );
+};
+
+export default BasicTable;

+ 2 - 1
doc/gui/extension/example_library/front-end/src/index.ts

@@ -7,5 +7,6 @@
 // Note that we export the 'ColoredLabel' component as 'ExampleLabel', which is
 // the name used in the element declaration in the element library.
 import ColoredLabel from "./ColoredLabel";
+import BasicTable from "./BasicTable";
 
-export { ColoredLabel as ExampleLabel };
+export { ColoredLabel as ExampleLabel, BasicTable };

+ 18 - 0
doc/gui/extension/table_data.py

@@ -0,0 +1,18 @@
+import random
+
+from example_library import ExampleLibrary
+
+from taipy.gui import Gui
+
+data = {
+    "date": [f"2000-03-{(i % 31) + 1:02d}T00:00:00.000Z" for i in range(100)],
+    "volume": [random.randint(1000, 10000) for _ in range(100)],
+    "price": [round(random.uniform(100, 200), 3) for _ in range(100)],
+}
+
+page = """
+<|{data}|example.basic_table|rows_per_page=5|>
+"""
+
+if __name__ == "__main__":
+    Gui(page, libraries=[ExampleLibrary()]).run(port=3001, use_reloader=True, title="Basic Table", debug=True)