@jsenv/navi 0.29.92 → 0.29.93

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -24208,12 +24208,25 @@ const useUIStateController = (
24208
24208
  } = controlInfo;
24209
24209
  controller.value = value;
24210
24210
  controller.defaultValue = defaultValue;
24211
+ // An optimistic control with work in flight keeps the user's intent on
24212
+ // screen: an incoming external state is the echo of an intermediate
24213
+ // commit (the first of two queued toggles landing), and the queued
24214
+ // request about to go out was built on top of the UI state —
24215
+ // overwriting it would both flash the superseded value and send it.
24216
+ // `controller.state` is still taken below: it is the last known good
24217
+ // state, the rollback target if the chain fails.
24218
+ const optimisticWorkInFlight = Boolean(
24219
+ props.optimistic &&
24220
+ (controller.actionInFlight || controller.queuedActionAllowedEvent),
24221
+ );
24211
24222
  if (hasStateProp) {
24212
24223
  controller.hasStateProp = true;
24213
24224
  const currentState = controller.state;
24214
24225
  if (!compareTwoJsValues(state, currentState)) {
24215
24226
  controller.state = state;
24216
- controller.setUIState(state, new CustomEvent("state_prop_change"));
24227
+ if (!optimisticWorkInFlight) {
24228
+ controller.setUIState(state, new CustomEvent("state_prop_change"));
24229
+ }
24217
24230
  }
24218
24231
  } else {
24219
24232
  if (controller.hasStateProp) {
@@ -24230,10 +24243,12 @@ const useUIStateController = (
24230
24243
  const currentState = controller.state;
24231
24244
  if (!compareTwoJsValues(stateFromSignal, currentState)) {
24232
24245
  controller.state = stateFromSignal;
24233
- controller.setUIState(
24234
- stateFromSignal,
24235
- new CustomEvent("state_prop_change"),
24236
- );
24246
+ if (!optimisticWorkInFlight) {
24247
+ controller.setUIState(
24248
+ stateFromSignal,
24249
+ new CustomEvent("state_prop_change"),
24250
+ );
24251
+ }
24237
24252
  }
24238
24253
  }
24239
24254
  }
@@ -27219,7 +27234,6 @@ const useInteractiveProps = (props, {
27219
27234
  uiStateController.actionInFlight = false;
27220
27235
  uiStateController.runningAction = null;
27221
27236
  const queuedEvent = uiStateController.queuedActionAllowedEvent;
27222
- uiStateController.queuedActionAllowedEvent = null;
27223
27237
  if (!queuedEvent) {
27224
27238
  return;
27225
27239
  }
@@ -27227,12 +27241,20 @@ const useInteractiveProps = (props, {
27227
27241
  // A failure abandons the queue: the UI is rolled back to the last
27228
27242
  // known state (resetOnError above), and what was queued was built
27229
27243
  // on top of the state that just failed.
27244
+ uiStateController.queuedActionAllowedEvent = null;
27230
27245
  return;
27231
27246
  }
27232
27247
  // A microtask later, not right here: this runs inside the batch()
27233
27248
  // that settles the action (see watchActionCompletion for the same
27234
27249
  // constraint).
27235
27250
  queueMicrotask(() => {
27251
+ // Cleared here, right before the dispatch, never earlier: between
27252
+ // the outcome and this microtask a render can slip in (the echo
27253
+ // of what the settled run committed), and the external-state gate
27254
+ // in ui_state_controller.js must still see work in flight or it
27255
+ // would overwrite the UI state the queued request is about to
27256
+ // send.
27257
+ uiStateController.queuedActionAllowedEvent = null;
27236
27258
  executeAction(queuedEvent);
27237
27259
  });
27238
27260
  });