Slide button component (bounty) (#16259)
This commit is contained in:
parent
4f4489ee51
commit
d43b73b566
|
@ -0,0 +1,101 @@
|
||||||
|
(ns quo2.components.buttons.slide-button.animations
|
||||||
|
(:require
|
||||||
|
[react-native.gesture :as gesture]
|
||||||
|
[quo2.components.buttons.slide-button.utils :as utils]
|
||||||
|
[oops.core :as oops]
|
||||||
|
[react-native.reanimated :as reanimated]))
|
||||||
|
|
||||||
|
(def ^:private extrapolation
|
||||||
|
{:extrapolateLeft "clamp"
|
||||||
|
:extrapolateRight "clamp"})
|
||||||
|
|
||||||
|
(defn- track-interpolation-inputs
|
||||||
|
[in-vectors track-width]
|
||||||
|
(map (partial * track-width) in-vectors))
|
||||||
|
|
||||||
|
;; Interpolations
|
||||||
|
(defn- track-clamp-interpolation
|
||||||
|
[track-width]
|
||||||
|
{:in [-1 0 1]
|
||||||
|
:out [track-width 0 track-width]})
|
||||||
|
|
||||||
|
(defn- track-cover-interpolation
|
||||||
|
[track-width thumb-size]
|
||||||
|
{:in [0 1]
|
||||||
|
:out [(/ thumb-size 2) track-width]})
|
||||||
|
|
||||||
|
(defn- arrow-icon-position-interpolation
|
||||||
|
[thumb-size]
|
||||||
|
{:in [0.9 1]
|
||||||
|
:out [0 (- thumb-size)]})
|
||||||
|
|
||||||
|
(defn- action-icon-position-interpolation
|
||||||
|
[thumb-size]
|
||||||
|
{:in [0.9 1]
|
||||||
|
:out [thumb-size 0]})
|
||||||
|
|
||||||
|
(defn interpolate-track
|
||||||
|
"Interpolate the position in the track
|
||||||
|
`x-pos` Track animated value
|
||||||
|
`track-width` Usable width of the track
|
||||||
|
`thumb-size` Size of the thumb
|
||||||
|
`interpolation` ` :thumb-border-radius`/`:thumb-drop-position`/`:thumb-drop-scale`/`:thumb-drop-z-index`/..."
|
||||||
|
([x-pos track-width thumb-size interpolation]
|
||||||
|
(let [interpolations {:track-cover (track-cover-interpolation track-width thumb-size)
|
||||||
|
:track-clamp (track-clamp-interpolation track-width)
|
||||||
|
:action-icon-position (action-icon-position-interpolation thumb-size)
|
||||||
|
:arrow-icon-position (arrow-icon-position-interpolation thumb-size)}
|
||||||
|
|
||||||
|
interpolation-values (interpolation interpolations)
|
||||||
|
output (:out interpolation-values)
|
||||||
|
input (-> (:in interpolation-values)
|
||||||
|
(track-interpolation-inputs track-width))]
|
||||||
|
(if interpolation-values
|
||||||
|
(reanimated/interpolate x-pos
|
||||||
|
input
|
||||||
|
output
|
||||||
|
extrapolation)
|
||||||
|
x-pos))))
|
||||||
|
|
||||||
|
;; Animations
|
||||||
|
(defn- animate-spring
|
||||||
|
[value to-value]
|
||||||
|
(reanimated/animate-shared-value-with-spring value
|
||||||
|
to-value
|
||||||
|
{:mass 1
|
||||||
|
:damping 30
|
||||||
|
:stiffness 400}))
|
||||||
|
|
||||||
|
(defn- complete-animation
|
||||||
|
[sliding-complete?]
|
||||||
|
(reset! sliding-complete? true))
|
||||||
|
|
||||||
|
(defn- reset-track-position
|
||||||
|
[x-pos]
|
||||||
|
(animate-spring x-pos 0))
|
||||||
|
|
||||||
|
;; Gestures
|
||||||
|
(defn drag-gesture
|
||||||
|
[x-pos
|
||||||
|
gestures-disabled?
|
||||||
|
disabled?
|
||||||
|
track-width
|
||||||
|
sliding-complete?]
|
||||||
|
(let [gestures-enabled? (not (or disabled? @gestures-disabled?))]
|
||||||
|
(-> (gesture/gesture-pan)
|
||||||
|
(gesture/with-test-ID :slide-button-gestures)
|
||||||
|
(gesture/enabled gestures-enabled?)
|
||||||
|
(gesture/min-distance 0)
|
||||||
|
(gesture/on-update (fn [event]
|
||||||
|
(let [x-translation (oops/oget event "translationX")
|
||||||
|
clamped-x (utils/clamp-value x-translation 0 track-width)
|
||||||
|
reached-end? (>= clamped-x track-width)]
|
||||||
|
(reanimated/set-shared-value x-pos clamped-x)
|
||||||
|
(when (and reached-end? (not @sliding-complete?))
|
||||||
|
(reset! gestures-disabled? true)
|
||||||
|
(complete-animation sliding-complete?)))))
|
||||||
|
(gesture/on-end (fn [event]
|
||||||
|
(let [x-translation (oops/oget event "translationX")
|
||||||
|
reached-end? (>= x-translation track-width)]
|
||||||
|
(when (not reached-end?)
|
||||||
|
(reset-track-position x-pos))))))))
|
|
@ -0,0 +1,105 @@
|
||||||
|
(ns quo2.components.buttons.slide-button.component-spec
|
||||||
|
(:require [quo2.components.buttons.slide-button.view :as slide-button]
|
||||||
|
[quo2.components.buttons.slide-button.constants :as constants]
|
||||||
|
[quo2.components.buttons.slide-button.utils :as utils]
|
||||||
|
["@testing-library/react-native" :as rtl]
|
||||||
|
["react-native-gesture-handler/jest-utils" :as gestures-jest]
|
||||||
|
[reagent.core :as r]
|
||||||
|
[test-helpers.component :as h]))
|
||||||
|
|
||||||
|
;; NOTE stolen from
|
||||||
|
;; (https://github.com/reagent-project/reagent/blob/a14faba55e373000f8f93edfcfce0d1222f7e71a/test/reagenttest/utils.cljs#LL104C7-L104C10),
|
||||||
|
;;
|
||||||
|
;; There's also a comment over there about it being
|
||||||
|
;; not "usable with production React", but no explanation why.
|
||||||
|
;; If we decide to keep it, can be moved to `test-helpers.component`.
|
||||||
|
(defn act
|
||||||
|
"Run f to trigger Reagent updates,
|
||||||
|
will return Promise which will resolve after
|
||||||
|
Reagent and React render."
|
||||||
|
[f]
|
||||||
|
(js/Promise.
|
||||||
|
(fn [resolve reject]
|
||||||
|
(try
|
||||||
|
(.then (rtl/act
|
||||||
|
#(let [p (js/Promise. (fn [resolve _reject]
|
||||||
|
(r/after-render (fn reagent-act-after-reagent-flush []
|
||||||
|
(resolve)))))]
|
||||||
|
(f)
|
||||||
|
p))
|
||||||
|
resolve
|
||||||
|
reject)
|
||||||
|
(catch :default e
|
||||||
|
(reject e))))))
|
||||||
|
|
||||||
|
(def ^:private gesture-state
|
||||||
|
{:untedermined 0
|
||||||
|
:failed 1
|
||||||
|
:began 2
|
||||||
|
:cancelled 3
|
||||||
|
:active 4
|
||||||
|
:end 5})
|
||||||
|
|
||||||
|
(defn gesture-x-event
|
||||||
|
[event position]
|
||||||
|
(clj->js {:state (event gesture-state)
|
||||||
|
:translationX position}))
|
||||||
|
|
||||||
|
(defn slide-events
|
||||||
|
[dest]
|
||||||
|
[(gesture-x-event :began 0)
|
||||||
|
(gesture-x-event :active 0)
|
||||||
|
(gesture-x-event :active dest)
|
||||||
|
(gesture-x-event :end dest)])
|
||||||
|
|
||||||
|
(defn get-by-gesture-test-id
|
||||||
|
[test-id]
|
||||||
|
(gestures-jest/getByGestureTestId
|
||||||
|
(str test-id)))
|
||||||
|
|
||||||
|
(def ^:private default-props
|
||||||
|
{:on-complete identity
|
||||||
|
:track-text :test-track-text
|
||||||
|
:track-icon :face-id})
|
||||||
|
|
||||||
|
(h/describe "slide-button"
|
||||||
|
(h/test "render the correct text"
|
||||||
|
(h/render [slide-button/view default-props])
|
||||||
|
(h/is-truthy (h/get-by-text :test-track-text)))
|
||||||
|
|
||||||
|
(h/test "render the disabled button"
|
||||||
|
(h/render [slide-button/view (assoc default-props :disabled? true)])
|
||||||
|
(let [track-mock (h/get-by-test-id :slide-button-track)]
|
||||||
|
(h/has-style track-mock {:opacity constants/disable-opacity})))
|
||||||
|
|
||||||
|
(h/test "render the small button"
|
||||||
|
(h/render [slide-button/view (assoc default-props :size :small)])
|
||||||
|
(let [mock (h/get-by-test-id :slide-button-track)
|
||||||
|
small-height (:track-height constants/small-dimensions)]
|
||||||
|
(h/has-style mock {:height small-height})))
|
||||||
|
|
||||||
|
(h/test "render with the correct customization-color"
|
||||||
|
(h/render [slide-button/view (assoc default-props :customization-color :purple)])
|
||||||
|
(let [track-mock (h/get-by-test-id :slide-button-track)
|
||||||
|
purple-color (utils/slider-color :track :purple)]
|
||||||
|
(h/has-style track-mock {:backgroundColor purple-color})))
|
||||||
|
|
||||||
|
(h/test
|
||||||
|
"calls on-complete when dragged"
|
||||||
|
(let [props (merge default-props {:on-complete (h/mock-fn)})
|
||||||
|
slide-dest constants/default-width
|
||||||
|
gesture-events (slide-events slide-dest)]
|
||||||
|
(h/render [slide-button/view props])
|
||||||
|
(-> (act #(gestures-jest/fireGestureHandler (get-by-gesture-test-id :slide-button-gestures)
|
||||||
|
gesture-events))
|
||||||
|
(.then #(h/was-called (:on-complete props))))))
|
||||||
|
|
||||||
|
(h/test
|
||||||
|
"doesn't call on-complete if the slide was incomplete"
|
||||||
|
(let [props (merge default-props {:on-complete (h/mock-fn)})
|
||||||
|
slide-dest (- constants/default-width 100)
|
||||||
|
gesture-events (slide-events slide-dest)]
|
||||||
|
(h/render [slide-button/view props])
|
||||||
|
(-> (act #(gestures-jest/fireGestureHandler (get-by-gesture-test-id :slide-button-gestures)
|
||||||
|
gesture-events))
|
||||||
|
(.then #(h/was-not-called (:on-complete props)))))))
|
|
@ -0,0 +1,15 @@
|
||||||
|
(ns quo2.components.buttons.slide-button.constants)
|
||||||
|
|
||||||
|
(def track-padding 4)
|
||||||
|
|
||||||
|
(def small-dimensions
|
||||||
|
{:track-height 40
|
||||||
|
:thumb 32})
|
||||||
|
|
||||||
|
(def large-dimensions
|
||||||
|
{:track-height 48
|
||||||
|
:thumb 40})
|
||||||
|
|
||||||
|
(def disable-opacity 0.3)
|
||||||
|
|
||||||
|
(def default-width 300)
|
|
@ -0,0 +1,80 @@
|
||||||
|
(ns quo2.components.buttons.slide-button.style
|
||||||
|
(:require
|
||||||
|
[quo2.components.buttons.slide-button.constants :as constants]
|
||||||
|
[quo2.components.buttons.slide-button.utils :as utils]
|
||||||
|
[react-native.reanimated :as reanimated]
|
||||||
|
[quo2.foundations.typography :as typography]))
|
||||||
|
|
||||||
|
(def absolute-fill
|
||||||
|
{:position :absolute
|
||||||
|
:top 0
|
||||||
|
:bottom 0
|
||||||
|
:left 0
|
||||||
|
:right 0})
|
||||||
|
|
||||||
|
(defn thumb-container
|
||||||
|
[interpolate-track thumb-size customization-color]
|
||||||
|
(reanimated/apply-animations-to-style
|
||||||
|
{:transform [{:translate-x (interpolate-track :track-clamp)}]}
|
||||||
|
{:background-color (utils/slider-color :main customization-color)
|
||||||
|
:border-radius 12
|
||||||
|
:height thumb-size
|
||||||
|
:width thumb-size
|
||||||
|
:align-items :center
|
||||||
|
:overflow :hidden
|
||||||
|
:justify-content :center}))
|
||||||
|
|
||||||
|
(defn arrow-icon-container
|
||||||
|
[interpolate-track]
|
||||||
|
(reanimated/apply-animations-to-style
|
||||||
|
{:transform [{:translate-x (interpolate-track :arrow-icon-position)}]}
|
||||||
|
{:flex 1
|
||||||
|
:align-items :center
|
||||||
|
:justify-content :center}))
|
||||||
|
|
||||||
|
(defn action-icon
|
||||||
|
[interpolate-track size]
|
||||||
|
(reanimated/apply-animations-to-style
|
||||||
|
{:transform [{:translate-x (interpolate-track :action-icon-position)}]}
|
||||||
|
{:height size
|
||||||
|
:width size
|
||||||
|
:position :absolute
|
||||||
|
:align-items :center
|
||||||
|
:left 0
|
||||||
|
:top 0
|
||||||
|
:flex-direction :row
|
||||||
|
:justify-content :space-around}))
|
||||||
|
|
||||||
|
(defn track
|
||||||
|
[disabled? customization-color height]
|
||||||
|
{:align-items :flex-start
|
||||||
|
:justify-content :center
|
||||||
|
:border-radius 14
|
||||||
|
:height height
|
||||||
|
:align-self :stretch
|
||||||
|
:padding constants/track-padding
|
||||||
|
:opacity (if disabled? 0.3 1)
|
||||||
|
:background-color (utils/slider-color :track customization-color)})
|
||||||
|
|
||||||
|
(defn track-cover
|
||||||
|
[interpolate-track]
|
||||||
|
(reanimated/apply-animations-to-style
|
||||||
|
{:left (interpolate-track :track-cover)}
|
||||||
|
(assoc absolute-fill :overflow :hidden)))
|
||||||
|
|
||||||
|
(defn track-cover-text-container
|
||||||
|
[track-width]
|
||||||
|
{:position :absolute
|
||||||
|
:right 0
|
||||||
|
:top 0
|
||||||
|
:bottom 0
|
||||||
|
:align-items :center
|
||||||
|
:justify-content :center
|
||||||
|
:flex-direction :row
|
||||||
|
:width track-width})
|
||||||
|
|
||||||
|
(defn track-text
|
||||||
|
[customization-color]
|
||||||
|
(-> typography/paragraph-1
|
||||||
|
(merge typography/font-medium)
|
||||||
|
(assoc :color (utils/slider-color :main customization-color))))
|
|
@ -0,0 +1,38 @@
|
||||||
|
(ns quo2.components.buttons.slide-button.utils
|
||||||
|
(:require
|
||||||
|
[quo2.components.buttons.slide-button.constants :as constants]
|
||||||
|
[quo2.foundations.colors :as colors]))
|
||||||
|
|
||||||
|
(defn slider-color
|
||||||
|
"- `color-key` `:main`/`:track`
|
||||||
|
- `customization-color` Customization color"
|
||||||
|
[color-key customization-color]
|
||||||
|
(let [colors-by-key {:main (colors/custom-color-by-theme customization-color 50 60)
|
||||||
|
:track (colors/custom-color-by-theme customization-color 50 60 10 10)}]
|
||||||
|
(color-key colors-by-key)))
|
||||||
|
|
||||||
|
(defn clamp-value
|
||||||
|
[value min-value max-value]
|
||||||
|
(cond
|
||||||
|
(< value min-value) min-value
|
||||||
|
(> value max-value) max-value
|
||||||
|
:else value))
|
||||||
|
|
||||||
|
(defn calc-usable-track
|
||||||
|
"Calculate the track section in which the
|
||||||
|
thumb can move in. Mostly used for interpolations."
|
||||||
|
[track-width thumb-size]
|
||||||
|
(let [double-padding (* constants/track-padding 2)]
|
||||||
|
(- track-width double-padding thumb-size)))
|
||||||
|
|
||||||
|
(defn get-dimensions
|
||||||
|
[track-width size dimension-key]
|
||||||
|
(let [default-dimensions (case size
|
||||||
|
:small constants/small-dimensions
|
||||||
|
:large constants/large-dimensions
|
||||||
|
constants/large-dimensions)]
|
||||||
|
(-> default-dimensions
|
||||||
|
(merge {:usable-track (calc-usable-track
|
||||||
|
track-width
|
||||||
|
(:thumb default-dimensions))})
|
||||||
|
(get dimension-key))))
|
|
@ -0,0 +1,89 @@
|
||||||
|
(ns quo2.components.buttons.slide-button.view
|
||||||
|
(:require
|
||||||
|
[quo2.components.icon :as icon]
|
||||||
|
[quo2.foundations.colors :as colors]
|
||||||
|
[quo2.components.buttons.slide-button.style :as style]
|
||||||
|
[quo2.components.buttons.slide-button.utils :as utils]
|
||||||
|
[quo2.components.buttons.slide-button.animations :as animations]
|
||||||
|
[react-native.gesture :as gesture]
|
||||||
|
[react-native.core :as rn]
|
||||||
|
[reagent.core :as reagent]
|
||||||
|
[oops.core :as oops]
|
||||||
|
[react-native.reanimated :as reanimated]
|
||||||
|
[quo2.components.buttons.slide-button.constants :as constants]))
|
||||||
|
|
||||||
|
(defn- f-slider
|
||||||
|
[{:keys [disabled?]}]
|
||||||
|
(let [track-width (reagent/atom nil)
|
||||||
|
sliding-complete? (reagent/atom false)
|
||||||
|
gestures-disabled? (reagent/atom disabled?)
|
||||||
|
on-track-layout (fn [evt]
|
||||||
|
(let [width (oops/oget evt "nativeEvent.layout.width")]
|
||||||
|
(reset! track-width width)))]
|
||||||
|
|
||||||
|
(fn [{:keys [on-complete
|
||||||
|
track-text
|
||||||
|
track-icon
|
||||||
|
disabled?
|
||||||
|
customization-color
|
||||||
|
size]}]
|
||||||
|
(let [x-pos (reanimated/use-shared-value 0)
|
||||||
|
dimensions (partial utils/get-dimensions
|
||||||
|
(or @track-width constants/default-width)
|
||||||
|
size)
|
||||||
|
interpolate-track (partial animations/interpolate-track
|
||||||
|
x-pos
|
||||||
|
(dimensions :usable-track)
|
||||||
|
(dimensions :thumb))]
|
||||||
|
|
||||||
|
(rn/use-effect (fn []
|
||||||
|
(when @sliding-complete?
|
||||||
|
(on-complete)))
|
||||||
|
[@sliding-complete?])
|
||||||
|
|
||||||
|
[gesture/gesture-detector
|
||||||
|
{:gesture (animations/drag-gesture x-pos
|
||||||
|
gestures-disabled?
|
||||||
|
disabled?
|
||||||
|
(dimensions :usable-track)
|
||||||
|
sliding-complete?)}
|
||||||
|
[reanimated/view
|
||||||
|
{:test-ID :slide-button-track
|
||||||
|
:style (style/track disabled? customization-color (dimensions :track-height))
|
||||||
|
:on-layout (when-not (some? @track-width)
|
||||||
|
on-track-layout)}
|
||||||
|
[reanimated/view {:style (style/track-cover interpolate-track)}
|
||||||
|
[rn/view {:style (style/track-cover-text-container @track-width)}
|
||||||
|
[icon/icon track-icon
|
||||||
|
{:color (utils/slider-color :main customization-color)
|
||||||
|
:size 20}]
|
||||||
|
[rn/view {:width 4}]
|
||||||
|
[rn/text {:style (style/track-text customization-color)} track-text]]]
|
||||||
|
[reanimated/view
|
||||||
|
{:style (style/thumb-container interpolate-track
|
||||||
|
(dimensions :thumb)
|
||||||
|
customization-color)}
|
||||||
|
[reanimated/view {:style (style/arrow-icon-container interpolate-track)}
|
||||||
|
[icon/icon :arrow-right
|
||||||
|
{:color colors/white
|
||||||
|
:size 20}]]
|
||||||
|
[reanimated/view
|
||||||
|
{:style (style/action-icon interpolate-track
|
||||||
|
(dimensions :thumb))}
|
||||||
|
[icon/icon track-icon
|
||||||
|
{:color colors/white
|
||||||
|
:size 20}]]]]]))))
|
||||||
|
|
||||||
|
(defn view
|
||||||
|
"Options
|
||||||
|
- `on-complete` Callback called when the sliding is complete
|
||||||
|
- `disabled?` Boolean that disables the button
|
||||||
|
(_and gestures_)
|
||||||
|
- `size` `:small`/`:large`
|
||||||
|
- `track-text` Text that is shown on the track
|
||||||
|
- `track-icon` Key of the icon shown on the track
|
||||||
|
(e.g. `:face-id`)
|
||||||
|
- `customization-color` Customization color
|
||||||
|
"
|
||||||
|
[props]
|
||||||
|
[:f> f-slider props])
|
|
@ -11,6 +11,7 @@
|
||||||
quo2.components.buttons.button
|
quo2.components.buttons.button
|
||||||
quo2.components.buttons.dynamic-button
|
quo2.components.buttons.dynamic-button
|
||||||
quo2.components.buttons.predictive-keyboard.view
|
quo2.components.buttons.predictive-keyboard.view
|
||||||
|
quo2.components.buttons.slide-button.view
|
||||||
quo2.components.colors.color-picker.view
|
quo2.components.colors.color-picker.view
|
||||||
quo2.components.community.community-card-view
|
quo2.components.community.community-card-view
|
||||||
quo2.components.community.community-list-view
|
quo2.components.community.community-list-view
|
||||||
|
@ -130,6 +131,7 @@
|
||||||
(def button quo2.components.buttons.button/button)
|
(def button quo2.components.buttons.button/button)
|
||||||
(def dynamic-button quo2.components.buttons.dynamic-button/dynamic-button)
|
(def dynamic-button quo2.components.buttons.dynamic-button/dynamic-button)
|
||||||
(def predictive-keyboard quo2.components.buttons.predictive-keyboard.view/view)
|
(def predictive-keyboard quo2.components.buttons.predictive-keyboard.view/view)
|
||||||
|
(def slide-button quo2.components.buttons.slide-button.view/view)
|
||||||
|
|
||||||
;;;; CARDS
|
;;;; CARDS
|
||||||
(def small-option-card quo2.components.onboarding.small-option-card.view/small-option-card)
|
(def small-option-card quo2.components.onboarding.small-option-card.view/small-option-card)
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
[quo2.components.banners.banner.component-spec]
|
[quo2.components.banners.banner.component-spec]
|
||||||
[quo2.components.buttons.--tests--.buttons-component-spec]
|
[quo2.components.buttons.--tests--.buttons-component-spec]
|
||||||
[quo2.components.buttons.predictive-keyboard.component-spec]
|
[quo2.components.buttons.predictive-keyboard.component-spec]
|
||||||
|
[quo2.components.buttons.slide-button.component-spec]
|
||||||
[quo2.components.colors.color-picker.component-spec]
|
[quo2.components.colors.color-picker.component-spec]
|
||||||
[quo2.components.counter.--tests--.counter-component-spec]
|
[quo2.components.counter.--tests--.counter-component-spec]
|
||||||
[quo2.components.counter.step.component-spec]
|
[quo2.components.counter.step.component-spec]
|
||||||
|
|
|
@ -33,12 +33,16 @@
|
||||||
|
|
||||||
(defn max-pointers [gesture count] (.maxPointers ^js gesture count))
|
(defn max-pointers [gesture count] (.maxPointers ^js gesture count))
|
||||||
|
|
||||||
|
(defn min-distance [gesture dist] (.minDistance ^js gesture dist))
|
||||||
|
|
||||||
(defn number-of-taps [gesture count] (.numberOfTaps ^js gesture count))
|
(defn number-of-taps [gesture count] (.numberOfTaps ^js gesture count))
|
||||||
|
|
||||||
(defn enabled [gesture enabled?] (.enabled ^js gesture enabled?))
|
(defn enabled [gesture enabled?] (.enabled ^js gesture enabled?))
|
||||||
|
|
||||||
(defn average-touches [gesture average-touches?] (.averageTouches ^js gesture average-touches?))
|
(defn average-touches [gesture average-touches?] (.averageTouches ^js gesture average-touches?))
|
||||||
|
|
||||||
|
(defn with-test-ID [gesture test-ID] (.withTestId ^js gesture (str test-ID)))
|
||||||
|
|
||||||
(defn simultaneous
|
(defn simultaneous
|
||||||
([g1 g2] (.Simultaneous ^js Gesture g1 g2))
|
([g1 g2] (.Simultaneous ^js Gesture g1 g2))
|
||||||
([g1 g2 g3] (.Simultaneous ^js Gesture g1 g2 g3)))
|
([g1 g2 g3] (.Simultaneous ^js Gesture g1 g2 g3)))
|
||||||
|
@ -77,7 +81,6 @@
|
||||||
|
|
||||||
(def scroll-view (reagent/adapt-react-class ScrollView))
|
(def scroll-view (reagent/adapt-react-class ScrollView))
|
||||||
|
|
||||||
|
|
||||||
;;; Custom gesture section-list
|
;;; Custom gesture section-list
|
||||||
(defn- flatten-sections
|
(defn- flatten-sections
|
||||||
[sections]
|
[sections]
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
(ns status-im2.contexts.quo-preview.buttons.slide-button
|
||||||
|
(:require [quo2.core :as quo]
|
||||||
|
[quo2.foundations.colors :as colors]
|
||||||
|
[react-native.core :as rn]
|
||||||
|
[reagent.core :as reagent]
|
||||||
|
[status-im2.contexts.quo-preview.preview :as preview]))
|
||||||
|
|
||||||
|
(def descriptor
|
||||||
|
[{:label "Size:"
|
||||||
|
:key :size
|
||||||
|
:type :select
|
||||||
|
:options [{:key :large
|
||||||
|
:value "Large"}
|
||||||
|
{:key :small
|
||||||
|
:value "Small"}]}
|
||||||
|
{:label "Disabled:"
|
||||||
|
:key :disabled?
|
||||||
|
:type :boolean}
|
||||||
|
{:label "Custom Color"
|
||||||
|
:key :color
|
||||||
|
:type :select
|
||||||
|
:options (map (fn [color]
|
||||||
|
(let [key (get color :name)]
|
||||||
|
{:key key :value key}))
|
||||||
|
(quo/picker-colors))}])
|
||||||
|
|
||||||
|
(defn cool-preview
|
||||||
|
[]
|
||||||
|
(let [state (reagent/atom {:disabled? false
|
||||||
|
:color :blue
|
||||||
|
:size :large})
|
||||||
|
color (reagent/cursor state [:color])
|
||||||
|
disabled? (reagent/cursor state [:disabled?])
|
||||||
|
size (reagent/cursor state [:size])
|
||||||
|
complete? (reagent/atom false)]
|
||||||
|
(fn []
|
||||||
|
[rn/touchable-without-feedback {:on-press rn/dismiss-keyboard!}
|
||||||
|
[rn/view {:padding-bottom 150}
|
||||||
|
[preview/customizer state descriptor]
|
||||||
|
[rn/view
|
||||||
|
{:padding-vertical 60
|
||||||
|
:padding-horizontal 40
|
||||||
|
:align-items :center}
|
||||||
|
(if (not @complete?)
|
||||||
|
[quo/slide-button
|
||||||
|
{:track-text "We gotta slide"
|
||||||
|
:track-icon :face-id
|
||||||
|
:customization-color @color
|
||||||
|
:size @size
|
||||||
|
:disabled? @disabled?
|
||||||
|
:on-complete (fn []
|
||||||
|
(js/setTimeout (fn [] (reset! complete? true))
|
||||||
|
1000)
|
||||||
|
(js/alert "I don't wanna slide anymore"))}]
|
||||||
|
[quo/button {:on-press (fn [] (reset! complete? false))}
|
||||||
|
"Try again"])]]])))
|
||||||
|
|
||||||
|
(defn preview-slide-button
|
||||||
|
[]
|
||||||
|
[rn/view
|
||||||
|
{:background-color (colors/theme-colors colors/white colors/neutral-90)
|
||||||
|
:flex 1}
|
||||||
|
[rn/flat-list
|
||||||
|
{:flex 1
|
||||||
|
:keyboard-should-persist-taps :always
|
||||||
|
:header [cool-preview]
|
||||||
|
:key-fn str}]])
|
|
@ -17,6 +17,7 @@
|
||||||
[status-im2.contexts.quo-preview.avatars.wallet-user-avatar :as wallet-user-avatar]
|
[status-im2.contexts.quo-preview.avatars.wallet-user-avatar :as wallet-user-avatar]
|
||||||
[status-im2.contexts.quo-preview.banners.banner :as banner]
|
[status-im2.contexts.quo-preview.banners.banner :as banner]
|
||||||
[status-im2.contexts.quo-preview.buttons.button :as button]
|
[status-im2.contexts.quo-preview.buttons.button :as button]
|
||||||
|
[status-im2.contexts.quo-preview.buttons.slide-button :as slide-button]
|
||||||
[status-im2.contexts.quo-preview.buttons.dynamic-button :as dynamic-button]
|
[status-im2.contexts.quo-preview.buttons.dynamic-button :as dynamic-button]
|
||||||
[status-im2.contexts.quo-preview.buttons.predictive-keyboard :as predictive-keyboard]
|
[status-im2.contexts.quo-preview.buttons.predictive-keyboard :as predictive-keyboard]
|
||||||
[status-im2.contexts.quo-preview.code.snippet :as code-snippet]
|
[status-im2.contexts.quo-preview.code.snippet :as code-snippet]
|
||||||
|
@ -130,6 +131,9 @@
|
||||||
{:name :dynamic-button
|
{:name :dynamic-button
|
||||||
:options {:topBar {:visible true}}
|
:options {:topBar {:visible true}}
|
||||||
:component dynamic-button/preview-dynamic-button}
|
:component dynamic-button/preview-dynamic-button}
|
||||||
|
{:name :slide-button
|
||||||
|
:options {:topBar {:visible true}}
|
||||||
|
:component slide-button/preview-slide-button}
|
||||||
{:name :predictive-keyboard
|
{:name :predictive-keyboard
|
||||||
:options {:topBar {:visible true}}
|
:options {:topBar {:visible true}}
|
||||||
:component predictive-keyboard/preview-predictive-keyboard}]
|
:component predictive-keyboard/preview-predictive-keyboard}]
|
||||||
|
|
|
@ -211,3 +211,7 @@
|
||||||
(defn was-not-called
|
(defn was-not-called
|
||||||
[mock]
|
[mock]
|
||||||
(was-called-times mock 0))
|
(was-called-times mock 0))
|
||||||
|
|
||||||
|
(defn has-style
|
||||||
|
[mock styles]
|
||||||
|
(.toHaveStyle (js/expect mock) (clj->js styles)))
|
||||||
|
|
Loading…
Reference in New Issue