diff --git a/src/quo/animated.cljs b/src/quo/animated.cljs index 7dc4ee764f..751ce65661 100644 --- a/src/quo/animated.cljs +++ b/src/quo/animated.cljs @@ -51,8 +51,9 @@ (def bezier (.-bezier ^js Easing)) (def linear (.-linear ^js Easing)) -(def easings {:ease-in (bezier 0.42 0 1 1) - :ease-out (bezier 0 0 0.58 1)}) +(def easings {:ease-in (bezier 0.42 0 1 1) + :ease-out (bezier 0 0 0.58 1) + :ease-in-out (bezier 0.42 0 0.58 1)}) (defn set-value [anim val] (ocall anim "setValue" val)) @@ -143,6 +144,12 @@ (defn with-timing-transition [val config] (.withTimingTransition ^js redash val (clj->js config))) +(defn use-spring-transition [val config] + (.withSpringTransition ^js redash val (clj->js config))) + +(defn use-timing-transition [val config] + (.withTimingTransition ^js redash val (clj->js config))) + (defn re-timing [config] (.timing ^js redash (clj->js config))) @@ -160,6 +167,9 @@ (defn mix [anim-value a b] (.mix ^js redash anim-value a b)) +(defn mix-color [anim-value a b] + (.mixColor ^js redash anim-value a b)) + (defn loop* [opts] (ocall redash "loop" (clj->js opts))) diff --git a/src/quo/components/controls/styles.cljs b/src/quo/components/controls/styles.cljs new file mode 100644 index 0000000000..04a4a0dbfe --- /dev/null +++ b/src/quo/components/controls/styles.cljs @@ -0,0 +1,61 @@ +(ns quo.components.controls.styles + (:require [quo.animated :as animated] + [quo.design-system.colors :as colors])) + +(defn switch-style [state] + {:width 52 + :height 28 + :border-radius 14 + :padding 4 + :background-color (animated/mix-color state + (:ui-01 @colors/theme) + (:interactive-01 @colors/theme))}) + +(defn switch-bullet-style [state hold] + {:width 20 + :height 20 + :border-radius 10 + :opacity (animated/mix hold 1 0.6) + :transform [{:translateX (animated/mix state 0 24)}] + :background-color colors/white + :elevation 4 + :shadow-opacity 1 + :shadow-radius 16 + :shadow-color (:shadow-01 @colors/theme) + :shadow-offset {:width 0 :height 4}}) + +(defn radio-style [state] + {:width 20 + :height 20 + :border-radius 10 + :padding 4 + :background-color (animated/mix-color state + (:ui-01 @colors/theme) + (:interactive-01 @colors/theme))}) + +(defn radio-bullet-style [state hold] + {:width 12 + :height 12 + :border-radius 6 + :opacity (animated/mix hold 1 0.6) + :transform [{:scale (animated/mix state 0.0001 1)}] + :background-color colors/white + :elevation 4 + :shadow-opacity 1 + :shadow-radius 16 + :shadow-color (:shadow-01 @colors/theme) + :shadow-offset {:width 0 :height 4}}) + +(defn checkbox-style [state] + {:width 18 + :height 18 + :border-radius 4 + :justify-content :center + :align-items :center + :background-color (animated/mix-color state + (:ui-01 @colors/theme) + (:interactive-01 @colors/theme))}) + +(defn check-icon-style [state hold] + {:opacity (animated/mix hold 1 0.6) + :transform [{:scale (animated/mix state 0.0001 1)}]}) diff --git a/src/quo/components/controls/view.cljs b/src/quo/components/controls/view.cljs new file mode 100644 index 0000000000..cfa6ed67ec --- /dev/null +++ b/src/quo/components/controls/view.cljs @@ -0,0 +1,71 @@ +(ns quo.components.controls.view + (:require [reagent.core :as reagent] + [cljs-bean.core :as bean] + [quo.react :as react] + [quo.animated :as animated] + [quo.gesture-handler :as gh] + [quo.design-system.colors :as colors] + [quo.components.controls.styles :as styles] + [status-im.ui.components.icons.vector-icons :as icons])) + +(def spring-config {:damping 50 + :mass 0.3 + :stiffness 120 + :overshootClamping true + :bouncyFactor 1}) + +(defn control-builder [component] + (fn [props] + (let [{:keys [value onChange disabled]} + (bean/bean props) + state (animated/use-value 0) + tap-state (animated/use-value (:undetermined gh/states)) + tap-handler (animated/on-gesture {:state tap-state}) + hold (react/use-memo + (fn [] + (animated/with-timing-transition + (animated/eq tap-state (:began gh/states)) + {})) + []) + transition (react/use-memo + (fn [] + (animated/with-spring-transition state spring-config)) + []) + press-end (fn [] + (when (and (not disabled) onChange) + (onChange (not value))))] + (animated/code! + (fn [] + (animated/cond* (animated/eq tap-state (:end gh/states)) + [(animated/set state (animated/not* state)) + (animated/set tap-state (:undetermined gh/states)) + (animated/call* [] press-end)])) + [press-end]) + (animated/code! + (fn [] + (animated/set state (if (true? value) 1 0))) + [value]) + (reagent/as-element + [gh/tap-gesture-handler (merge tap-handler + {:shouldCancelWhenOutside true + :enabled (not disabled)}) + [animated/view + [component {:transition transition + :hold hold}]]])))) + +(defn switch-view [{:keys [transition hold]}] + [animated/view {:style (styles/switch-style transition)} + [animated/view {:style (styles/switch-bullet-style transition hold)}]]) + +(defn radio-view [{:keys [transition hold]}] + [animated/view {:style (styles/radio-style transition)} + [animated/view {:style (styles/radio-bullet-style transition hold)}]]) + +(defn checkbox-view [{:keys [transition hold]}] + [animated/view {:style (styles/checkbox-style transition)} + [animated/view {:style (styles/check-icon-style transition hold)} + [icons/tiny-icon :tiny-icons/tiny-check {:color colors/white}]]]) + +(def switch (reagent/adapt-react-class (control-builder switch-view))) +(def radio (reagent/adapt-react-class (control-builder radio-view))) +(def checkbox (reagent/adapt-react-class (control-builder checkbox-view))) diff --git a/src/quo/components/list/item.cljs b/src/quo/components/list/item.cljs index 11ea86de3c..b6e562bd20 100644 --- a/src/quo/components/list/item.cljs +++ b/src/quo/components/list/item.cljs @@ -4,9 +4,8 @@ [quo.design-system.spacing :as spacing] [quo.design-system.colors :as colors] [quo.components.text :as text] + [quo.components.controls.view :as controls] ;; 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])) @@ -113,12 +112,12 @@ :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}] + :radio [controls/radio {:value active + :on-change on-press}] + :checkbox [controls/checkbox {:value active + :on-change on-press}] + :switch [controls/switch {:value active + :on-change on-press}] :text [text/text {:color :secondary :number-of-lines 1} accessory-text] diff --git a/src/quo/core.cljs b/src/quo/core.cljs index 3c5ae9039e..158f8307cd 100644 --- a/src/quo/core.cljs +++ b/src/quo/core.cljs @@ -9,6 +9,7 @@ [quo.components.list.header :as list-header] [quo.components.list.footer :as list-footer] [quo.components.list.item :as list-item] + [quo.components.controls.view :as controls] [quo.components.bottom-sheet.view :as bottom-sheet])) (def text text/text) @@ -21,6 +22,9 @@ (def list-footer list-footer/footer) (def list-item list-item/list-item) (def bottom-sheet bottom-sheet/bottom-sheet) +(def switch controls/switch) +(def radio controls/radio) +(def checkbox controls/checkbox) (def safe-area-provider safe-area/provider) (def safe-area-consumer safe-area/consumer) (def safe-area-view safe-area/view) diff --git a/src/quo/previews/controls.cljs b/src/quo/previews/controls.cljs new file mode 100644 index 0000000000..a2e6999668 --- /dev/null +++ b/src/quo/previews/controls.cljs @@ -0,0 +1,44 @@ +(ns quo.previews.controls + (:require [reagent.core :as reagent] + [quo.core :as quo] + [quo.react-native :as rn] + [quo.design-system.colors :as colors])) + +(defn preview [] + (let [switch-state (reagent/atom true) + radio-state (reagent/atom true) + checkbox-state (reagent/atom true)] + (fn [] + [rn/view {:background-color (:ui-background @colors/theme) + :flex 1} + [rn/view {:padding 20 + :flex-direction :row + :align-items :center + :justify-content :space-between} + [rn/touchable-opacity {:style {:margin-vertical 10 + :padding 10} + :on-press #(swap! switch-state not)} + [quo/text (str "Switch state: " @switch-state)]] + [quo/switch {:value @switch-state + :on-change #(reset! switch-state %)}]] + + [rn/view {:padding 20 + :flex-direction :row + :align-items :center + :justify-content :space-between} + [rn/touchable-opacity {:style {:margin-vertical 10 + :padding 10} + :on-press #(swap! radio-state not)} + [quo/text (str "Radio state: " @radio-state)]] + [quo/radio {:value @radio-state + :on-change #(reset! radio-state %)}]] + [rn/view {:padding 20 + :flex-direction :row + :align-items :center + :justify-content :space-between} + [rn/touchable-opacity {:style {:margin-vertical 10 + :padding 10} + :on-press #(swap! checkbox-state not)} + [quo/text (str "Checkbox state: " @checkbox-state)]] + [quo/checkbox {:value @checkbox-state + :on-change #(reset! checkbox-state %)}]]]))) diff --git a/src/quo/previews/main.cljs b/src/quo/previews/main.cljs index 704c18e6ea..0badbdefb0 100644 --- a/src/quo/previews/main.cljs +++ b/src/quo/previews/main.cljs @@ -6,6 +6,7 @@ [quo.previews.button :as button] [quo.previews.lists :as lists] [quo.previews.bottom-sheet :as bottom-sheet] + [quo.previews.controls :as controls] [quo.react-native :as rn] [quo.core :as quo] [reagent.core :as reagent] @@ -33,7 +34,10 @@ :component lists/preview} {:name :bottom-sheet :insets {:top false} - :component bottom-sheet/preview}]) + :component bottom-sheet/preview} + {:name :controls + :insets {:top false} + :component controls/preview}]) (defn theme-switcher [] [rn/view {:style {:flex-direction :row diff --git a/src/quo/react.cljs b/src/quo/react.cljs index cee0cc3795..caf98b52af 100644 --- a/src/quo/react.cljs +++ b/src/quo/react.cljs @@ -112,13 +112,15 @@ (react/useCallback f (maybe-js-deps @prev-deps*)) deps))) -(defn memo +(defn use-memo ([f] (react/useMemo f)) ([f deps] (with-deps-check [prev-deps*] (react/useMemo f (maybe-js-deps @prev-deps*)) deps))) +(def memo react/memo) + (defn get-children [^js children] (->> children (react/Children.toArray) diff --git a/src/quo/react_native.cljs b/src/quo/react_native.cljs index e6db4f319d..669a3815d0 100644 --- a/src/quo/react_native.cljs +++ b/src/quo/react_native.cljs @@ -32,8 +32,6 @@ :linear (-> ^js layout-animation .-Presets .-linear) :spring (-> ^js layout-animation .-Presets .-spring)}) -(def switch (reagent/adapt-react-class (.-Switch ^js rn))) - (def activity-indicator-class (reagent/adapt-react-class (.-ActivityIndicator ^js rn))) (defn activity-indicator [props]