@@ -2461,36 +2461,36 @@ function lanesToEventPriority(lanes) {
2461
2461
return IdleEventPriority;
2462
2462
}
2463
2463
2464
- function noop$3 () {}
2464
+ function noop$4 () {}
2465
2465
2466
2466
var DefaultDispatcher = {
2467
2467
f
2468
2468
/* flushSyncWork */
2469
- : noop$3 ,
2469
+ : noop$4 ,
2470
2470
r
2471
2471
/* requestFormReset */
2472
- : noop$3 ,
2472
+ : noop$4 ,
2473
2473
D
2474
2474
/* prefetchDNS */
2475
- : noop$3 ,
2475
+ : noop$4 ,
2476
2476
C
2477
2477
/* preconnect */
2478
- : noop$3 ,
2478
+ : noop$4 ,
2479
2479
L
2480
2480
/* preload */
2481
- : noop$3 ,
2481
+ : noop$4 ,
2482
2482
m
2483
2483
/* preloadModule */
2484
- : noop$3 ,
2484
+ : noop$4 ,
2485
2485
X
2486
2486
/* preinitScript */
2487
- : noop$3 ,
2487
+ : noop$4 ,
2488
2488
S
2489
2489
/* preinitStyle */
2490
- : noop$3 ,
2490
+ : noop$4 ,
2491
2491
M
2492
2492
/* preinitModuleScript */
2493
- : noop$3
2493
+ : noop$4
2494
2494
};
2495
2495
var Internals = {
2496
2496
Events: null,
@@ -8568,6 +8568,9 @@ transition) {
8568
8568
8569
8569
return currentEventTransitionLane;
8570
8570
}
8571
+ function didCurrentEventScheduleTransition() {
8572
+ return currentEventTransitionLane !== NoLane;
8573
+ }
8571
8574
8572
8575
// transition updates that occur while the async action is still in progress
8573
8576
// are treated as part of the action.
@@ -9570,7 +9573,7 @@ function isThenableResolved(thenable) {
9570
9573
return status === 'fulfilled' || status === 'rejected';
9571
9574
}
9572
9575
9573
- function noop$2 () {}
9576
+ function noop$3 () {}
9574
9577
9575
9578
function trackUsedThenable(thenableState, thenable, index) {
9576
9579
if (ReactSharedInternals.actQueue !== null) {
@@ -9613,7 +9616,7 @@ function trackUsedThenable(thenableState, thenable, index) {
9613
9616
// intentionally ignore.
9614
9617
9615
9618
9616
- thenable.then(noop$2 , noop$2 );
9619
+ thenable.then(noop$3 , noop$3 );
9617
9620
thenable = previous;
9618
9621
}
9619
9622
} // We use an expando to track the status and result of a thenable so that we
@@ -9646,7 +9649,7 @@ function trackUsedThenable(thenableState, thenable, index) {
9646
9649
// some custom userspace implementation. We treat it as "pending".
9647
9650
// Attach a dummy listener, to ensure that any lazy initialization can
9648
9651
// happen. Flight lazily parses JSON when the value is actually awaited.
9649
- thenable.then(noop$2 , noop$2 );
9652
+ thenable.then(noop$3 , noop$3 );
9650
9653
} else {
9651
9654
// This is an uncached thenable that we haven't seen before.
9652
9655
// Detect infinite ping loops caused by uncached promises.
@@ -13481,20 +13484,25 @@ function startTransition(fiber, queue, pendingState, finishedState, callback, op
13481
13484
}
13482
13485
}
13483
13486
13484
- function startHostTransition(formFiber, pendingState, callback, formData) {
13487
+ var noop$2 = function () {};
13488
+
13489
+ function startHostTransition(formFiber, pendingState, action, formData) {
13485
13490
13486
13491
if (formFiber.tag !== HostComponent) {
13487
13492
throw new Error('Expected the form instance to be a HostComponent. This ' + 'is a bug in React.');
13488
13493
}
13489
13494
13490
13495
var stateHook = ensureFormComponentIsStateful(formFiber);
13491
13496
var queue = stateHook.queue;
13492
- startTransition(formFiber, queue, pendingState, NotPendingTransition, // TODO: We can avoid this extra wrapper, somehow. Figure out layering
13493
- // once more of this function is implemented.
13494
- function () {
13497
+ startTransition(formFiber, queue, pendingState, NotPendingTransition, // TODO: `startTransition` both sets the pending state and dispatches
13498
+ // the action, if one is provided. Consider refactoring these two
13499
+ // concerns to avoid the extra lambda.
13500
+ action === null ? // No action was provided, but we still call `startTransition` to
13501
+ // set the pending form status.
13502
+ noop$2 : function () {
13495
13503
// Automatically reset the form when the action completes.
13496
13504
requestFormReset$1(formFiber);
13497
- return callback (formData);
13505
+ return action (formData);
13498
13506
});
13499
13507
}
13500
13508
@@ -30838,7 +30846,7 @@ identifierPrefix, onUncaughtError, onCaughtError, onRecoverableError, transition
30838
30846
return root;
30839
30847
}
30840
30848
30841
- var ReactVersion = '19.0.0-www-classic-f43a9c2f ';
30849
+ var ReactVersion = '19.0.0-www-classic-54329650 ';
30842
30850
30843
30851
function createPortal$1(children, containerInfo, // TODO: figure out the API for cross-renderer implementation.
30844
30852
implementation) {
@@ -33892,11 +33900,49 @@ function extractEvents$2(dispatchQueue, domEventName, targetInst, nativeEvent, n
33892
33900
}
33893
33901
}
33894
33902
33903
+ function coerceFormActionProp(actionProp) {
33904
+ // This should match the logic in ReactDOMComponent
33905
+ if (actionProp == null || typeof actionProp === 'symbol' || typeof actionProp === 'boolean') {
33906
+ return null;
33907
+ } else if (typeof actionProp === 'function') {
33908
+ return actionProp;
33909
+ } else {
33910
+ {
33911
+ checkAttributeStringCoercion(actionProp, 'action');
33912
+ }
33913
+
33914
+ return sanitizeURL(enableTrustedTypesIntegration ? actionProp : '' + actionProp);
33915
+ }
33916
+ }
33917
+
33918
+ function createFormDataWithSubmitter(form, submitter) {
33919
+ // The submitter's value should be included in the FormData.
33920
+ // It should be in the document order in the form.
33921
+ // Since the FormData constructor invokes the formdata event it also
33922
+ // needs to be available before that happens so after construction it's too
33923
+ // late. We use a temporary fake node for the duration of this event.
33924
+ // TODO: FormData takes a second argument that it's the submitter but this
33925
+ // is fairly new so not all browsers support it yet. Switch to that technique
33926
+ // when available.
33927
+ var temp = submitter.ownerDocument.createElement('input');
33928
+ temp.name = submitter.name;
33929
+ temp.value = submitter.value;
33930
+
33931
+ if (form.id) {
33932
+ temp.setAttribute('form', form.id);
33933
+ }
33934
+
33935
+ submitter.parentNode.insertBefore(temp, submitter);
33936
+ var formData = new FormData(form);
33937
+ temp.parentNode.removeChild(temp);
33938
+ return formData;
33939
+ }
33895
33940
/**
33896
33941
* This plugin invokes action functions on forms, inputs and buttons if
33897
33942
* the form doesn't prevent default.
33898
33943
*/
33899
33944
33945
+
33900
33946
function extractEvents$1(dispatchQueue, domEventName, maybeTargetInst, nativeEvent, nativeEventTarget, eventSystemFlags, targetContainer) {
33901
33947
if (domEventName !== 'submit') {
33902
33948
return;
@@ -33910,15 +33956,16 @@ function extractEvents$1(dispatchQueue, domEventName, maybeTargetInst, nativeEve
33910
33956
33911
33957
var formInst = maybeTargetInst;
33912
33958
var form = nativeEventTarget;
33913
- var action = getFiberCurrentPropsFromNode(form).action;
33959
+ var action = coerceFormActionProp( getFiberCurrentPropsFromNode(form).action) ;
33914
33960
var submitter = nativeEvent.submitter;
33915
33961
var submitterAction;
33916
33962
33917
33963
if (submitter) {
33918
33964
var submitterProps = getFiberCurrentPropsFromNode(submitter);
33919
- submitterAction = submitterProps ? submitterProps.formAction : submitter.getAttribute('formAction');
33965
+ submitterAction = submitterProps ? coerceFormActionProp(submitterProps.formAction) : // The built-in Flow type is ?string, wider than the spec
33966
+ submitter.getAttribute('formAction');
33920
33967
33921
- if (submitterAction != null) {
33968
+ if (submitterAction !== null) {
33922
33969
// The submitter overrides the form action.
33923
33970
action = submitterAction; // If the action is a function, we don't want to pass its name
33924
33971
// value to the FormData since it's controlled by the server.
@@ -33927,58 +33974,53 @@ function extractEvents$1(dispatchQueue, domEventName, maybeTargetInst, nativeEve
33927
33974
}
33928
33975
}
33929
33976
33930
- if (typeof action !== 'function') {
33931
- return;
33932
- }
33933
-
33934
33977
var event = new SyntheticEvent('action', 'action', null, nativeEvent, nativeEventTarget);
33935
33978
33936
33979
function submitForm() {
33937
33980
if (nativeEvent.defaultPrevented) {
33938
- // We let earlier events to prevent the action from submitting.
33939
- return;
33940
- } // Prevent native navigation.
33941
-
33942
-
33943
- event.preventDefault();
33944
- var formData;
33981
+ // An earlier event prevented form submission. If a transition update was
33982
+ // also scheduled, we should trigger a pending form status — even if
33983
+ // no action function was provided.
33984
+ if (didCurrentEventScheduleTransition()) {
33985
+ // We're going to set the pending form status, but because the submission
33986
+ // was prevented, we should not fire the action function.
33987
+ var formData = submitter ? createFormDataWithSubmitter(form, submitter) : new FormData(form);
33988
+ var pendingState = {
33989
+ pending: true,
33990
+ data: formData,
33991
+ method: form.method,
33992
+ action: action
33993
+ };
33945
33994
33946
- if (submitter) {
33947
- // The submitter's value should be included in the FormData.
33948
- // It should be in the document order in the form.
33949
- // Since the FormData constructor invokes the formdata event it also
33950
- // needs to be available before that happens so after construction it's too
33951
- // late. We use a temporary fake node for the duration of this event.
33952
- // TODO: FormData takes a second argument that it's the submitter but this
33953
- // is fairly new so not all browsers support it yet. Switch to that technique
33954
- // when available.
33955
- var temp = submitter.ownerDocument.createElement('input');
33956
- temp.name = submitter.name;
33957
- temp.value = submitter.value;
33995
+ {
33996
+ Object.freeze(pendingState);
33997
+ }
33958
33998
33959
- if (form.id) {
33960
- temp.setAttribute('form', form.id);
33999
+ startHostTransition(formInst, pendingState, // Pass `null` as the action
34000
+ // TODO: Consider splitting up startHostTransition into two separate
34001
+ // functions, one that sets the form status and one that invokes
34002
+ // the action.
34003
+ null, formData);
33961
34004
}
34005
+ } else if (typeof action === 'function') {
34006
+ // A form action was provided. Prevent native navigation.
34007
+ event.preventDefault(); // Dispatch the action and set a pending form status.
33962
34008
33963
- submitter.parentNode.insertBefore(temp, submitter);
33964
- formData = new FormData(form);
33965
- temp.parentNode.removeChild(temp);
33966
- } else {
33967
- formData = new FormData(form);
33968
- }
34009
+ var _formData = submitter ? createFormDataWithSubmitter(form, submitter) : new FormData(form);
33969
34010
33970
- var pendingState = {
33971
- pending: true,
33972
- data: formData ,
33973
- method: form.method,
33974
- action: action
33975
- };
34011
+ var _pendingState = {
34012
+ pending: true,
34013
+ data: _formData ,
34014
+ method: form.method,
34015
+ action: action
34016
+ };
33976
34017
33977
- {
33978
- Object.freeze(pendingState );
33979
- }
34018
+ {
34019
+ Object.freeze(_pendingState );
34020
+ }
33980
34021
33981
- startHostTransition(formInst, pendingState, action, formData);
34022
+ startHostTransition(formInst, _pendingState, action, _formData);
34023
+ } else ;
33982
34024
}
33983
34025
33984
34026
dispatchQueue.push({
0 commit comments