[#18362] Add support for Reanimated inline styles in Reagent (#18381)

* Modify reanimated/view to support vectors contained in styles
* Add code examples of animated inline styles

Additionally,
*  Fix warning about reactive deref not supported in lazy-seqs
This commit is contained in:
Ulises Manuel 2024-01-10 20:09:32 -06:00 committed by GitHub
parent fdfc06d6dd
commit 0f43daa836
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 82 additions and 39 deletions

View File

@ -1,8 +1,7 @@
(ns quo.components.buttons.slide-button.style
(:require
[quo.components.buttons.slide-button.constants :as constants]
[quo.components.buttons.slide-button.utils :as utils]
[react-native.reanimated :as reanimated]))
[quo.components.buttons.slide-button.utils :as utils]))
(def absolute-fill
{:position :absolute
@ -13,28 +12,25 @@
(defn thumb-container
[{:keys [interpolate-track thumb-size customization-color theme]}]
(reanimated/apply-animations-to-style
{:transform [{:translate-x (interpolate-track :track-clamp)}]}
[{:transform [{:translate-x (interpolate-track :track-clamp)}]}
{:background-color (utils/main-color customization-color theme)
:border-radius 12
:height thumb-size
:width thumb-size
:align-items :center
:overflow :hidden
:justify-content :center}))
:justify-content :center}])
(defn arrow-icon-container
[interpolate-track]
(reanimated/apply-animations-to-style
{:transform [{:translate-x (interpolate-track :arrow-icon-position)}]}
{:flex 1
{:transform [{:translate-x (interpolate-track :arrow-icon-position)}]
:flex 1
:align-items :center
:justify-content :center}))
:justify-content :center})
(defn action-icon
[interpolate-track size]
(reanimated/apply-animations-to-style
{:transform [{:translate-x (interpolate-track :action-icon-position)}]}
[{:transform [{:translate-x (interpolate-track :action-icon-position)}]}
{:height size
:width size
:position :absolute
@ -42,7 +38,7 @@
:left 0
:top 0
:flex-direction :row
:justify-content :space-around}))
:justify-content :space-around}])
(defn track
[{:keys [disabled? customization-color height blur?]}]
@ -57,9 +53,8 @@
(defn track-cover
[interpolate-track]
(reanimated/apply-animations-to-style
{:left (interpolate-track :track-cover)}
(assoc absolute-fill :overflow :hidden)))
[{:left (interpolate-track :track-cover)}
(assoc absolute-fill :overflow :hidden)])
(defn track-cover-text-container
[track-width]

View File

@ -22,8 +22,10 @@
useAnimatedScrollHandler
runOnJS)]
["react-native-redash" :refer (withPause)]
[oops.core :as oops]
[react-native.flat-list :as rn-flat-list]
[reagent.core :as reagent]
[utils.transforms :as transforms]
[utils.worklets.core :as worklets.core]))
(def enable-layout-animations enableLayoutAnimations)
@ -38,7 +40,20 @@
;; Animated Components
(def create-animated-component (comp reagent/adapt-react-class (.-createAnimatedComponent reanimated)))
(def view (reagent/adapt-react-class (.-View reanimated)))
(def ^:private view* (reagent/adapt-react-class (.-View reanimated)))
(defn view
[]
(let [current-component (reagent/current-component)
reagent-props (reagent/props current-component)
children (reagent/children current-component)
updated-props (update reagent-props :style transforms/styles-with-vectors)
;; Some components add JS props to their children (such as TouchableWithoutFeedback), to make
;; this component fully compatible we are passing those props to the inner component (`view*`).
external-props (oops/gobj-get current-component "props")
all-props (transforms/copy-js-obj-to-map external-props updated-props #(not= % "argv"))]
(into [view* all-props] children)))
(def text (reagent/adapt-react-class (.-Text reanimated)))
(def scroll-view (reagent/adapt-react-class (.-ScrollView reanimated)))
(def image (reagent/adapt-react-class (.-Image reanimated)))

View File

@ -36,9 +36,9 @@
(if (seq images) constants/images-container-height 0)))
[images])
[reanimated/view
{:style (reanimated/apply-animations-to-style {:height height}
{:margin-horizontal -20
:z-index 1})}
{:style {:height height
:margin-horizontal -20
:z-index 1}}
[gesture/flat-list
{:key-fn first
:render-fn (fn [item]

View File

@ -22,6 +22,7 @@
(defn album-message
[{:keys [albumize?] :as message} context on-long-press message-container-data]
(let [shared-element-id (rf/sub [:shared-element-id])
media-server-port (rf/sub [:mediaserver/port])
first-image (first (:album message))
album-style (if (> (:image-width first-image) (:image-height first-image))
:landscape
@ -57,8 +58,7 @@
:index index}])}
[fast-image/fast-image
{:style (style/image dimensions index portrait? images-count)
:source {:uri (url/replace-port (:image (:content item))
(rf/sub [:mediaserver/port]))}
:source {:uri (url/replace-port (:image (:content item)) media-server-port)}
:native-ID (when (and (= shared-element-id (:message-id item))
(< index constants/max-album-photos))
:shared-element)}]

View File

@ -1,6 +1,5 @@
(ns status-im.contexts.profile.settings.header.style
(:require [quo.foundations.colors :as colors]
[react-native.reanimated :as reanimated]))
(:require [quo.foundations.colors :as colors]))
(defn header-view
[customization-color theme]
@ -30,19 +29,17 @@
(defn radius-container
[opacity-animation]
(reanimated/apply-animations-to-style
{:opacity opacity-animation}
{:flex-direction :row
:justify-content :space-between}))
{:opacity opacity-animation
:flex-direction :row
:justify-content :space-between})
(defn avatar-container
[theme scale-animation top-margin-animation side-margin-animation]
(reanimated/apply-animations-to-style
{:transform [{:scale scale-animation}]
[{:transform [{:scale scale-animation}]
:margin-top top-margin-animation
:margin-left side-margin-animation
:margin-bottom side-margin-animation}
{:align-items :flex-start
:border-width 4
:border-color (colors/theme-colors colors/border-avatar-light colors/neutral-80-opa-80 theme)
:border-radius 100}))
:border-radius 100}])

View File

@ -2,13 +2,11 @@
(:require
[quo.foundations.colors :as colors]
[react-native.platform :as platform]
[react-native.reanimated :as reanimated]
[status-im.contexts.shell.jump-to.utils :as utils]))
(defn bottom-tabs-container
[pass-through? height]
(reanimated/apply-animations-to-style
{:height height}
[{:height height}
{:background-color (if pass-through? :transparent colors/neutral-100)
:flex 1
:align-items :center
@ -18,7 +16,7 @@
:right 0
:left 0
:overflow :hidden
:accessibility-label :bottom-tabs-container}))
:accessibility-label :bottom-tabs-container}])
(defn bottom-tabs
[]
@ -30,11 +28,10 @@
(defn bottom-tabs-blur-overlay
[height]
(reanimated/apply-animations-to-style
{:height height}
[{:height height}
{:position :absolute
:left 0
:right 0
:bottom 0
:height (utils/bottom-tabs-container-height)
:background-color colors/neutral-100-opa-70-blur}))
:background-color colors/neutral-100-opa-70-blur}])

View File

@ -1,7 +1,10 @@
(ns utils.transforms
(:refer-clojure :exclude [js->clj])
(:require
[cljs-bean.core :as clj-bean]))
[cljs-bean.core :as clj-bean]
[oops.core :as oops]
[reagent.impl.template :as reagent.template]
[reagent.impl.util :as reagent.util]))
(defn js->clj [data] (cljs.core/js->clj data :keywordize-keys true))
@ -21,3 +24,39 @@
[json]
(when-not (= json "undefined")
(try (.parse js/JSON json) (catch js/Error _ (when (string? json) json)))))
(declare styles-with-vectors)
(defn ^:private convert-keys-and-values
"Takes a JS Object a key and a value.
Transforms the key from a Clojure style prop to a JS style prop, using the reagent cache.
Performs a mutual recursion transformation on the value using `styles-with-vectors`.
Based on `reagent.impl.template/kv-conv`."
[obj k v]
(doto obj
(oops/gobj-set (reagent.template/cached-prop-name k) (styles-with-vectors v))))
(defn styles-with-vectors
"Takes a Clojure style map or a Clojure vector of style maps and returns a JS Object
valid to use as React Native styles.
The transformation is done by performing mutual recursive calls with `convert-keys-and-values`.
Based on `reagent.impl.template/convert-prop-value`."
[x]
(cond (reagent.util/js-val? x) x
(reagent.util/named? x) (name x)
(map? x) (reduce-kv convert-keys-and-values #js {} x)
(vector? x) (to-array (mapv styles-with-vectors x))
:else (clj->js x)))
(defn copy-js-obj-to-map
"Copy `obj` keys and values into `m` if `(pred obj-key)` is satisfied."
[obj m pred]
(persistent!
(reduce (fn [acc js-prop]
(if (pred js-prop)
(assoc! acc js-prop (oops/gobj-get obj js-prop))
acc))
(transient m)
(js-keys obj))))