Add animated list item into Quo
Change press animation ns Add list item component Fix pressable does not reset animation state Improve long-press handling Signed-off-by: Gheorghe Pinzaru <feross95@gmail.com>
This commit is contained in:
parent
2f65cedd2d
commit
d28ae1212d
|
@ -1,4 +1,4 @@
|
|||
(ns quo.components.button.animation
|
||||
(ns quo.components.animated.pressable
|
||||
(:require [quo.animated :as animated]
|
||||
[quo.gesture-handler :as gesture-handler]))
|
||||
|
||||
|
@ -50,8 +50,8 @@
|
|||
active (animated/eq state (:began gesture-handler/states))
|
||||
gesture-handler (animated/on-gesture {:state state})
|
||||
duration (animated/cond* active time-in time-out)
|
||||
long-duration (animated/cond* active long-press-duration 0)
|
||||
long-pressed (animated/value 0)
|
||||
long-duration (animated/cond* active long-press-duration 0)
|
||||
long-timing (animated/with-timing-transition active
|
||||
{:duration long-duration})
|
||||
animation (animated/with-timing-transition active
|
||||
|
@ -70,9 +70,15 @@
|
|||
handle-press-start (fn [] (when on-press-start (on-press-start)))
|
||||
handle-long-press (fn [] (when on-long-press (on-long-press)))]
|
||||
[:<>
|
||||
[animated/code
|
||||
{:exec (animated/cond* (animated/eq long-timing 1)
|
||||
(animated/set long-pressed 1))}]
|
||||
(when on-long-press
|
||||
[animated/code
|
||||
{:exec (animated/block
|
||||
[(animated/cond* (animated/eq long-timing 1)
|
||||
(animated/set long-pressed 1))
|
||||
(animated/cond* long-pressed
|
||||
[(animated/set long-pressed 0)
|
||||
(animated/call* [] handle-long-press)
|
||||
(animated/set state (:undetermined gesture-handler/states))])])}])
|
||||
[animated/code
|
||||
{:key (str on-press on-long-press on-press-start)
|
||||
:exec (animated/on-change state
|
||||
|
@ -80,11 +86,8 @@
|
|||
(animated/call* [] handle-press-start))
|
||||
(animated/cond* (animated/and* (animated/eq state (:end gesture-handler/states))
|
||||
(animated/not* long-pressed))
|
||||
(animated/call* [] handle-press))
|
||||
(animated/cond* (animated/and* (animated/eq state (:end gesture-handler/states))
|
||||
long-pressed)
|
||||
[(animated/set long-pressed 0)
|
||||
(animated/call* [] handle-long-press)])])}]
|
||||
[(animated/call* [] handle-press)
|
||||
(animated/set state (:undetermined gesture-handler/states))])])}]
|
||||
[gesture-handler/tap-gesture-handler
|
||||
(merge gesture-handler
|
||||
{:shouldCancelWhenOutside true
|
|
@ -1,5 +1,5 @@
|
|||
(ns quo.components.button.view
|
||||
(:require [quo.components.button.animation :as animation]
|
||||
(:require [quo.components.animated.pressable :as animation]
|
||||
[quo.react-native :as rn]
|
||||
[quo.haptic :as haptic]
|
||||
[quo.design-system.colors :as colors]
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
(ns quo.components.list.footer
|
||||
(:require [quo.react-native :as rn]
|
||||
[quo.design-system.spacing :as spacing]
|
||||
[quo.components.text :as text]))
|
||||
|
||||
(defn footer [& children]
|
||||
[rn/view {:style (merge (:base spacing/padding-horizontal)
|
||||
(:small spacing/padding-vertical))}
|
||||
(into [text/text {:color :secondary}]
|
||||
children)])
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
(ns quo.components.list.header
|
||||
(:require [quo.react-native :as rn]
|
||||
[quo.design-system.spacing :as spacing]
|
||||
[quo.components.text :as text]))
|
||||
|
||||
(defn header [& children]
|
||||
[rn/view {:style (merge (:base spacing/padding-horizontal)
|
||||
(:x-tiny spacing/padding-vertical))}
|
||||
(into [text/text {:color :secondary
|
||||
:style {:margin-top 10}}]
|
||||
children)])
|
|
@ -0,0 +1,163 @@
|
|||
(ns quo.components.list.item
|
||||
(:require [quo.react-native :as rn]
|
||||
[quo.platform :as platform]
|
||||
[quo.design-system.spacing :as spacing]
|
||||
[quo.design-system.colors :as colors]
|
||||
[quo.components.text :as text]
|
||||
;; FIXME:
|
||||
[status-im.ui.components.radio :as radio]
|
||||
[status-im.ui.components.checkbox.view :as checkbox]
|
||||
[status-im.ui.components.icons.vector-icons :as icons]
|
||||
[quo.components.animated.pressable :as animated]))
|
||||
|
||||
(defn themes [theme]
|
||||
(case theme
|
||||
:main {:icon-color (:icon-04 @colors/theme)
|
||||
:icon-bg-color (:interactive-02 @colors/theme)
|
||||
:active-background (:interactive-02 @colors/theme)
|
||||
:passive-background (:ui-background @colors/theme)
|
||||
:text-color (:text-01 @colors/theme)}
|
||||
:accent {:icon-color (:icon-04 @colors/theme)
|
||||
:icon-bg-color (:interactive-02 @colors/theme)
|
||||
:active-background (:interactive-02 @colors/theme)
|
||||
:passive-background (:ui-background @colors/theme)
|
||||
:text-color (:text-04 @colors/theme)}
|
||||
:negative {:icon-color (:negative-01 @colors/theme)
|
||||
:icon-bg-color (:negative-02 @colors/theme)
|
||||
:active-background (:negative-02 @colors/theme)
|
||||
:passive-background (:ui-background @colors/theme)
|
||||
:text-color (:negative-01 @colors/theme)}
|
||||
:positive {:icon-color (:positive-01 @colors/theme)
|
||||
:icon-bg-color (:positive-02 @colors/theme)
|
||||
:active-background (:positive-02 @colors/theme)
|
||||
:passive-background (:ui-background @colors/theme)
|
||||
:text-color (:positive-01 @colors/theme)}
|
||||
:disabled {:icon-color (:icon-02 @colors/theme)
|
||||
:icon-bg-color (:ui-01 @colors/theme)
|
||||
:active-background (:ui-01 @colors/theme)
|
||||
:passive-background (:ui-background @colors/theme)
|
||||
:text-color (:text-02 @colors/theme)}))
|
||||
|
||||
(defn size->icon-size [size]
|
||||
(case size
|
||||
:small 36
|
||||
40))
|
||||
|
||||
(defn size->container-size [size]
|
||||
(case size
|
||||
:small 52
|
||||
64))
|
||||
|
||||
(defn container [{:keys [size]} & children]
|
||||
(into [rn/view {:style (merge (:tiny spacing/padding-horizontal)
|
||||
{:min-height (size->container-size size)
|
||||
:padding-vertical 8
|
||||
:flex-direction :row
|
||||
:flex 1
|
||||
:align-items :center
|
||||
:justify-content :space-between})}]
|
||||
children))
|
||||
|
||||
(defn- icon-column [{:keys [icon icon-bg-color icon-color size]}]
|
||||
(when icon
|
||||
(let [icon-size (if (= size :default)
|
||||
40
|
||||
36)]
|
||||
[rn/view {:style (:tiny spacing/padding-horizontal)}
|
||||
(cond
|
||||
(vector? icon)
|
||||
icon
|
||||
|
||||
(keyword? icon)
|
||||
[rn/view {:style {:width icon-size
|
||||
:height icon-size
|
||||
:align-items :center
|
||||
:justify-content :center
|
||||
:border-radius (/ icon-size 2)
|
||||
:background-color icon-bg-color}}
|
||||
[icons/icon icon {:color icon-color}]])])))
|
||||
|
||||
(defn title-column [{:keys [title text-color subtitle subtitle-max-lines]}]
|
||||
[rn/view {:style (merge (:tiny spacing/padding-horizontal)
|
||||
{:justify-content :center})}
|
||||
(cond
|
||||
|
||||
(and title subtitle)
|
||||
[:<>
|
||||
[text/text {:weight :medium
|
||||
:style {:color text-color}
|
||||
:ellipsize-mode :tail
|
||||
:number-of-lines 1}
|
||||
title]
|
||||
[text/text {:weight :regular
|
||||
:color :secondary
|
||||
:ellipsize-mode :tail
|
||||
:number-of-lines subtitle-max-lines}
|
||||
subtitle]]
|
||||
|
||||
title [text/text {:number-of-lines 1
|
||||
:style {:color text-color}
|
||||
:ellipsize-mode :tail
|
||||
:size :large}
|
||||
title])])
|
||||
|
||||
(defn left-side [props]
|
||||
[rn/view {:style {:flex-direction :row
|
||||
:flex 1
|
||||
:align-items :center}}
|
||||
[icon-column props]
|
||||
[title-column props]])
|
||||
|
||||
(defn right-side [{:keys [chevron on-press active accessory accessory-text]}]
|
||||
[rn/view {:style {:align-items :center
|
||||
:flex-direction :row}}
|
||||
[rn/view {:style (:tiny spacing/padding-horizontal)}
|
||||
(case accessory
|
||||
:radio [radio/radio active]
|
||||
:checkbox [checkbox/checkbox {:checked? active}]
|
||||
:switch [rn/switch {:value active
|
||||
:track-color #js {:true (:interactive-01 @colors/theme)
|
||||
:false nil}
|
||||
:on-value-change on-press}]
|
||||
:text [text/text {:color :secondary
|
||||
:number-of-lines 1}
|
||||
accessory-text]
|
||||
nil)]
|
||||
(when (and chevron platform/ios?)
|
||||
[rn/view {:style {:padding-right (:tiny spacing/spacing)}}
|
||||
[icons/icon :main-icons/next {:container-style {:opacity 0.4
|
||||
:align-items :center
|
||||
:justify-content :center}
|
||||
:resize-mode :center
|
||||
:color (:icon-02 @colors/theme)}]])])
|
||||
|
||||
(defn list-item
|
||||
[{:keys [theme accessory disabled subtitle-max-lines icon title
|
||||
subtitle active on-press on-long-press chevron size
|
||||
accessory-text]
|
||||
:or {subtitle-max-lines 1
|
||||
theme :main}}]
|
||||
(let [theme (if disabled :disabled theme)
|
||||
{:keys [icon-color text-color icon-bg-color
|
||||
active-background passive-background]}
|
||||
(themes theme)]
|
||||
[rn/view {:background-color (if active active-background passive-background)}
|
||||
[animated/pressable {:type :list-item
|
||||
:disabled disabled
|
||||
:background-color active-background
|
||||
:on-press on-press
|
||||
:on-long-press on-long-press}
|
||||
[container {:size size}
|
||||
[left-side {:icon-color icon-color
|
||||
:text-color text-color
|
||||
:icon-bg-color icon-bg-color
|
||||
:icon icon
|
||||
:title title
|
||||
:size size
|
||||
:subtitle subtitle
|
||||
:subtitle-max-lines subtitle-max-lines}]
|
||||
[right-side {:chevron chevron
|
||||
:active active
|
||||
:on-press on-press
|
||||
:accessory-text accessory-text
|
||||
:accessory accessory}]]]]))
|
|
@ -5,7 +5,10 @@
|
|||
[quo.components.text-input :as text-input]
|
||||
[quo.components.tooltip :as tooltip]
|
||||
[quo.components.text :as text]
|
||||
[quo.components.button.view :as button]))
|
||||
[quo.components.button.view :as button]
|
||||
[quo.components.list.header :as list-header]
|
||||
[quo.components.list.footer :as list-footer]
|
||||
[quo.components.list.item :as list-item]))
|
||||
|
||||
(def text text/text)
|
||||
(def header header/header)
|
||||
|
@ -13,6 +16,9 @@
|
|||
(def text-input text-input/text-input)
|
||||
(def tooltip tooltip/tooltip)
|
||||
(def button button/button)
|
||||
(def list-header list-header/header)
|
||||
(def list-footer list-footer/footer)
|
||||
(def list-item list-item/list-item)
|
||||
(def safe-area-provider safe-area/provider)
|
||||
(def safe-area-consumer safe-area/consumer)
|
||||
(def safe-area-view safe-area/view)
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
(ns quo.previews.lists
|
||||
(:require [reagent.core :as reagent]
|
||||
[quo.core :as quo]
|
||||
[quo.react-native :as rn]
|
||||
[quo.design-system.colors :as colors]
|
||||
[quo.previews.preview :as preview]))
|
||||
|
||||
(def all-props (preview/list-comp [] {}))
|
||||
|
||||
(defn avatar []
|
||||
[rn/view {:border-radius 20
|
||||
:width 40
|
||||
:height 40
|
||||
:justify-content :center
|
||||
:align-items :center
|
||||
:background-color :red}
|
||||
[quo/text {:weight :bold
|
||||
:size :large}
|
||||
"T"]])
|
||||
|
||||
(defn icon-element [type]
|
||||
(case type
|
||||
:icon :main-icons/add-contact
|
||||
:component [avatar]
|
||||
nil))
|
||||
|
||||
(def descriptor [{:label "Accessory:"
|
||||
:key :accessory
|
||||
:type :select
|
||||
:options [{:key :radio
|
||||
:value "Radio"}
|
||||
{:key :checkbox
|
||||
:value "Checkbox"}
|
||||
{:key :switch
|
||||
:value "Switch"}
|
||||
{:key :text
|
||||
:value "Text"}
|
||||
{:key :default
|
||||
:value "Default"}]}
|
||||
{:label "Size:"
|
||||
:key :size
|
||||
:type :select
|
||||
:options [{:key :small
|
||||
:value "Small"}
|
||||
{:key :default
|
||||
:value "Default"}]}
|
||||
{:label "Icon:"
|
||||
:key :icon
|
||||
:type :select
|
||||
:options [{:key :icon
|
||||
:value "Icon"}
|
||||
{:key :component
|
||||
:value "Component"}]}
|
||||
{:label "Theme:"
|
||||
:key :theme
|
||||
:type :select
|
||||
:options [{:key :main
|
||||
:value "Main"}
|
||||
{:key :accent
|
||||
:value "Accent"}
|
||||
{:key :negative
|
||||
:value "Negative"}
|
||||
{:key :positive
|
||||
:value "Positive"}]}
|
||||
{:label "Selectable"
|
||||
:key :selectable
|
||||
:type :boolean}
|
||||
{:label "Chevron"
|
||||
:key :chevron
|
||||
:type :boolean}
|
||||
{:label "Disabled:"
|
||||
:key :disabled
|
||||
:type :boolean}
|
||||
{:label "Title"
|
||||
:key :title
|
||||
:type :text}
|
||||
{:label "Subtitle"
|
||||
:key :subtitle
|
||||
:type :text}])
|
||||
|
||||
(defn render-item [_]
|
||||
[rn/view {:style {:padding-vertical 24}}])
|
||||
|
||||
(defn cool-preview []
|
||||
(let [state (reagent/atom {:title "Title"
|
||||
:active false})
|
||||
icon (reagent/cursor state [:icon])
|
||||
active (reagent/cursor state [:active])
|
||||
selectable (reagent/cursor state [:selectable])]
|
||||
(fn []
|
||||
[rn/view {:margin-bottom 50}
|
||||
[rn/view {:padding-horizontal 16}
|
||||
[preview/customizer state descriptor]]
|
||||
[rn/view {:padding-vertical 16}
|
||||
[quo/list-item (merge (dissoc @state :active :selectable)
|
||||
(when @selectable
|
||||
{:active @active
|
||||
:on-press #(swap! active not)})
|
||||
{:accessory-text "Accessory"
|
||||
:icon (icon-element @icon)})]]])))
|
||||
|
||||
(defn preview []
|
||||
[rn/view {:background-color (:ui-background @colors/theme)
|
||||
:flex 1}
|
||||
[rn/flat-list {:flex 1
|
||||
:keyboardShouldPersistTaps :always
|
||||
:header [cool-preview]
|
||||
:data all-props
|
||||
:render-fn render-item
|
||||
:key-fn str}]])
|
|
@ -4,6 +4,7 @@
|
|||
[quo.previews.text-input :as text-input]
|
||||
[quo.previews.tooltip :as tooltip]
|
||||
[quo.previews.button :as button]
|
||||
[quo.previews.lists :as lists]
|
||||
[quo.react-native :as rn]
|
||||
[quo.core :as quo]
|
||||
[reagent.core :as reagent]
|
||||
|
@ -23,9 +24,12 @@
|
|||
{:name :headers
|
||||
:insets {:top false}
|
||||
:component header/preview-header}
|
||||
{:name :button
|
||||
:insets {:top false}
|
||||
:component button/preview-button}])
|
||||
{:name :button
|
||||
:insets {:top false}
|
||||
:component button/preview-button}
|
||||
{:name :lists
|
||||
:instes {:top false}
|
||||
:component lists/preview}])
|
||||
|
||||
(defn theme-switcher []
|
||||
[rn/view {:style {:flex-direction :row
|
||||
|
|
Loading…
Reference in New Issue