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

backport on_create (#232) (#254)

* #232 backport on_create

* #232 backport on_creation before|after

* #232 one on_creation is enough

* name => label

---------

Co-authored-by: Fred Lefévère-Laoide <Fred.Lefevere-Laoide@Taipy.io>
Fred Lefévère-Laoide преди 1 година
родител
ревизия
02100933d8
променени са 4 файла, в които са добавени 53 реда и са изтрити 8 реда
  1. 1 1
      gui/package.json
  2. 5 4
      gui/src/ScenarioSelector.tsx
  3. 41 3
      src/taipy/gui_core/GuiCoreLib.py
  4. 6 0
      src/taipy/gui_core/viselements.json

+ 1 - 1
gui/package.json

@@ -1,6 +1,6 @@
 {
   "name": "taipy-gui-core",
-  "version": "2.3.1",
+  "version": "2.4.0",
   "private": true,
   "devDependencies": {
     "@types/react": "^18.0.15",

+ 5 - 4
gui/src/ScenarioSelector.tsx

@@ -96,6 +96,7 @@ interface ScenarioSelectorProps {
     scenarios?: Cycles | Scenarios;
     onScenarioCrud: string;
     onChange?: string;
+    onCreation?: string;
     coreChanged?: Record<string, unknown>;
     updateVars: string;
     configs?: Array<[string, string]>;
@@ -305,7 +306,7 @@ const ScenarioEditDialog = ({ scenario, submit, open, actionEdit, configs, close
             values.properties = [...properties];
             setProperties([]);
             submit(actionEdit, false, values);
-            form.resetForm();
+            form.resetForm({ values: { ...emptyScenario, config: configs?.length === 1 ? configs[0][0] : "" } });
             close();
         },
     });
@@ -320,7 +321,7 @@ const ScenarioEditDialog = ({ scenario, submit, open, actionEdit, configs, close
                       date: scenario[ScFProps.creation_date],
                       properties: [],
                   }
-                : emptyScenario
+                : { ...emptyScenario, config: configs?.length === 1 ? configs[0][0] : "" }
         );
         setProperties(
             scenario ? scenario[ScFProps.properties].map(([k, v], i) => ({ id: i + "", key: k, value: v })) : []
@@ -594,13 +595,13 @@ const ScenarioSelector = (props: ScenarioSelectorProps) => {
 
     const onSubmit = useCallback(
         (...values: unknown[]) => {
-            dispatch(createSendActionNameAction(id, module, props.onScenarioCrud, ...values));
+            dispatch(createSendActionNameAction(id, module, props.onScenarioCrud, ...values, props.onCreation));
             if (values.length > 1 && values[1]) {
                 // delete requested => unselect current node
                 unselect();
             }
         },
-        [id, props.onScenarioCrud, dispatch, module, unselect]
+        [id, props.onScenarioCrud, dispatch, module, props.onCreation, unselect]
     );
 
     // Refresh on broadcast

+ 41 - 3
src/taipy/gui_core/GuiCoreLib.py

@@ -34,6 +34,7 @@ from taipy.core.notification import CoreEventConsumerBase, EventEntityType
 from taipy.core.notification.event import Event
 from taipy.core.notification.notifier import Notifier
 from taipy.gui import Gui, State
+from taipy.gui._warnings import _warn
 from taipy.gui.extension import Element, ElementLibrary, ElementProperty, PropertyType
 from taipy.gui.gui import _DoNotUpdate
 from taipy.gui.utils import _TaipyBase
@@ -75,8 +76,12 @@ class _GuiCoreScenarioAdapter(_TaipyBase):
                     scenario.cycle.get_simple_label() if scenario.cycle else "",
                     scenario.get_simple_label(),
                     list(scenario.tags) if scenario.tags else [],
-                    [(k, v) for k, v in scenario.properties.items() if k not in _GuiCoreScenarioAdapter.__INNER_PROPS] if scenario.properties else [],
-                    [(p.id, p.get_simple_label(), is_submittable(p)) for p in scenario.pipelines.values()] if scenario.pipelines else [],
+                    [(k, v) for k, v in scenario.properties.items() if k not in _GuiCoreScenarioAdapter.__INNER_PROPS]
+                    if scenario.properties
+                    else [],
+                    [(p.id, p.get_simple_label(), is_submittable(p)) for p in scenario.pipelines.values()]
+                    if scenario.pipelines
+                    else [],
                     list(scenario.properties.get("authorized_tags", [])) if scenario.properties else [],
                     is_deletable(scenario),  # deletable
                     is_promotable(scenario),
@@ -230,6 +235,7 @@ class _GuiCoreContext(CoreEventConsumerBase):
         delete = args[1]
         data = args[2]
         scenario = None
+
         name = data.get(_GuiCoreContext.__PROP_ENTITY_NAME)
         if update:
             scenario_id = data.get(_GuiCoreContext.__PROP_ENTITY_ID)
@@ -253,6 +259,37 @@ class _GuiCoreContext(CoreEventConsumerBase):
                 state.assign(_GuiCoreContext._SCENARIO_SELECTOR_ERROR_VAR, f"Invalid date ({date_str}).{e}")
                 return
             try:
+                gui: Gui = state._gui
+                on_creation = args[3] if len(args) > 3 and isinstance(args[3], str) else None
+                on_creation_function = gui._get_user_function(on_creation) if on_creation else None
+                if callable(on_creation_function):
+                    try:
+                        res = gui._call_function_with_state(
+                            on_creation_function,
+                            [
+                                id,
+                                on_creation,
+                                {
+                                    "config": scenario_config,
+                                    "date": date,
+                                    "label": name,
+                                    "properties": {v.get("key"): v.get("value") for v in data.get("properties", [])},
+                                },
+                            ],
+                        )
+                        if isinstance(res, Scenario):
+                            # everything's fine
+                            state.assign(_GuiCoreContext._SCENARIO_SELECTOR_ERROR_VAR, "")
+                            return
+                        if res:
+                            # do not create
+                            state.assign(_GuiCoreContext._SCENARIO_SELECTOR_ERROR_VAR, f"{res}")
+                            return
+                    except Exception as e:  # pragma: no cover
+                        if not gui._call_on_exception(on_creation, e):
+                            _warn(f"on_creation(): Exception raised in '{on_creation}()':\n{e}")
+                else:
+                    _warn(f"on_creation(): '{on_creation}' is not a function.")
                 scenario = create_scenario(scenario_config, date, name)
             except Exception as e:
                 state.assign(_GuiCoreContext._SCENARIO_SELECTOR_ERROR_VAR, f"Error creating Scenario. {e}")
@@ -268,7 +305,7 @@ class _GuiCoreContext(CoreEventConsumerBase):
                         for prop in props:
                             key = prop.get("key")
                             if key and key not in _GuiCoreContext.__SCENARIO_PROPS:
-                                sc._properties[key] = prop.get("value")
+                                sc.properties[key] = prop.get("value")
                         state.assign(_GuiCoreContext._SCENARIO_SELECTOR_ERROR_VAR, "")
                     except Exception as e:
                         state.assign(_GuiCoreContext._SCENARIO_SELECTOR_ERROR_VAR, f"Error creating Scenario. {e}")
@@ -389,6 +426,7 @@ class _GuiCore(ElementLibrary):
                 "on_change": ElementProperty(PropertyType.function),
                 "height": ElementProperty(PropertyType.string, "50vh"),
                 "class_name": ElementProperty(PropertyType.dynamic_string),
+                "on_creation": ElementProperty(PropertyType.function),
             },
             inner_properties={
                 "scenarios": ElementProperty(PropertyType.lov, f"{{{__CTX_VAR_NAME}.get_scenarios()}}"),

+ 6 - 0
src/taipy/gui_core/viselements.json

@@ -55,6 +55,12 @@
             "type": "str",
             "default_value": "50vh",
             "doc": "The maximum height, in CSS units, of the tree."
+          },
+          {
+            "name": "on_creation",
+            "type": "Callback",
+            "doc": "The name of the function that is triggered when a scenario will be or has been created.<br/><br/>All the parameters of that function are optional:\n<ul>\n<li>state (<code>State^</code>): the state instance.</li>\n<li>id (str): the identifier of the element.</li>\n<li>action (str): the name of the action that provoked the change.</li>\n<li>payload (dict): the details on this callback's invocation.<br/>\nThis dictionary has the following keys:\n<ul>\n<li>config: the selected configuration.</li>\n<li>date: the selected date.</li>\n<li>label: the user entered label.</li>\n<li>properties: a key/value dictionnary.</li>\n</ul>\n</li>\n<li>return: the callback can return a scenario, an error message (the scenario will not be created) or None (the scenario will be created with the user parameters).</li>\n</ul>",
+            "signature": [["state", "State"], ["id", "str"], ["action", "str"], ["payload", "dict"]]
           }
         ]
       }