From bd9e440839afa97badd015626f9c3a47d3d985e1 Mon Sep 17 00:00:00 2001 From: Nikolay Date: Tue, 16 Jul 2024 13:42:38 +0300 Subject: [PATCH] [18942] Slideshow: slider-bar component (#20615) --- .../slideshow/slider_bar/component_spec.cljs | 25 +++ .../slideshow/slider_bar/schema.cljs | 14 ++ .../slideshow/slider_bar/style.cljs | 31 ++++ .../components/slideshow/slider_bar/view.cljs | 150 ++++++++++++++++++ src/quo/core.cljs | 4 + src/quo/core_spec.cljs | 1 + src/status_im/contexts/preview/quo/main.cljs | 3 + .../preview/quo/slideshow/slider_bar.cljs | 49 ++++++ .../contexts/shell/share/wallet/style.cljs | 18 --- .../contexts/shell/share/wallet/view.cljs | 16 +- 10 files changed, 281 insertions(+), 30 deletions(-) create mode 100644 src/quo/components/slideshow/slider_bar/component_spec.cljs create mode 100644 src/quo/components/slideshow/slider_bar/schema.cljs create mode 100644 src/quo/components/slideshow/slider_bar/style.cljs create mode 100644 src/quo/components/slideshow/slider_bar/view.cljs create mode 100644 src/status_im/contexts/preview/quo/slideshow/slider_bar.cljs delete mode 100644 src/status_im/contexts/shell/share/wallet/style.cljs diff --git a/src/quo/components/slideshow/slider_bar/component_spec.cljs b/src/quo/components/slideshow/slider_bar/component_spec.cljs new file mode 100644 index 0000000000..bc9cc1e8d0 --- /dev/null +++ b/src/quo/components/slideshow/slider_bar/component_spec.cljs @@ -0,0 +1,25 @@ +(ns quo.components.slideshow.slider-bar.component-spec + (:require + [quo.components.slideshow.slider-bar.view :as slider-bar] + [test-helpers.component :as h])) + +(h/describe "Slideshow: slider-bar" + (h/test "default render" + (h/render-with-theme-provider [slider-bar/view {:accessibility-label :slider-bar}]) + (h/is-truthy (h/query-by-label-text :slider-bar))) + + (h/test "render with total-amount and active-index" + (h/render-with-theme-provider [slider-bar/view + {:total-amount 4 + :active-index 1 + :accessibility-label :slider-bar}]) + (h/is-truthy (h/query-by-label-text :slider-bar)) + (h/is-equal 4 (count (h/query-all-by-label-text :slide-bar-item)))) + + (h/test "render with total-amount active-index and possible scroll" + (h/render-with-theme-provider [slider-bar/view + {:total-amount 10 + :active-index 7 + :accessibility-label :slider-bar}]) + (h/is-truthy (h/query-by-label-text :slider-bar)) + (h/is-equal 10 (count (h/query-all-by-label-text :slide-bar-item))))) diff --git a/src/quo/components/slideshow/slider_bar/schema.cljs b/src/quo/components/slideshow/slider_bar/schema.cljs new file mode 100644 index 0000000000..e479cc950d --- /dev/null +++ b/src/quo/components/slideshow/slider_bar/schema.cljs @@ -0,0 +1,14 @@ +(ns quo.components.slideshow.slider-bar.schema) + +(def ?schema + [:=> + [:cat + [:map {:closed true} + [:total-amount {:optional true} [:maybe :int]] + [:active-index {:optional true} :int] + [:customization-color {:optional true} + [:maybe :schema.common/customization-color]] + [:blur? {:optional true} [:maybe :boolean]] + [:accessibility-label {:optional true} [:maybe :keyword]] + [:container-style {:optional true} [:maybe :map]]]] + :any]) diff --git a/src/quo/components/slideshow/slider_bar/style.cljs b/src/quo/components/slideshow/slider_bar/style.cljs new file mode 100644 index 0000000000..504a1e289d --- /dev/null +++ b/src/quo/components/slideshow/slider_bar/style.cljs @@ -0,0 +1,31 @@ +(ns quo.components.slideshow.slider-bar.style + (:require + [quo.foundations.colors :as colors])) + +(def list-wrapper + {:align-items :center}) + +(defn list-bar + [bar-width] + {:width bar-width}) + +(defn item-wrapper + [{:keys [size spacing]}] + {:width size + :height size + :margin-left spacing + :flex 1 + :align-items :center + :justify-content :center}) + +(defn item + [{:keys [size spacing active? customization-color theme blur?]}] + {:width size + :height size + :border-radius spacing + :background-color + (cond + (and blur? active?) colors/white + (and blur? (not active?)) colors/white-opa-10 + (and (not blur?) active?) (colors/resolve-color customization-color theme) + :else (colors/theme-colors colors/neutral-20 colors/neutral-80 theme))}) diff --git a/src/quo/components/slideshow/slider_bar/view.cljs b/src/quo/components/slideshow/slider_bar/view.cljs new file mode 100644 index 0000000000..f95f60cbd0 --- /dev/null +++ b/src/quo/components/slideshow/slider_bar/view.cljs @@ -0,0 +1,150 @@ +(ns quo.components.slideshow.slider-bar.view + (:require + [quo.components.slideshow.slider-bar.schema :as component-schema] + [quo.components.slideshow.slider-bar.style :as style] + [quo.theme] + [react-native.core :as rn] + [react-native.reanimated :as reanimated] + [schema.core :as schema])) + +(def item-size 8) +(def item-spacing item-size) +(def max-length 6) +(def bar-width (* (+ item-spacing item-size) max-length)) + +(def micro-scale 0.25) +(def small-scale 0.5) +(def medium-scale 0.75) +(def default-scale 1) + +(def items-before-scroll + (/ max-length 2)) + +(defn- calc-relative-index + "Calculates item index in visible area" + [index active-index total-length] + (let [last-index (dec total-length)] + (cond + (< active-index items-before-scroll) index + (> active-index + (- last-index items-before-scroll)) (+ (- index total-length) max-length) + :else (+ (- index active-index) items-before-scroll)))) + +(defn- calc-first-item-scale + "Calculates scale for first item in visible area" + [active-index] + (cond + (= active-index items-before-scroll) medium-scale + (> active-index items-before-scroll) small-scale + :else default-scale)) + +(defn- calc-second-item-scale + "Calculates scale for second item in visible area" + [active-index] + (if (> active-index items-before-scroll) + medium-scale + default-scale)) + +(defn- calc-last-item-scale + "Calculates scale for last item in visible area" + [active-index total-length] + (let [last-index (dec total-length)] + (if (> active-index (- last-index items-before-scroll)) + medium-scale + small-scale))) + +(defn- calc-last-but-one-item-scale + "Calculates scale for before last item in visible area" + [active-index total-length] + (if (> active-index (- (dec total-length) items-before-scroll)) + default-scale + medium-scale)) + +(defn- get-scale + [index active-index total-length] + (let [relative-index (calc-relative-index index active-index total-length)] + (if (or (= index active-index) + (< total-length max-length)) + default-scale + (cond + (< relative-index 0) micro-scale + (= relative-index 0) (calc-first-item-scale active-index) + (= relative-index 1) (calc-second-item-scale active-index) + (= relative-index 4) (calc-last-but-one-item-scale active-index total-length) + (= relative-index 5) (calc-last-item-scale active-index total-length) + (> relative-index 5) micro-scale + :else + default-scale)))) + +(defn- bar-item + [index _ _ {:keys [active-index total-length customization-color theme blur?]}] + (let [active? (= index active-index) + shared-scale (reanimated/use-shared-value (get-scale index active-index total-length))] + (rn/use-effect (fn [] + (let [new-scale-value (get-scale index active-index total-length)] + (reanimated/animate shared-scale new-scale-value))) + [index active-index total-length]) + [rn/view + {:key index + :style (style/item-wrapper {:size item-size + :spacing item-spacing})} + [reanimated/view + {:accessibility-label :slide-bar-item + :style + (reanimated/apply-animations-to-style + {:transform [{:scale shared-scale}]} + (style/item {:size item-size + :spacing item-spacing + :active? active? + :customization-color customization-color + :theme theme + :blur? blur?}))}]])) + +(defn- get-item-layout + [_ index] + (let [length (+ item-size item-spacing)] + #js {:length length + :index index + :offset (* index length)})) + +(defn- view-internal + [{:keys [customization-color blur? accessibility-label container-style] + :as props}] + (let [active-index (or (:active-index props) 0) + total-amount (or (:total-amount props) 1) + theme (quo.theme/use-theme) + flat-list-ref (rn/use-ref-atom nil) + set-flat-list-ref (rn/use-callback #(reset! flat-list-ref %)) + center-position 0.5 + data (range total-amount) + scroll-to-index (rn/use-callback + (fn [] + (some-> ^js @flat-list-ref + (.scrollToIndex #js {:animated true + :index active-index + :viewOffset item-spacing + :viewPosition center-position}))) + [flat-list-ref active-index])] + (rn/use-effect scroll-to-index [active-index]) + [rn/view + {:style (merge style/list-wrapper container-style) + :accessibility-label accessibility-label} + [reanimated/flat-list + {:style (style/list-bar bar-width) + :data data + :scroll-enabled false + :ref set-flat-list-ref + :shows-horizontal-scroll-indicator false + :bounces false + :horizontal true + :extra-data (str active-index) + :render-data {:active-index active-index + :total-length total-amount + :customization-color customization-color + :theme theme + :blur? blur?} + :get-item-layout get-item-layout + :render-fn bar-item + :key-fn identity}]])) + +(def view (schema/instrument #'view-internal component-schema/?schema)) diff --git a/src/quo/core.cljs b/src/quo/core.cljs index dd01cf0b75..a819ee74d7 100644 --- a/src/quo/core.cljs +++ b/src/quo/core.cljs @@ -147,6 +147,7 @@ quo.components.settings.settings-item.view quo.components.share.qr-code.view quo.components.share.share-qr-code.view + quo.components.slideshow.slider-bar.view quo.components.switchers.group-messaging-card.view quo.components.tabs.account-selector quo.components.tabs.segmented-tab @@ -418,6 +419,9 @@ (def qr-code quo.components.share.qr-code.view/view) (def share-qr-code quo.components.share.share-qr-code.view/view) +;;;; Slideshow +(def slider-bar quo.components.slideshow.slider-bar.view/view) + ;;;; SWITCHER (def group-messaging-card quo.components.switchers.group-messaging-card.view/view) diff --git a/src/quo/core_spec.cljs b/src/quo/core_spec.cljs index aa685d80c5..59d76a356e 100644 --- a/src/quo/core_spec.cljs +++ b/src/quo/core_spec.cljs @@ -87,6 +87,7 @@ quo.components.settings.reorder-item.component-spec quo.components.settings.settings-item.component-spec quo.components.share.share-qr-code.component-spec + quo.components.slideshow.slider-bar.component-spec quo.components.switchers.base-card.component-spec quo.components.switchers.group-messaging-card.component-spec quo.components.tags.collectible-tag.component-spec diff --git a/src/status_im/contexts/preview/quo/main.cljs b/src/status_im/contexts/preview/quo/main.cljs index 2f6ff77299..795cb39300 100644 --- a/src/status_im/contexts/preview/quo/main.cljs +++ b/src/status_im/contexts/preview/quo/main.cljs @@ -170,6 +170,7 @@ [status-im.contexts.preview.quo.settings.settings-item :as settings-item] [status-im.contexts.preview.quo.share.qr-code :as qr-code] [status-im.contexts.preview.quo.share.share-qr-code :as share-qr-code] + [status-im.contexts.preview.quo.slideshow.slider-bar :as slider-bar] [status-im.contexts.preview.quo.style :as style] [status-im.contexts.preview.quo.switcher.group-messaging-card :as group-messaging-card] @@ -502,6 +503,8 @@ :component qr-code/view} {:name :share-qr-code :component share-qr-code/view}] + :slideshow [{:name :slider-bar + :component slider-bar/view}] :switchers [{:name :group-messaging-card :component group-messaging-card/view} {:name :switcher-cards diff --git a/src/status_im/contexts/preview/quo/slideshow/slider_bar.cljs b/src/status_im/contexts/preview/quo/slideshow/slider_bar.cljs new file mode 100644 index 0000000000..c2d332305b --- /dev/null +++ b/src/status_im/contexts/preview/quo/slideshow/slider_bar.cljs @@ -0,0 +1,49 @@ +(ns status-im.contexts.preview.quo.slideshow.slider-bar + (:require + [quo.core :as quo] + [react-native.core :as rn] + [status-im.contexts.preview.quo.preview :as preview])) + +(def descriptor + [{:key :total-amount + :type :number} + {:label "Blur (dark only)?" + :key :blur? + :type :boolean} + (preview/customization-color-option)]) + +(defn view + [] + (let [[state set-state] (rn/use-state {:total-amount 10 + :active-index 0 + :blur? false + :customization-color :blue})] + [preview/preview-container + {:state state + :set-state set-state + :descriptor descriptor + :blur? (:blur? state) + :show-blur-background? true + :blur-dark-only? true} + [rn/view + {:style {:flex-direction :row + :justify-content :center + :gap 10 + :padding 10}} + [quo/button + {:type :outline + :style {:disabled true} + :disabled? (zero? (:active-index state)) + :icon-only? true + :on-press (fn [] + (set-state (update state :active-index dec)))} + :i/chevron-left] + [quo/button + {:type :outline + :disabled? (= (:active-index state) + (dec (:total-amount state))) + :icon-only? true + :on-press (fn [] + (set-state (update state :active-index inc)))} + :i/chevron-right]] + [quo/slider-bar state]])) diff --git a/src/status_im/contexts/shell/share/wallet/style.cljs b/src/status_im/contexts/shell/share/wallet/style.cljs deleted file mode 100644 index b5e029c67f..0000000000 --- a/src/status_im/contexts/shell/share/wallet/style.cljs +++ /dev/null @@ -1,18 +0,0 @@ -(ns status-im.contexts.shell.share.wallet.style - (:require - [quo.foundations.colors :as colors])) - -(defn indicator-wrapper-style - [active?] - {:width 8 - :height 8 - :border-radius 4 - :background-color colors/white - :opacity (if active? 1.0 0.5)}) - -(def indicator-list-style - {:display :flex - :flex-direction :row - :align-items :center - :justify-content :center - :gap 8}) diff --git a/src/status_im/contexts/shell/share/wallet/view.cljs b/src/status_im/contexts/shell/share/wallet/view.cljs index 7b379adc93..3962bb3fd6 100644 --- a/src/status_im/contexts/shell/share/wallet/view.cljs +++ b/src/status_im/contexts/shell/share/wallet/view.cljs @@ -6,7 +6,6 @@ [react-native.platform :as platform] [reagent.core :as reagent] [status-im.contexts.shell.share.style :as style] - [status-im.contexts.shell.share.wallet.style :as wallet-style] [status-im.contexts.wallet.common.utils :as utils] [status-im.contexts.wallet.common.utils.networks :as network-utils] [status-im.contexts.wallet.sheets.network-preferences.view :as network-preferences] @@ -85,16 +84,6 @@ :on-legacy-press on-legacy-press :on-settings-press on-settings-press}]]])))) -(defn- indicator - [active?] - [rn/view {:style (wallet-style/indicator-wrapper-style active?)}]) - -(defn- indicator-list - [num-indicators current-index] - [rn/view {:style wallet-style/indicator-list-style} - (for [i (range num-indicators)] - ^{:key i} [indicator (= current-index i)])]) - (defn render-item [{:keys [address] :as account}] [wallet-qr-code-item @@ -136,4 +125,7 @@ :render-fn render-item}] (when (> num-accounts 1) [rn/view {:style {:margin-top 20}} - [indicator-list num-accounts @current-index]])])))) + [quo/slider-bar + {:total-amount num-accounts + :active-index @current-index + :blur? true}]])]))))