@@ -34483,7 +34483,12 @@ const GROUP_DEFAULTS = {
3448334483 }
3448434484 // Merged in on the way up (see above), so on the way down it takes the
3448534485 // whole object and picks out its own keys — the same value it produced.
34486- if (isNamelessGrouping(child, child.uiState)) {
34486+ // Only for a child with no name of its own: the same condition the
34487+ // aggregate applies before merging. A NAMED child holding an object (a
34488+ // `type="object"` picker, say) answers for one key, and the object it is
34489+ // not mentioned in says nothing about it — handing it the whole value
34490+ // would make it hold the group.
34491+ if (!childName && isNamelessGrouping(child, child.uiState)) {
3448734492 return newUIState;
3448834493 }
3448934494 if (
@@ -34771,7 +34776,17 @@ const useUIGroupStateController = (
3477134776 e,
3477234777 `${controlType}.getUIState -> ${JSON.stringify(groupUIState)}`,
3477334778 );
34774- if (notifyExternal === true) {
34779+ if (notifyExternal === true || notifyExternal === "if-it-moves") {
34780+ if (
34781+ notifyExternal === "if-it-moves" &&
34782+ compareTwoJsValues(groupUIState, controller.uiState)
34783+ ) {
34784+ // A child confirming what it already shows, to a group already
34785+ // worth it: the value arrived while the popup was open and the
34786+ // group reacted then. One trip through a picker is one answer, so
34787+ // there is nothing left to say here.
34788+ return;
34789+ }
3477534790 // Somebody answered: what the group is worth is what its children say
3477634791 // between them, from here on.
3477734792 controller.stateGivenFromAbove = false;
@@ -35085,7 +35100,7 @@ const useUIGroupStateController = (
3508535100 onChildUIAction: (
3508635101 childUIStateController,
3508735102 e,
35088- { stateChanged, silent },
35103+ { stateChanged, silent, onlyIfGroupValueMoves },
3508935104 ) => {
3509035105 const delegatedTo = delegatedChildrenRef.current.get(
3509135106 childUIStateController,
@@ -35094,6 +35109,7 @@ const useUIGroupStateController = (
3509435109 delegatedTo.onChildUIAction(childUIStateController, e, {
3509535110 stateChanged,
3509635111 silent,
35112+ onlyIfGroupValueMoves,
3509735113 });
3509835114 return;
3509935115 }
@@ -35108,7 +35124,11 @@ const useUIGroupStateController = (
3510835124 );
3510935125 if (stateChanged) {
3511035126 onChange(e, {
35111- notifyExternal: silent ? "silent" : true,
35127+ notifyExternal: silent
35128+ ? "silent"
35129+ : onlyIfGroupValueMoves
35130+ ? "if-it-moves"
35131+ : true,
3511235132 actingChild: childUIStateController,
3511335133 });
3511435134 } else {
@@ -51839,13 +51859,28 @@ const isUIStateHeld = (controller) => {
5183951859 * `command` on a control is its reaction to being used, and this is a
5184051860 * confirmation happening elsewhere, whose own command (the picker's) is already
5184151861 * running.
51862+ *
51863+ * And up one, to the group the control answers to: a picker inside a form is
51864+ * one of its fields, and a form is worth what its fields say. What the popup
51865+ * put there arrived through a mount sync, which is deliberately silent — a
51866+ * popup opening is nobody answering (see onChange in ui_state_controller.js) —
51867+ * so this is the first moment the form can be told.
51868+ *
51869+ * That group only hears about it when it moves: a value the user picked in the
51870+ * popup reached it already, and one trip through a picker is one answer. Down
51871+ * the subtree the reaction re-runs either way — a control saying again what it
51872+ * holds says the same thing, and it is where a signal is written.
5184251873 */
5184351874const commitUIStateAsAnswer = (controller, e) => {
5184451875 if (!controller) {
5184551876 return;
5184651877 }
5184751878 const answering = controller.facadeChild || controller;
5184851879 commitSubtree(answering, e);
51880+ controller.parentUIStateController?.onChildUIAction(controller, e, {
51881+ stateChanged: true,
51882+ onlyIfGroupValueMoves: true,
51883+ });
5184951884};
5185051885const commitSubtree = (controller, e) => {
5185151886 controller.onUIAction?.(e, { skipCommand: true });
0 commit comments