From fd87ffd51629964e17b8095d3c04c084d3bb8901 Mon Sep 17 00:00:00 2001 From: Carson Gross Date: Tue, 28 Mar 2023 14:15:00 -0600 Subject: [PATCH] Experimental support for the [View Transitions API](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API) --- src/htmx.js | 35 ++++++++++++++++++++++++++++++- test/scratch/scratch.html | 44 +++++++++++++++++++++++++++++++++++---- www/attributes/hx-swap.md | 6 ++++++ www/docs.md | 17 +++++++++++++++ www/events.md | 14 +++++++++++++ 5 files changed, 111 insertions(+), 5 deletions(-) diff --git a/src/htmx.js b/src/htmx.js index b7312271..5955d236 100644 --- a/src/htmx.js +++ b/src/htmx.js @@ -70,6 +70,7 @@ return (function () { scrollBehavior: 'smooth', defaultFocusScroll: false, getCacheBusterParam: false, + globalViewTransitions: false, }, parseInterval:parseInterval, _:internalEval, @@ -2396,6 +2397,9 @@ return (function () { if (modifier.indexOf("settle:") === 0) { swapSpec["settleDelay"] = parseInterval(modifier.substr(7)); } + if (modifier.indexOf("transition:") === 0) { + swapSpec["transition"] = modifier.substr(11) === "true"; + } if (modifier.indexOf("scroll:") === 0) { var scrollSpec = modifier.substr(7); var splitSpec = scrollSpec.split(":"); @@ -3127,9 +3131,13 @@ return (function () { var swapSpec = getSwapSpecification(elt, swapOverride); target.classList.add(htmx.config.swappingClass); + + // optional transition API promise callbacks + var settleResolve = null; + var settleReject = null; + var doSwap = function () { try { - var activeElt = document.activeElement; var selectionInfo = {}; try { @@ -3228,6 +3236,7 @@ return (function () { } handleTrigger(xhr, "HX-Trigger-After-Settle", finalElt); } + maybeCall(settleResolve); } if (swapSpec.settleDelay > 0) { @@ -3237,10 +3246,34 @@ return (function () { } } catch (e) { triggerErrorEvent(elt, 'htmx:swapError', responseInfo); + maybeCall(settleReject); throw e; } }; + var shouldTransition = htmx.config.globalViewTransitions + if(swapSpec.hasOwnProperty('transition')){ + shouldTransition = swapSpec.transition; + } + + if(shouldTransition && + triggerEvent(elt, 'htmx:beforeTransition', responseInfo) && + typeof Promise !== "undefined" && document.startViewTransition){ + var settlePromise = new Promise(function (_resolve, _reject) { + settleResolve = _resolve; + settleReject = _reject; + }); + // wrap the original doSwap() in a call to startViewTransition() + var innerDoSwap = doSwap; + doSwap = function() { + document.startViewTransition(function () { + innerDoSwap(); + return settlePromise; + }); + } + } + + if (swapSpec.swapDelay > 0) { setTimeout(doSwap, swapSpec.swapDelay) } else { diff --git a/test/scratch/scratch.html b/test/scratch/scratch.html index 07e9f084..1229bd64 100644 --- a/test/scratch/scratch.html +++ b/test/scratch/scratch.html @@ -14,6 +14,36 @@ } + + + + @@ -48,6 +78,7 @@ +

Server Options


@@ -62,12 +93,17 @@ Autorespond: - - --- + +

--

diff --git a/www/attributes/hx-swap.md b/www/attributes/hx-swap.md index 2123b63f..397730f8 100644 --- a/www/attributes/hx-swap.md +++ b/www/attributes/hx-swap.md @@ -35,6 +35,12 @@ The `div` will issue a request to `/example` and append the returned content aft The `hx-swap` attributes supports modifiers for changing the behavior of the swap. They are outlined below. +#### Transition: `transition` + +If you want to use the new (View Transitions)[https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API] API +when a swap occurs, you can use the `transition:true` option for your swap. You can also enable this feature globally by +setting the `htmx.config.globalViewTransitions` config setting to `true`. + #### Timing: `swap` & `settle` You can modify the amount of time that htmx will wait after receiving a response to swap the content diff --git a/www/docs.md b/www/docs.md index 0c898b08..05264fa0 100644 --- a/www/docs.md +++ b/www/docs.md @@ -417,6 +417,22 @@ The following extensions are available for morph-style swaps: * [Idiomorph](https://github.com/bigskysoftware/idiomorph#htmx) - A newer morphing algorithm developed by us, the creators of htmx. Idiomorph will be available out of the box in htmx 2.0. +#### [View Transitions](#view-transitions) + +The new, experimental [View Transitions API](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API) +gives developers a way to create an animated transition between different DOM states. It is still in active development +and is not available in all browsers, but htmx provides a way to work with this new API that falls back to the non-transition +mechanism if the API is not available in a given browser. + +You can experiment with this new API using the following approaches: + +* Set the `htmx.config.globalViewTransitions` config variable to `true` to use transitions for all swaps +* Use the `transition:true` option in the `hx-swap` attribute +* If an element swap is going to be transitioned due to either of the above configurations, you may catch the + `htmx:beforeTransition` event and call `preventDefault()` on it to cancel the transition. + +View Transitions can be configured using CSS, as outlined in [the Chrome documentation for the feature](https://developer.chrome.com/docs/web-platform/view-transitions/#simple-customization). + ### [Synchronization](#synchronization) Often you want to coordinate the requests between two elements. For example, you may want a request from one element @@ -1402,6 +1418,7 @@ listed below: | `htmx.config.timeout` | defaults to 0 in milliseconds | | `htmx.config.defaultFocusScroll` | if the focused element should be scrolled into view, defaults to false and can be overridden using the [focus-scroll](/attributes/hx-swap/#focus-scroll) swap modifier. | | `htmx.config.getCacheBusterParam` | defaults to false, if set to true htmx will include a cache-busting parameter in `GET` requests to avoid caching partial responses by the browser | +| `htmx.config.globalViewTransitions` | if set to `true`, htmx will use the [View Transition](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API) API when swapping in new content. | diff --git a/www/events.md b/www/events.md index db58e940..e027564a 100644 --- a/www/events.md +++ b/www/events.md @@ -134,6 +134,20 @@ the documentation on [configuring swapping](/docs#modifying_swapping_behavior_wi * `detail.shouldSwap` - if the content will be swapped (defaults to `false` for non-200 response codes) * `detail.target` - the target of the swap +### Event - [`htmx:beforeTransition`](#htmx:beforeTransition) + +This event is triggered before a [View Transition](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API) +wrapped swap occurs. If the event is cancelled, the View Transition will not occur and the normal swapping logic will +happen instead. + +##### Details + +* `detail.elt` - the element that dispatched the request +* `detail.xhr` - the `XMLHttpRequest` +* `detail.requestConfig` - the configuration of the AJAX request +* `detail.shouldSwap` - if the content will be swapped (defaults to `false` for non-200 response codes) +* `detail.target` - the target of the swap + ### Event - [`htmx:configRequest`](#htmx:configRequest) This event is triggered after htmx has collected parameters for inclusion in the request. It can be