diff --git a/src/quo/components/colors/color_picker/component_spec.cljs b/src/quo/components/colors/color_picker/component_spec.cljs index 055ee326cf..527f123567 100644 --- a/src/quo/components/colors/color_picker/component_spec.cljs +++ b/src/quo/components/colors/color_picker/component_spec.cljs @@ -1,7 +1,6 @@ (ns quo.components.colors.color-picker.component-spec (:require [quo.components.colors.color-picker.view :as color-picker] - [reagent.core :as reagent] [test-helpers.component :as h])) (h/describe "color-picker" @@ -12,7 +11,7 @@ (-> (h/expect event) (.toHaveBeenCalled)))) (h/test "color picker color changed" - (let [selected (reagent/atom nil)] + (let [selected (atom nil)] (h/render [color-picker/view {:on-change #(reset! selected %)}]) (h/fire-event :press (get (h/get-all-by-label-text :color-picker-item) 0)) (-> (h/expect @selected) diff --git a/src/quo/components/header.cljs b/src/quo/components/header.cljs index 7016004938..0b26f73c17 100644 --- a/src/quo/components/header.cljs +++ b/src/quo/components/header.cljs @@ -6,8 +6,7 @@ [quo.foundations.colors :as colors] [quo.theme :as quo.theme] [react-native.core :as rn] - [react-native.reanimated :as reanimated] - [reagent.core :as reagent])) + [react-native.reanimated :as reanimated])) (def header-height 56) @@ -57,7 +56,7 @@ :justify-content :center :align-items :flex-end}) -(defn title-style +(defn get-title-style [{:keys [left right]} title-align] (merge absolute-fill @@ -133,76 +132,77 @@ :size :large} title])]) -(defn- header-internal - [{:keys [left-width right-width]}] - (let [layout (reagent/atom {:left {:width (or left-width 8) - :height header-height} - :right {:width (or right-width 8) - :height header-height} - :title {:width 0 - :height header-height}}) - handle-layout (fn [el get-layout] - (fn [evt] - (let [width (oget evt "nativeEvent" "layout" "width") - height (oget evt "nativeEvent" "layout" "height")] - (when get-layout - (get-layout el - {:width width - :height height})) - (swap! layout assoc - el - {:width width - :height height}))))] - (fn - [{:keys [left-accessories left-component border-bottom - right-accessories right-component insets get-layout - title subtitle title-component style title-align - background theme] - :or {title-align :center - border-bottom false}}] - (let [status-bar-height (get insets :top 0) - height (+ header-height status-bar-height)] - [reanimated/view - {:style (header-wrapper-style {:height height - :background background - :border-bottom border-bottom - :theme theme})} - [rn/view - {:pointer-events :box-none - :height status-bar-height}] - [rn/view - {:style (merge {:height header-height} - style) - :pointer-events :box-none} - [rn/view - {:style absolute-fill - :pointer-events :box-none} - [rn/view - {:style content - :pointer-events :box-none} - [rn/view - {:style left-style - :on-layout (handle-layout :left get-layout) - :pointer-events :box-none} - [header-actions - {:accessories left-accessories - :component left-component}]] - - [rn/view - {:style (title-style @layout title-align) - :on-layout (handle-layout :title get-layout) - :pointer-events :box-none} - [header-title - {:title title - :subtitle subtitle - :title-align title-align - :component title-component}]] - [rn/view - {:style right-style - :on-layout (handle-layout :right get-layout) - :pointer-events :box-none} - [header-actions - {:accessories right-accessories - :component right-component}]]]]]])))) - -(def header (quo.theme/with-theme header-internal)) +(defn header + [{:keys [left-accessories left-component border-bottom left-width right-width + right-accessories right-component insets get-layout + title subtitle title-component style title-align + background] + :or {title-align :center + border-bottom false}}] + (let [theme (quo.theme/use-theme-value) + [layout set-layout] (rn/use-state {:left {:width (or left-width 8) + :height header-height} + :right {:width (or right-width 8) + :height header-height} + :title {:width 0 + :height header-height}}) + handle-layout (rn/use-callback + (fn [el get-layout] + (fn [evt] + (let [width (oget evt "nativeEvent" "layout" "width") + height (oget evt "nativeEvent" "layout" "height")] + (when get-layout + (get-layout el + {:width width + :height height})) + (set-layout + (assoc layout + el + {:width width + :height height}))))) + [layout]) + status-bar-height (get insets :top 0) + height (+ header-height status-bar-height) + title-style (rn/use-memo (fn [] (get-title-style layout title-align)) + [layout title-align])] + [reanimated/view + {:style (header-wrapper-style {:height height + :background background + :border-bottom border-bottom + :theme theme})} + [rn/view + {:pointer-events :box-none + :height status-bar-height}] + [rn/view + {:style (merge {:height header-height} + style) + :pointer-events :box-none} + [rn/view + {:style absolute-fill + :pointer-events :box-none} + [rn/view + {:style content + :pointer-events :box-none} + [rn/view + {:style left-style + :on-layout (handle-layout :left get-layout) + :pointer-events :box-none} + [header-actions + {:accessories left-accessories + :component left-component}]] + [rn/view + {:style title-style + :on-layout (handle-layout :title get-layout) + :pointer-events :box-none} + [header-title + {:title title + :subtitle subtitle + :title-align title-align + :component title-component}]] + [rn/view + {:style right-style + :on-layout (handle-layout :right get-layout) + :pointer-events :box-none} + [header-actions + {:accessories right-accessories + :component right-component}]]]]]])) diff --git a/src/quo/components/messages/gap.cljs b/src/quo/components/messages/gap.cljs index 6c407f256b..62c89e48ba 100644 --- a/src/quo/components/messages/gap.cljs +++ b/src/quo/components/messages/gap.cljs @@ -5,8 +5,7 @@ [quo.components.markdown.text :as text] [quo.foundations.colors :as colors] [quo.theme :as theme] - [react-native.core :as rn] - [reagent.core :as reagent])) + [react-native.core :as rn])) ;;; helpers (def themes @@ -137,24 +136,25 @@ style on-press warning-label]}] - (let [body-height (reagent/atom nil)] - (fn [] - [rn/view - {:on-layout #(reset! body-height (oget % "nativeEvent.layout.height")) - :overflow :hidden - :flex 1} - [hborder {:type :top}] - [hborder {:type :bottom}] - [rn/view - (merge {:width "100%" - :background-color (get-color :background) - :flex-direction :row - :padding 20 - :padding-left 31 - :margin-vertical 4} - style) + (let [[body-height set-body-height] (rn/use-state nil) + on-layout (rn/use-callback #(set-body-height + (oget % "nativeEvent.layout.height")))] + [rn/view + {:on-layout on-layout + :overflow :hidden + :flex 1} + [hborder {:type :top}] + [hborder {:type :bottom}] + [rn/view + (merge {:width "100%" + :background-color (get-color :background) + :flex-direction :row + :padding 20 + :padding-left 31 + :margin-vertical 4} + style) - [timeline] - [body timestamp-far timestamp-near on-info-button-pressed on-press warning-label]] - [vborder :left body-height] - [vborder :right body-height]]))) + [timeline] + [body timestamp-far timestamp-near on-info-button-pressed on-press warning-label]] + [vborder :left body-height] + [vborder :right body-height]])) diff --git a/src/quo/components/numbered_keyboard/keyboard_key/view.cljs b/src/quo/components/numbered_keyboard/keyboard_key/view.cljs index bcd15c4ac3..a53ee76ccc 100644 --- a/src/quo/components/numbered_keyboard/keyboard_key/view.cljs +++ b/src/quo/components/numbered_keyboard/keyboard_key/view.cljs @@ -4,50 +4,48 @@ [quo.components.markdown.text :as text] [quo.components.numbered-keyboard.keyboard-key.style :as style] [quo.theme :as quo.theme] - [react-native.core :as rn] - [reagent.core :as reagent])) + [react-native.core :as rn])) (defn- label->accessibility-label [label] (let [label-name (if (keyword? label) (name label) label)] (keyword (str "keyboard-key-" label-name)))) -(defn- view-internal - [] - (let [pressed? (reagent/atom false)] - (fn [{:keys [disabled? theme blur? on-press on-long-press type]} label] - (let [label-color (style/get-label-color disabled? theme blur?) - background-color (style/toggle-background-color @pressed? blur? theme)] - [rn/pressable - {:accessibility-label (label->accessibility-label label) - :disabled (or disabled? (not label)) - :on-press (fn [] - (when on-press - (on-press label))) - :on-long-press (fn [] - (when (fn? on-long-press) - (on-long-press label))) - :allow-multiple-presses? true - :on-press-in #(reset! pressed? true) - :on-press-out #(reset! pressed? false) - :hit-slop {:top 8 :bottom 8 :left 25 :right 25} - :style (style/container background-color)} - (case type - :key [icons/icon - label - {:color label-color - :accessibility-label :icon-label}] - :digit [text/text - {:accessibility-label :text-label - :weight :regular - :size :heading-1 - :style {:color label-color}} - label] - :derivation-path [icons/icon - :i/derivation-path - {:color label-color - :size 32 - :accessibility-label :derivation-path-label}] - nil)])))) - -(def view (quo.theme/with-theme view-internal)) +(defn view + [{:keys [disabled? blur? on-press on-long-press type]} label] + (let [theme (quo.theme/use-theme-value) + [pressed? set-pressed?] (rn/use-state false) + on-press (rn/use-callback #(when on-press (on-press label)) [label]) + on-long-press (rn/use-callback #(when (fn? on-long-press) (on-long-press label)) + [label]) + on-press-in (rn/use-callback #(set-pressed? true)) + on-press-out (rn/use-callback #(set-pressed? false)) + label-color (style/get-label-color disabled? theme blur?) + background-color (style/toggle-background-color pressed? blur? theme)] + [rn/pressable + {:accessibility-label (label->accessibility-label label) + :disabled (or disabled? (not label)) + :on-press on-press + :on-long-press on-long-press + :allow-multiple-presses? true + :on-press-in on-press-in + :on-press-out on-press-out + :hit-slop {:top 8 :bottom 8 :left 25 :right 25} + :style (style/container background-color)} + (case type + :key [icons/icon + label + {:color label-color + :accessibility-label :icon-label}] + :digit [text/text + {:accessibility-label :text-label + :weight :regular + :size :heading-1 + :style {:color label-color}} + label] + :derivation-path [icons/icon + :i/derivation-path + {:color label-color + :size 32 + :accessibility-label :derivation-path-label}] + nil)])) diff --git a/src/quo/components/profile/select_profile/view.cljs b/src/quo/components/profile/select_profile/view.cljs index dbdacd46c9..51d413367e 100644 --- a/src/quo/components/profile/select_profile/view.cljs +++ b/src/quo/components/profile/select_profile/view.cljs @@ -3,48 +3,33 @@ [quo.components.avatars.user-avatar.view :as user-avatar] [quo.components.markdown.text :as text] [quo.components.profile.select-profile.style :as style] - [react-native.core :as rn] - [reagent.core :as reagent])) - -(defn- on-change-handler - [selected? on-change] - (swap! selected? not) - (when on-change (on-change @selected?))) + [react-native.core :as rn])) (defn view "Options - - `default-selected?` The default selected state. Use when the component is not controlled. - `selected?` Selected state. Use when the component is controlled. - `name` Full name - `profile-picture` Profile picture - `customization-color` Customization color - `(on-change selected?)` On change event handler " - [{:keys [default-selected?]}] - (let [internal-selected? (reagent/atom (or default-selected? false))] - (fn [{:keys [selected? - profile-picture - name - customization-color - on-change] - :or {customization-color :turquoise}}] - (when (and (not (nil? selected?)) (not= @internal-selected? selected?)) - (reset! internal-selected? selected?)) - [rn/touchable-opacity - {:style (style/container customization-color @internal-selected?) - :on-press #(on-change-handler internal-selected? on-change) - :active-opacity 1 - :accessibility-label :select-profile} - [rn/view {:style style/header} - [user-avatar/user-avatar - {:full-name name - :status-indicator? false - :size :medium - :profile-picture profile-picture}] - [rn/view {:style (style/select-radio @internal-selected?)} - (when @internal-selected? [rn/view {:style style/select-radio-inner}])]] - [text/text - {:size :heading-2 - :weight :semi-bold - :style style/profile-name} - name]]))) + [{:keys [selected? profile-picture name customization-color on-change] + :or {customization-color :turquoise}}] + [rn/touchable-opacity + {:style (style/container customization-color selected?) + :on-press #(when on-change (on-change (not selected?))) + :accessibility-label :select-profile} + [rn/view {:style style/header} + [user-avatar/user-avatar + {:full-name name + :status-indicator? false + :size :medium + :profile-picture profile-picture}] + [rn/view {:style (style/select-radio selected?)} + (when selected? + [rn/view {:style style/select-radio-inner}])]] + [text/text + {:size :heading-2 + :weight :semi-bold + :style style/profile-name} + name]]) diff --git a/src/quo/components/profile/showcase_nav/component_spec.cljs b/src/quo/components/profile/showcase_nav/component_spec.cljs index d4015be52c..85873ef132 100644 --- a/src/quo/components/profile/showcase_nav/component_spec.cljs +++ b/src/quo/components/profile/showcase_nav/component_spec.cljs @@ -1,7 +1,6 @@ (ns quo.components.profile.showcase-nav.component-spec (:require [quo.components.profile.showcase-nav.view :as view] - [reagent.core :as reagent] [test-helpers.component :as h])) (def nav-data @@ -35,7 +34,7 @@ (.toHaveBeenCalled)))) (h/test "active id updated" - (let [active-id (reagent/atom :recent)] + (let [active-id (atom :recent)] (h/render [view/view {:data nav-data :on-press #(reset! active-id %)}]) diff --git a/src/quo/components/settings/category/reorder/view.cljs b/src/quo/components/settings/category/reorder/view.cljs index bc1f8a0636..fc72a9a071 100644 --- a/src/quo/components/settings/category/reorder/view.cljs +++ b/src/quo/components/settings/category/reorder/view.cljs @@ -7,17 +7,23 @@ [quo.foundations.colors :as colors] [quo.theme :as quo.theme] [react-native.core :as rn] - [react-native.draggable-flatlist :as draggable-flatlist] - [reagent.core :as reagent])) + [react-native.draggable-flatlist :as draggable-flatlist])) -(defn on-drag-end-fn - [data atom-data] - (reset! atom-data data) - (reagent/flush)) +(defn key-fn [item index] (str (:title item) index)) -(defn- reorder-category-internal - [{:keys [label data blur? theme container-style]}] - (reagent/with-let [atom-data (reagent/atom data)] +(defn reorder-category + [{:keys [label data blur? container-style]}] + (let [theme (quo.theme/use-theme-value) + [atom-data set-atom-data] (rn/use-state data) + render-fn (rn/use-callback + (fn [item _ _ _ _ drag] + [reorder-item/reorder-item item types/item + {:blur? blur? :drag drag}]) + [blur?]) + on-drag-end-fn (rn/use-callback (fn [_ _ data] (set-atom-data data))) + separator (rn/use-memo (fn [] [rn/view + {:style (style/reorder-separator blur? theme)}]) + [blur? theme])] [rn/view {:style (merge (style/container label) container-style)} [text/text {:weight :medium @@ -25,14 +31,9 @@ :style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)}} label] [draggable-flatlist/draggable-flatlist - {:data @atom-data - :key-fn (fn [item index] (str (:title item) index)) + {:data atom-data + :key-fn key-fn :style style/reorder-items - :render-fn (fn [item _ _ _ _ drag] [reorder-item/reorder-item item types/item - {:blur? blur? :drag drag}]) - :on-drag-end-fn (fn [_ _ data] - (on-drag-end-fn data atom-data)) - :separator [rn/view - {:style (style/reorder-separator blur? theme)}]}]])) - -(def reorder-category (quo.theme/with-theme reorder-category-internal)) + :render-fn render-fn + :on-drag-end-fn on-drag-end-fn + :separator separator}]])) diff --git a/src/quo/components/share/share_qr_code/view.cljs b/src/quo/components/share/share_qr_code/view.cljs index 66e50f1ebd..f203258840 100644 --- a/src/quo/components/share/share_qr_code/view.cljs +++ b/src/quo/components/share/share_qr_code/view.cljs @@ -16,7 +16,6 @@ [quo.foundations.colors :as colors] [quo.theme] [react-native.core :as rn] - [reagent.core :as reagent] [schema.core :as schema] [utils.i18n :as i18n])) @@ -190,20 +189,20 @@ (defn- view-internal [props] - (reagent/with-let [component-width (reagent/atom nil) - container-component [rn/view {:background-color style/overlay-color}]] + (let [[component-width + set-component-width] (rn/use-state nil) + on-layout (rn/use-callback #(set-component-width + (oops/oget % "nativeEvent.layout.width"))) + props (-> props + (assoc :component-width component-width) + (clojure.set/rename-keys {:type :share-qr-type}))] [quo.theme/provider {:theme :dark} [rn/view {:accessibility-label :share-qr-code :style style/outer-container - :on-layout #(reset! component-width (oops/oget % "nativeEvent.layout.width"))} - (conj container-component - (when @component-width - [share-qr-code - (-> props - (assoc :component-width @component-width) - (clojure.set/rename-keys {:type :share-qr-type}))]))]])) + :on-layout on-layout} + [rn/view {:style {:background-color style/overlay-color}} + (when component-width + [share-qr-code props])]]])) -(def view - (quo.theme/with-theme - (schema/instrument #'view-internal component-schema/?schema))) +(def view (schema/instrument #'view-internal component-schema/?schema)) diff --git a/src/quo/components/tabs/segmented_tab.cljs b/src/quo/components/tabs/segmented_tab.cljs index ec8563fac3..de93566046 100644 --- a/src/quo/components/tabs/segmented_tab.cljs +++ b/src/quo/components/tabs/segmented_tab.cljs @@ -3,8 +3,7 @@ [quo.components.tabs.tab.view :as tab] [quo.foundations.colors :as colors] [quo.theme :as quo.theme] - [react-native.core :as rn] - [reagent.core :as reagent])) + [react-native.core :as rn])) (def themes-for-blur {:light {:background-color colors/neutral-80-opa-5} @@ -14,41 +13,41 @@ {:light {:background-color colors/neutral-10} :dark {:background-color colors/neutral-90}}) -(defn- segmented-control-internal - [{:keys [default-active on-change]}] - (let [active-tab-id (reagent/atom default-active)] - (fn [{:keys [data size theme blur? container-style item-container-style - active-item-container-style]}] - (let [active-id @active-tab-id] - [rn/view - (merge - {:flex-direction :row - :background-color (get-in (if blur? themes-for-blur themes) - [theme :background-color]) - :border-radius (case size - 32 10 - 28 10 - 24 8 - 20 6) - :padding 2} - container-style) - (for [[indx {:keys [label id]}] (map-indexed vector data)] - ^{:key id} - [rn/view - {:margin-left (if (= 0 indx) 0 2) - :flex 1} - [tab/view - {:id id - :active-item-container-style active-item-container-style - :item-container-style item-container-style - :segmented? true - :size size - :blur? blur? - :active (= id active-id) - :on-press (fn [tab-id] - (reset! active-tab-id tab-id) - (when on-change - (on-change tab-id)))} - label]])])))) - -(def segmented-control (quo.theme/with-theme segmented-control-internal)) +(defn segmented-control + [{:keys [data size blur? container-style item-container-style + active-item-container-style default-active on-change]}] + (let [theme (quo.theme/use-theme-value) + [active-tab-id + set-active-tab-id] (rn/use-state default-active) + on-press (rn/use-callback + (fn [tab-id] + (set-active-tab-id tab-id) + (when on-change (on-change tab-id))) + [on-change])] + [rn/view + (merge + {:flex-direction :row + :background-color (get-in (if blur? themes-for-blur themes) + [theme :background-color]) + :border-radius (case size + 32 10 + 28 10 + 24 8 + 20 6) + :padding 2} + container-style) + (for [[indx {:keys [label id]}] (map-indexed vector data)] + ^{:key id} + [rn/view + {:margin-left (if (= 0 indx) 0 2) + :flex 1} + [tab/view + {:id id + :active-item-container-style active-item-container-style + :item-container-style item-container-style + :segmented? true + :size size + :blur? blur? + :active (= id active-tab-id) + :on-press on-press} + label]])])) diff --git a/src/quo/components/tabs/tabs/view.cljs b/src/quo/components/tabs/tabs/view.cljs index aeebe9b381..971b511d57 100644 --- a/src/quo/components/tabs/tabs/view.cljs +++ b/src/quo/components/tabs/tabs/view.cljs @@ -41,7 +41,7 @@ (into [:<>] children))) (defn- on-scroll-handler - [{:keys [on-scroll fading fade-end-percentage fade-end?]} ^js e] + [{:keys [on-scroll fading set-fading fade-end-percentage fade-end?]} ^js e] (when fade-end? (let [offset-x (oget e "nativeEvent.contentOffset.x") content-width (oget e "nativeEvent.contentSize.width") @@ -52,11 +52,8 @@ :layout-width layout-width :max-fade-percentage fade-end-percentage})] ;; Avoid unnecessary re-rendering. - (when (not= new-percentage - (get @fading :fade-end-percentage)) - (swap! fading assoc - :fade-end-percentage - new-percentage)))) + (when (not= new-percentage (get fading :fade-end-percentage)) + (set-fading (assoc fading :fade-end-percentage new-percentage))))) (when on-scroll (on-scroll e))) @@ -64,6 +61,7 @@ [{:keys [id label notification-dot? accessibility-label]} index _ {:keys [active-tab-id + set-active-tab-id blur? customization-color flat-list-ref @@ -85,9 +83,9 @@ :accessibility-label accessibility-label :size size :blur? blur? - :active (= id @active-tab-id) + :active (= id active-tab-id) :on-press (fn [id] - (reset! active-tab-id id) + (set-active-tab-id id) (when (and scroll-on-press? @flat-list-ref) (.scrollToIndex ^js @flat-list-ref #js @@ -118,87 +116,81 @@ - `scroll-on-press?` When non-nil, clicking on a tag centers it the middle (with animation enabled). " - [{:keys [default-active fade-end-percentage] - :or {fade-end-percentage 0.8}}] - (let [active-tab-id (reagent/atom default-active) - fading (reagent/atom {:fade-end-percentage fade-end-percentage}) - flat-list-ref (atom nil)] - (fn - [{:keys [data - fade-end-percentage - fade-end? - on-change - on-scroll - scroll-on-press? - scrollable? - style - container-style - size - blur? - in-scroll-view? - customization-color] - :or {fade-end-percentage fade-end-percentage - fade-end? false - scrollable? false - scroll-on-press? false - size default-tab-size} - :as props}] - (if scrollable? - [rn/view {:style {:margin-top (- (dec unread-count-offset))}} - [masked-view-wrapper - {:fade-end-percentage (get @fading :fade-end-percentage) :fade-end? fade-end?} - [(if in-scroll-view? - gesture/flat-list - rn/flat-list) - (merge - (dissoc props - :default-active - :fade-end-percentage - :fade-end? - :on-change - :scroll-on-press? - :size) - (when scroll-on-press? - {:initial-scroll-index (utils.collection/first-index #(= @active-tab-id (:id %)) data)}) - {:ref #(reset! flat-list-ref %) - :style style - ;; The padding-top workaround is needed because on Android - ;; {:overflow :visible} doesn't work on components inheriting - ;; from ScrollView (e.g. FlatList). There are open issues, here's - ;; just one about this topic: - ;; https://github.com/facebook/react-native/issues/31218 - :content-container-style - (assoc container-style :padding-top (dec unread-count-offset)) - :horizontal true - :scroll-event-throttle 64 - :shows-horizontal-scroll-indicator false - :data data - :key-fn (comp str :id) - :on-scroll-to-index-failed identity - :on-scroll (partial on-scroll-handler - {:fade-end-percentage fade-end-percentage - :fade-end? fade-end? - :fading fading - :on-scroll on-scroll}) - :render-fn tab-view - :render-data {:active-tab-id active-tab-id - :blur? blur? - :customization-color customization-color - :flat-list-ref flat-list-ref - :number-of-items (count data) - :on-change on-change - :scroll-on-press? scroll-on-press? - :size size - :style style}})]]] - [rn/view (merge style {:flex-direction :row}) - (map-indexed (fn [index item] - ^{:key (:id item)} - [tab-view item index nil - {:active-tab-id active-tab-id - :blur? blur? - :customization-color customization-color - :number-of-items (count data) - :on-change on-change - :size size - :style style}]) - data)])))) + [{:keys [default-active data fade-end-percentage fade-end? on-change on-scroll scroll-on-press? + scrollable? style container-style size blur? in-scroll-view? customization-color] + :or {fade-end-percentage 0.8 + fade-end? false + scrollable? false + scroll-on-press? false + size default-tab-size} + :as props}] + (let [[active-tab-id + set-active-tab-id] (rn/use-state default-active) + [fading set-fading] (rn/use-state {:fade-end-percentage fade-end-percentage}) + flat-list-ref (rn/use-ref-atom nil) + clean-props (dissoc props + :default-active + :fade-end-percentage + :fade-end? + :on-change + :scroll-on-press? + :size) + on-scroll (rn/use-callback + (partial on-scroll-handler + {:fade-end-percentage fade-end-percentage + :fade-end? fade-end? + :fading fading + :set-fading set-fading + :on-scroll on-scroll}) + [fade-end? fading])] + (if scrollable? + [rn/view {:style {:margin-top (- (dec unread-count-offset))}} + [masked-view-wrapper + {:fade-end-percentage (get fading :fade-end-percentage) :fade-end? fade-end?} + [(if in-scroll-view? + gesture/flat-list + rn/flat-list) + (merge + clean-props + (when scroll-on-press? + {:initial-scroll-index (utils.collection/first-index #(= active-tab-id (:id %)) data)}) + {:ref #(reset! flat-list-ref %) + :style style + ;; The padding-top workaround is needed because on Android + ;; {:overflow :visible} doesn't work on components inheriting + ;; from ScrollView (e.g. FlatList). There are open issues, here's + ;; just one about this topic: + ;; https://github.com/facebook/react-native/issues/31218 + :content-container-style + (assoc container-style :padding-top (dec unread-count-offset)) + :horizontal true + :scroll-event-throttle 64 + :shows-horizontal-scroll-indicator false + :data data + :key-fn (comp str :id) + :on-scroll-to-index-failed identity + :on-scroll on-scroll + :render-fn tab-view + :render-data {:active-tab-id active-tab-id + :set-active-tab-id set-active-tab-id + :blur? blur? + :customization-color customization-color + :flat-list-ref flat-list-ref + :number-of-items (count data) + :on-change on-change + :scroll-on-press? scroll-on-press? + :size size + :style style}})]]] + [rn/view (merge style {:flex-direction :row}) + (map-indexed (fn [index item] + ^{:key (:id item)} + [tab-view item index nil + {:active-tab-id active-tab-id + :set-active-tab-id set-active-tab-id + :blur? blur? + :customization-color customization-color + :number-of-items (count data) + :on-change on-change + :size size + :style style}]) + data)])))