Bottom sheet component
This commit is contained in:
parent
89725d693a
commit
10739cd2e4
|
@ -0,0 +1,56 @@
|
||||||
|
(ns status-im.ui.components.bottom-sheet.styles
|
||||||
|
(:require [status-im.ui.components.colors :as colors]
|
||||||
|
[status-im.utils.platform :as platform]))
|
||||||
|
|
||||||
|
(def border-radius 16)
|
||||||
|
(def bottom-padding (if platform/iphone-x? 34 8))
|
||||||
|
(def bottom-view-height 1000)
|
||||||
|
|
||||||
|
(def container
|
||||||
|
{:position :absolute
|
||||||
|
:left 0
|
||||||
|
:top 0
|
||||||
|
:right 0
|
||||||
|
:bottom 0
|
||||||
|
:flex 1
|
||||||
|
:justify-content :flex-end})
|
||||||
|
|
||||||
|
(defn shadow [opacity-value]
|
||||||
|
{:flex 1
|
||||||
|
:position :absolute
|
||||||
|
:left 0
|
||||||
|
:top 0
|
||||||
|
:right 0
|
||||||
|
:bottom 0
|
||||||
|
:opacity opacity-value
|
||||||
|
:background-color colors/black-transparent-40})
|
||||||
|
|
||||||
|
(defn content-container
|
||||||
|
[content-height bottom-value]
|
||||||
|
{:background-color colors/white
|
||||||
|
:border-top-left-radius border-radius
|
||||||
|
:border-top-right-radius border-radius
|
||||||
|
:height (+ content-height border-radius bottom-view-height)
|
||||||
|
:bottom (- bottom-view-height)
|
||||||
|
:align-self :stretch
|
||||||
|
:transform [{:translateY bottom-value}]
|
||||||
|
:justify-content :flex-start
|
||||||
|
:align-items :center
|
||||||
|
:padding-bottom bottom-padding})
|
||||||
|
|
||||||
|
(def content-header
|
||||||
|
{:height border-radius
|
||||||
|
:align-self :stretch
|
||||||
|
:justify-content :center
|
||||||
|
:align-items :center})
|
||||||
|
|
||||||
|
(def handle
|
||||||
|
{:width 31
|
||||||
|
:height 4
|
||||||
|
:background-color colors/gray-transparent-40
|
||||||
|
:border-radius 2})
|
||||||
|
|
||||||
|
(def bottom-view
|
||||||
|
{:background-color colors/white
|
||||||
|
:height bottom-view-height
|
||||||
|
:align-self :stretch})
|
|
@ -0,0 +1,146 @@
|
||||||
|
(ns status-im.ui.components.bottom-sheet.view
|
||||||
|
(:require [status-im.ui.components.react :as react]
|
||||||
|
[status-im.ui.components.animation :as animation]
|
||||||
|
[status-im.ui.components.bottom-sheet.styles :as styles]
|
||||||
|
[reagent.core :as reagent]))
|
||||||
|
|
||||||
|
(def initial-animation-duration 300)
|
||||||
|
(def release-animation-duration 150)
|
||||||
|
(def cancellation-animation-duration 100)
|
||||||
|
(def swipe-opacity-range 100)
|
||||||
|
(def cancellation-height 180)
|
||||||
|
(def min-opacity 0.05)
|
||||||
|
(def min-velocity 0.1)
|
||||||
|
|
||||||
|
(defn- animate
|
||||||
|
[{:keys [opacity new-opacity-value
|
||||||
|
bottom new-bottom-value
|
||||||
|
duration callback]}]
|
||||||
|
(animation/start
|
||||||
|
(animation/parallel
|
||||||
|
[(animation/timing opacity
|
||||||
|
{:toValue new-opacity-value
|
||||||
|
:duration duration
|
||||||
|
:useNativeDriver true})
|
||||||
|
(animation/timing bottom
|
||||||
|
{:toValue new-bottom-value
|
||||||
|
:duration duration
|
||||||
|
:useNativeDriver true})])
|
||||||
|
(when (fn? callback) callback)))
|
||||||
|
|
||||||
|
(defn animate-sign-panel
|
||||||
|
[opacity-value bottom-value]
|
||||||
|
(animate {:bottom bottom-value
|
||||||
|
:new-bottom-value 0
|
||||||
|
:opacity opacity-value
|
||||||
|
:new-opacity-value 1
|
||||||
|
:duration initial-animation-duration}))
|
||||||
|
|
||||||
|
(defn- on-move
|
||||||
|
[{:keys [height bottom-value opacity-value]}]
|
||||||
|
(fn [_ state]
|
||||||
|
(let [dy (.-dy state)]
|
||||||
|
(cond (pos? dy)
|
||||||
|
(let [opacity (max min-opacity (- 1 (/ dy (- height swipe-opacity-range))))]
|
||||||
|
(animation/set-value bottom-value dy)
|
||||||
|
(animation/set-value opacity-value opacity))
|
||||||
|
(neg? dy)
|
||||||
|
(animation/set-value bottom-value (/ dy 2))))))
|
||||||
|
|
||||||
|
(defn cancelled? [height dy vy]
|
||||||
|
(or
|
||||||
|
(<= min-velocity vy)
|
||||||
|
(> cancellation-height (- height dy))))
|
||||||
|
|
||||||
|
(defn- cancel
|
||||||
|
([opts] (cancel opts nil))
|
||||||
|
([{:keys [height bottom-value show-sheet? opacity-value]} callback]
|
||||||
|
(animate {:bottom bottom-value
|
||||||
|
:new-bottom-value height
|
||||||
|
:opacity opacity-value
|
||||||
|
:new-opacity-value 0
|
||||||
|
:duration cancellation-animation-duration
|
||||||
|
:callback #(do (reset! show-sheet? false)
|
||||||
|
(animation/set-value bottom-value height)
|
||||||
|
(animation/set-value opacity-value 0)
|
||||||
|
(when (fn? callback) (callback)))})))
|
||||||
|
|
||||||
|
(defn- on-release
|
||||||
|
[{:keys [height bottom-value opacity-value on-cancel] :as opts}]
|
||||||
|
(fn [_ state]
|
||||||
|
(let [{:strs [dy vy]} (js->clj state)]
|
||||||
|
(if (cancelled? height dy vy)
|
||||||
|
(cancel opts on-cancel)
|
||||||
|
(animate {:bottom bottom-value
|
||||||
|
:new-bottom-value 0
|
||||||
|
:opacity opacity-value
|
||||||
|
:new-opacity-value 1
|
||||||
|
:duration release-animation-duration})))))
|
||||||
|
|
||||||
|
(defn swipe-pan-responder [opts]
|
||||||
|
(.create
|
||||||
|
react/pan-responder
|
||||||
|
(clj->js
|
||||||
|
{:onMoveShouldSetPanResponder (fn [_ state]
|
||||||
|
(or (< 10 (js/Math.abs (.-dx state)))
|
||||||
|
(< 5 (js/Math.abs (.-dy state)))))
|
||||||
|
:onPanResponderMove (on-move opts)
|
||||||
|
:onPanResponderRelease (on-release opts)
|
||||||
|
:onPanResponderTerminate (on-release opts)})))
|
||||||
|
|
||||||
|
(defn pan-handlers [pan-responder]
|
||||||
|
(js->clj (.-panHandlers pan-responder)))
|
||||||
|
|
||||||
|
(defn- bottom-sheet-view
|
||||||
|
[{:keys [opacity-value bottom-value]}]
|
||||||
|
(reagent.core/create-class
|
||||||
|
{:component-did-mount
|
||||||
|
#(animate-sign-panel opacity-value bottom-value)
|
||||||
|
:reagent-render
|
||||||
|
(fn [{:keys [opacity-value bottom-value
|
||||||
|
height content on-cancel]
|
||||||
|
:as opts}]
|
||||||
|
[react/view
|
||||||
|
(merge
|
||||||
|
(pan-handlers (swipe-pan-responder opts))
|
||||||
|
{:style styles/container})
|
||||||
|
[react/touchable-highlight
|
||||||
|
{:on-press #(cancel opts on-cancel)
|
||||||
|
:style styles/container}
|
||||||
|
|
||||||
|
[react/animated-view (styles/shadow opacity-value)]]
|
||||||
|
[react/animated-view
|
||||||
|
{:style (styles/content-container height bottom-value)}
|
||||||
|
[react/view styles/content-header
|
||||||
|
[react/view styles/handle]]
|
||||||
|
content
|
||||||
|
[react/view {:style styles/bottom-view}]]])}))
|
||||||
|
|
||||||
|
(defn bottom-sheet
|
||||||
|
[{:keys [show? content-height on-cancel]} _]
|
||||||
|
{:pre [(fn? on-cancel) (pos? content-height)]}
|
||||||
|
(let [show-sheet? (reagent/atom show?)
|
||||||
|
total-content-height (+ content-height styles/border-radius
|
||||||
|
styles/bottom-padding)
|
||||||
|
bottom-value (animation/create-value total-content-height)
|
||||||
|
opacity-value (animation/create-value 0)
|
||||||
|
opts {:height total-content-height
|
||||||
|
:bottom-value bottom-value
|
||||||
|
:opacity-value opacity-value
|
||||||
|
:show-sheet? show-sheet?
|
||||||
|
:on-cancel on-cancel}]
|
||||||
|
(reagent.core/create-class
|
||||||
|
{:component-will-update
|
||||||
|
(fn [this [_ new-args]]
|
||||||
|
(let [old-args (second (.-argv (.-props this)))
|
||||||
|
old-show? (:show? old-args)
|
||||||
|
new-show? (:show? new-args)]
|
||||||
|
(cond (and (not old-show?) new-show?)
|
||||||
|
(reset! show-sheet? true)
|
||||||
|
|
||||||
|
(and old-show? (not new-show?) (true? @show-sheet?))
|
||||||
|
(cancel opts))))
|
||||||
|
:reagent-render
|
||||||
|
(fn [_ content]
|
||||||
|
(when @show-sheet?
|
||||||
|
[bottom-sheet-view (assoc opts :content content)]))})))
|
|
@ -22,10 +22,12 @@
|
||||||
;; BLACK
|
;; BLACK
|
||||||
(def black "#000000") ;; Used as the default text color
|
(def black "#000000") ;; Used as the default text color
|
||||||
(def black-transparent (alpha black 0.1)) ;; Used as background color for rounded button on dark background and as background color for containers like "Backup seed phrase"
|
(def black-transparent (alpha black 0.1)) ;; Used as background color for rounded button on dark background and as background color for containers like "Backup seed phrase"
|
||||||
|
(def black-transparent-40 (alpha black 0.4))
|
||||||
(def gray-light black-transparent) ;; Used as divider color
|
(def gray-light black-transparent) ;; Used as divider color
|
||||||
|
|
||||||
;; DARK GREY
|
;; DARK GREY
|
||||||
(def gray "#939ba1") ;; Dark grey, used as a background for a light foreground and as section header and secondary text color
|
(def gray "#939ba1") ;; Dark grey, used as a background for a light foreground and as section header and secondary text color
|
||||||
|
(def gray-transparent-40 (alpha gray 0.4))
|
||||||
;; LIGHT GREY
|
;; LIGHT GREY
|
||||||
(def gray-lighter "#eef2f5") ;; Light Grey, used as a background or shadow
|
(def gray-lighter "#eef2f5") ;; Light Grey, used as a background or shadow
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue