@@ -1851,7 +1851,7 @@ var htmx = (function() {
18511851 }
18521852
18531853 /**
1854- * Implements complete swapping pipeline, including: focus and selection preservation,
1854+ * Implements complete swapping pipeline, including: delay, view transitions, focus and selection preservation,
18551855 * title updates, scroll, OOB swapping, normal swapping and settling
18561856 * @param {string|Element } target
18571857 * @param {string } content
@@ -1862,6 +1862,12 @@ var htmx = (function() {
18621862 if ( ! swapOptions ) {
18631863 swapOptions = { }
18641864 }
1865+ // optional transition API promise callbacks
1866+ let settleResolve = null
1867+ let settleReject = null
1868+
1869+ let localSwap = function ( ) {
1870+ maybeCall ( swapOptions . beforeSwapCallback )
18651871
18661872 target = resolveTarget ( target )
18671873 const rootNode = swapOptions . contextElement ? getRootNode ( swapOptions . contextElement , false ) : getDocument ( )
@@ -1952,9 +1958,7 @@ var htmx = (function() {
19521958 }
19531959 triggerEvent ( elt , 'htmx:afterSwap' , swapOptions . eventInfo )
19541960 } )
1955- if ( swapOptions . afterSwapCallback ) {
1956- swapOptions . afterSwapCallback ( )
1957- }
1961+ maybeCall ( swapOptions . afterSwapCallback )
19581962
19591963 // merge in new title after swap but before settle
19601964 if ( ! swapSpec . ignoreTitle ) {
@@ -1981,16 +1985,54 @@ var htmx = (function() {
19811985 }
19821986
19831987 updateScrollState ( settleInfo . elts , swapSpec )
1984- if ( swapOptions . afterSettleCallback ) {
1985- swapOptions . afterSettleCallback ( )
1986- }
1988+ maybeCall ( swapOptions . afterSettleCallback )
1989+ maybeCall ( settleResolve )
19871990 }
19881991
19891992 if ( swapSpec . settleDelay > 0 ) {
19901993 getWindow ( ) . setTimeout ( doSettle , swapSpec . settleDelay )
19911994 } else {
19921995 doSettle ( )
19931996 }
1997+ }
1998+ let shouldTransition = htmx . config . globalViewTransitions
1999+ if ( swapSpec . hasOwnProperty ( 'transition' ) ) {
2000+ shouldTransition = swapSpec . transition
2001+ }
2002+
2003+ const elt = swapOptions . contextElement || getDocument ( )
2004+
2005+ if ( shouldTransition &&
2006+ triggerEvent ( elt , 'htmx:beforeTransition' , swapOptions . eventInfo ) &&
2007+ typeof Promise !== 'undefined' &&
2008+ // @ts -ignore experimental feature atm
2009+ document . startViewTransition ) {
2010+ const settlePromise = new Promise ( function ( _resolve , _reject ) {
2011+ settleResolve = _resolve
2012+ settleReject = _reject
2013+ } )
2014+ // wrap the original localSwap() in a call to startViewTransition()
2015+ const innerDoSwap = localSwap
2016+ localSwap = function ( ) {
2017+ // @ts -ignore experimental feature atm
2018+ document . startViewTransition ( function ( ) {
2019+ innerDoSwap ( )
2020+ return settlePromise
2021+ } )
2022+ }
2023+ }
2024+
2025+ try {
2026+ if ( swapSpec ?. swapDelay && swapSpec . swapDelay > 0 ) {
2027+ getWindow ( ) . setTimeout ( localSwap , swapSpec . swapDelay )
2028+ } else {
2029+ localSwap ( )
2030+ }
2031+ } catch ( e ) {
2032+ triggerErrorEvent ( elt , 'htmx:swapError' , swapOptions . eventInfo )
2033+ maybeCall ( settleReject )
2034+ throw e
2035+ }
19942036 }
19952037
19962038 /**
@@ -4789,10 +4831,6 @@ var htmx = (function() {
47894831
47904832 target . classList . add ( htmx . config . swappingClass )
47914833
4792- // optional transition API promise callbacks
4793- let settleResolve = null
4794- let settleReject = null
4795-
47964834 if ( responseInfoSelect ) {
47974835 selectOverride = responseInfoSelect
47984836 }
@@ -4804,20 +4842,6 @@ var htmx = (function() {
48044842 const selectOOB = getClosestAttributeValue ( elt , 'hx-select-oob' )
48054843 const select = getClosestAttributeValue ( elt , 'hx-select' )
48064844
4807- let doSwap = function ( ) {
4808- try {
4809- // if we need to save history, do so, before swapping so that relative resources have the correct base URL
4810- if ( historyUpdate . type ) {
4811- triggerEvent ( getDocument ( ) . body , 'htmx:beforeHistoryUpdate' , mergeObjects ( { history : historyUpdate } , responseInfo ) )
4812- if ( historyUpdate . type === 'push' ) {
4813- pushUrlIntoHistory ( historyUpdate . path )
4814- triggerEvent ( getDocument ( ) . body , 'htmx:pushedIntoHistory' , { path : historyUpdate . path } )
4815- } else {
4816- replaceUrlInHistory ( historyUpdate . path )
4817- triggerEvent ( getDocument ( ) . body , 'htmx:replacedInHistory' , { path : historyUpdate . path } )
4818- }
4819- }
4820-
48214845 swap ( target , serverResponse , swapSpec , {
48224846 select : selectOverride || select ,
48234847 selectOOB,
@@ -4841,46 +4865,21 @@ var htmx = (function() {
48414865 }
48424866 handleTriggerHeader ( xhr , 'HX-Trigger-After-Settle' , finalElt )
48434867 }
4844- maybeCall ( settleResolve )
4868+ } ,
4869+ beforeSwapCallback : function ( ) {
4870+ // if we need to save history, do so, before swapping so that relative resources have the correct base URL
4871+ if ( historyUpdate . type ) {
4872+ triggerEvent ( getDocument ( ) . body , 'htmx:beforeHistoryUpdate' , mergeObjects ( { history : historyUpdate } , responseInfo ) )
4873+ if ( historyUpdate . type === 'push' ) {
4874+ pushUrlIntoHistory ( historyUpdate . path )
4875+ triggerEvent ( getDocument ( ) . body , 'htmx:pushedIntoHistory' , { path : historyUpdate . path } )
4876+ } else {
4877+ replaceUrlInHistory ( historyUpdate . path )
4878+ triggerEvent ( getDocument ( ) . body , 'htmx:replacedInHistory' , { path : historyUpdate . path } )
4879+ }
4880+ }
48454881 }
48464882 } )
4847- } catch ( e ) {
4848- triggerErrorEvent ( elt , 'htmx:swapError' , responseInfo )
4849- maybeCall ( settleReject )
4850- throw e
4851- }
4852- }
4853-
4854- let shouldTransition = htmx . config . globalViewTransitions
4855- if ( swapSpec . hasOwnProperty ( 'transition' ) ) {
4856- shouldTransition = swapSpec . transition
4857- }
4858-
4859- if ( shouldTransition &&
4860- triggerEvent ( elt , 'htmx:beforeTransition' , responseInfo ) &&
4861- typeof Promise !== 'undefined' &&
4862- // @ts -ignore experimental feature atm
4863- document . startViewTransition ) {
4864- const settlePromise = new Promise ( function ( _resolve , _reject ) {
4865- settleResolve = _resolve
4866- settleReject = _reject
4867- } )
4868- // wrap the original doSwap() in a call to startViewTransition()
4869- const innerDoSwap = doSwap
4870- doSwap = function ( ) {
4871- // @ts -ignore experimental feature atm
4872- document . startViewTransition ( function ( ) {
4873- innerDoSwap ( )
4874- return settlePromise
4875- } )
4876- }
4877- }
4878-
4879- if ( swapSpec . swapDelay > 0 ) {
4880- getWindow ( ) . setTimeout ( doSwap , swapSpec . swapDelay )
4881- } else {
4882- doSwap ( )
4883- }
48844883 }
48854884 if ( isError ) {
48864885 triggerErrorEvent ( elt , 'htmx:responseError' , mergeObjects ( { error : 'Response Status Error Code ' + xhr . status + ' from ' + responseInfo . pathInfo . requestPath } , responseInfo ) )
@@ -5081,6 +5080,7 @@ var htmx = (function() {
50815080 * @property {Element } [contextElement]
50825081 * @property {swapCallback } [afterSwapCallback]
50835082 * @property {swapCallback } [afterSettleCallback]
5083+ * @property {swapCallback } [beforeSwapCallback]
50845084 */
50855085
50865086/**
0 commit comments