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]
|
(:require [quo.animated :as animated]
|
||||||
[quo.gesture-handler :as gesture-handler]))
|
[quo.gesture-handler :as gesture-handler]))
|
||||||
|
|
||||||
|
@ -50,8 +50,8 @@
|
||||||
active (animated/eq state (:began gesture-handler/states))
|
active (animated/eq state (:began gesture-handler/states))
|
||||||
gesture-handler (animated/on-gesture {:state state})
|
gesture-handler (animated/on-gesture {:state state})
|
||||||
duration (animated/cond* active time-in time-out)
|
duration (animated/cond* active time-in time-out)
|
||||||
long-duration (animated/cond* active long-press-duration 0)
|
|
||||||
long-pressed (animated/value 0)
|
long-pressed (animated/value 0)
|
||||||
|
long-duration (animated/cond* active long-press-duration 0)
|
||||||
long-timing (animated/with-timing-transition active
|
long-timing (animated/with-timing-transition active
|
||||||
{:duration long-duration})
|
{:duration long-duration})
|
||||||
animation (animated/with-timing-transition active
|
animation (animated/with-timing-transition active
|
||||||
|
@ -70,9 +70,15 @@
|
||||||
handle-press-start (fn [] (when on-press-start (on-press-start)))
|
handle-press-start (fn [] (when on-press-start (on-press-start)))
|
||||||
handle-long-press (fn [] (when on-long-press (on-long-press)))]
|
handle-long-press (fn [] (when on-long-press (on-long-press)))]
|
||||||
[:<>
|
[:<>
|
||||||
|
(when on-long-press
|
||||||
[animated/code
|
[animated/code
|
||||||
{:exec (animated/cond* (animated/eq long-timing 1)
|
{:exec (animated/block
|
||||||
(animated/set long-pressed 1))}]
|
[(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
|
[animated/code
|
||||||
{:key (str on-press on-long-press on-press-start)
|
{:key (str on-press on-long-press on-press-start)
|
||||||
:exec (animated/on-change state
|
:exec (animated/on-change state
|
||||||
|
@ -80,11 +86,8 @@
|
||||||
(animated/call* [] handle-press-start))
|
(animated/call* [] handle-press-start))
|
||||||
(animated/cond* (animated/and* (animated/eq state (:end gesture-handler/states))
|
(animated/cond* (animated/and* (animated/eq state (:end gesture-handler/states))
|
||||||
(animated/not* long-pressed))
|
(animated/not* long-pressed))
|
||||||
(animated/call* [] handle-press))
|
[(animated/call* [] handle-press)
|
||||||
(animated/cond* (animated/and* (animated/eq state (:end gesture-handler/states))
|
(animated/set state (:undetermined gesture-handler/states))])])}]
|
||||||
long-pressed)
|
|
||||||
[(animated/set long-pressed 0)
|
|
||||||
(animated/call* [] handle-long-press)])])}]
|
|
||||||
[gesture-handler/tap-gesture-handler
|
[gesture-handler/tap-gesture-handler
|
||||||
(merge gesture-handler
|
(merge gesture-handler
|
||||||
{:shouldCancelWhenOutside true
|
{:shouldCancelWhenOutside true
|
|
@ -1,5 +1,5 @@
|
||||||
(ns quo.components.button.view
|
(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.react-native :as rn]
|
||||||
[quo.haptic :as haptic]
|
[quo.haptic :as haptic]
|
||||||
[quo.design-system.colors :as colors]
|
[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.text-input :as text-input]
|
||||||
[quo.components.tooltip :as tooltip]
|
[quo.components.tooltip :as tooltip]
|
||||||
[quo.components.text :as text]
|
[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 text text/text)
|
||||||
(def header header/header)
|
(def header header/header)
|
||||||
|
@ -13,6 +16,9 @@
|
||||||
(def text-input text-input/text-input)
|
(def text-input text-input/text-input)
|
||||||
(def tooltip tooltip/tooltip)
|
(def tooltip tooltip/tooltip)
|
||||||
(def button button/button)
|
(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-provider safe-area/provider)
|
||||||
(def safe-area-consumer safe-area/consumer)
|
(def safe-area-consumer safe-area/consumer)
|
||||||
(def safe-area-view safe-area/view)
|
(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.text-input :as text-input]
|
||||||
[quo.previews.tooltip :as tooltip]
|
[quo.previews.tooltip :as tooltip]
|
||||||
[quo.previews.button :as button]
|
[quo.previews.button :as button]
|
||||||
|
[quo.previews.lists :as lists]
|
||||||
[quo.react-native :as rn]
|
[quo.react-native :as rn]
|
||||||
[quo.core :as quo]
|
[quo.core :as quo]
|
||||||
[reagent.core :as reagent]
|
[reagent.core :as reagent]
|
||||||
|
@ -25,7 +26,10 @@
|
||||||
:component header/preview-header}
|
:component header/preview-header}
|
||||||
{:name :button
|
{:name :button
|
||||||
:insets {:top false}
|
:insets {:top false}
|
||||||
:component button/preview-button}])
|
:component button/preview-button}
|
||||||
|
{:name :lists
|
||||||
|
:instes {:top false}
|
||||||
|
:component lists/preview}])
|
||||||
|
|
||||||
(defn theme-switcher []
|
(defn theme-switcher []
|
||||||
[rn/view {:style {:flex-direction :row
|
[rn/view {:style {:flex-direction :row
|
||||||
|
|
Loading…
Reference in New Issue