refactored carousel component
This commit is contained in:
parent
96957039c3
commit
d68e078130
|
@ -1,150 +0,0 @@
|
||||||
(ns syng-im.components.carousel
|
|
||||||
(:require [syng-im.components.react :refer [android?
|
|
||||||
view
|
|
||||||
scroll-view
|
|
||||||
touchable-without-feedback
|
|
||||||
text]]
|
|
||||||
[syng-im.utils.logging :as log]))
|
|
||||||
|
|
||||||
|
|
||||||
(defn page-width []
|
|
||||||
(.-width (.get (.. js/React -Dimensions) "window")))
|
|
||||||
|
|
||||||
(def defaults {:gap 10
|
|
||||||
:sneak 10
|
|
||||||
:pageWidth (- (page-width) 40)
|
|
||||||
:scrollThreshold 20})
|
|
||||||
|
|
||||||
|
|
||||||
(defn get-gap [data]
|
|
||||||
(get data :gap (:gap defaults)))
|
|
||||||
|
|
||||||
(defn get-sneak [data]
|
|
||||||
(get data :sneak (:sneak defaults)))
|
|
||||||
|
|
||||||
(defn get-page-width [data]
|
|
||||||
(get data :pageWidth (:pageWidth defaults)))
|
|
||||||
|
|
||||||
(defn get-scroll-threshold [data]
|
|
||||||
(get data :scrollThreshold (:scrollThreshold defaults)))
|
|
||||||
|
|
||||||
(defn calculate-gap [component props]
|
|
||||||
(let [prop-page-width (get-page-width props)
|
|
||||||
page-width (page-width)
|
|
||||||
sneak (get-sneak props)
|
|
||||||
gap (quot (- (- page-width (* 2 sneak)) prop-page-width) 2)]
|
|
||||||
(log/debug "calculate-gap: prop-page-width=" prop-page-width
|
|
||||||
"; page-width=" page-width"; sneak=" sneak "; gap=" gap)
|
|
||||||
(when (> prop-page-width page-width)
|
|
||||||
(log/warn "Invalid pageWidth"))
|
|
||||||
(reagent.core/set-state component {:gap gap})
|
|
||||||
))
|
|
||||||
|
|
||||||
(defn scroll-to [component x y]
|
|
||||||
(.scrollTo (.-scrollView component) (clj->js {:y y
|
|
||||||
:x x})))
|
|
||||||
|
|
||||||
(defn get-current-position [event]
|
|
||||||
(.-x (.-contentOffset (.-nativeEvent event))))
|
|
||||||
|
|
||||||
(defn on-scroll-end [event component starting-position]
|
|
||||||
(let [props (reagent.core/props component)
|
|
||||||
state (reagent.core/state component)
|
|
||||||
prop-page-width (get-page-width props)
|
|
||||||
sneak (get-sneak props)
|
|
||||||
scroll-threshold (get-scroll-threshold props)
|
|
||||||
gap (get-gap state)
|
|
||||||
page-offset (+ prop-page-width gap)
|
|
||||||
current-position (get-current-position event)
|
|
||||||
direction (cond
|
|
||||||
(> current-position (+ starting-position scroll-threshold)) 1
|
|
||||||
(< current-position (- starting-position scroll-threshold)) -1
|
|
||||||
:else 0)
|
|
||||||
current-page (+ (quot starting-position page-offset) direction)
|
|
||||||
]
|
|
||||||
(log/debug "on-scroll-end: prop-page-width=" prop-page-width
|
|
||||||
"; sneak=" sneak "; gap=" gap "; page-offset=" page-offset
|
|
||||||
"; starting position=" starting-position
|
|
||||||
"; current-position=" current-position
|
|
||||||
"; direction=" direction "; current-page=" current-page)
|
|
||||||
(scroll-to component (* current-page page-offset) 0)
|
|
||||||
(reagent.core/set-state component {:activePage current-page})
|
|
||||||
(when (:onPageChange props)
|
|
||||||
((:onPageChange props) current-page))))
|
|
||||||
|
|
||||||
(defn go-to-page [component position]
|
|
||||||
(let [props (reagent.core/props component)
|
|
||||||
state (reagent.core/state component)
|
|
||||||
props-page-width (get-page-width props)
|
|
||||||
gap (get-gap state)
|
|
||||||
page-position (* position (+ props-page-width gap))]
|
|
||||||
(log/debug "go-to-page: props-page-width=" props-page-width "; gap=" gap
|
|
||||||
"; page-position=" page-position)
|
|
||||||
(scroll-to component page-position 0)))
|
|
||||||
|
|
||||||
(defn component-will-mount [component new-args]
|
|
||||||
(let [props (reagent.core/props component)]
|
|
||||||
(log/debug "component-will-mount: component=" component "; new-args="new-args)
|
|
||||||
(calculate-gap component props)))
|
|
||||||
|
|
||||||
(defn component-did-mount [component]
|
|
||||||
(let [props (reagent.core/props component)
|
|
||||||
initial-page (.-initialPage props)]
|
|
||||||
(log/debug "component-did-mount: initial-page="initial-page)
|
|
||||||
(when (pos? initial-page)
|
|
||||||
(go-to-page component initial-page))))
|
|
||||||
|
|
||||||
(defn component-will-receive-props [component new-argv]
|
|
||||||
(log/debug "component-will-receive-props: component=" component
|
|
||||||
"; new-argv=" new-argv)
|
|
||||||
(calculate-gap component new-argv))
|
|
||||||
|
|
||||||
(defn get-pages [component data children]
|
|
||||||
(let [props-page-width (get-page-width data)
|
|
||||||
page-style (get data :pageStyle {})
|
|
||||||
gap (get-gap data)
|
|
||||||
margin (quot gap 2)]
|
|
||||||
(map-indexed (fn [index child]
|
|
||||||
(let [page-index index
|
|
||||||
touchable-data {:key index
|
|
||||||
:onPress (fn [event]
|
|
||||||
(go-to-page component page-index))}]
|
|
||||||
(log/debug "page " index " - " child)
|
|
||||||
[touchable-without-feedback touchable-data
|
|
||||||
[view {:style [{:width props-page-width
|
|
||||||
:justifyContent "center"
|
|
||||||
:marginLeft margin
|
|
||||||
:marginRight margin}
|
|
||||||
page-style]}
|
|
||||||
child]])) children)))
|
|
||||||
|
|
||||||
(defn reagent-render [data children]
|
|
||||||
(let [starting-position (atom 0)
|
|
||||||
component (reagent.core/current-component)
|
|
||||||
sneak (get-sneak data)
|
|
||||||
gap (get-gap data)
|
|
||||||
pages (get-pages component data children)]
|
|
||||||
(log/debug "reagent-render: ")
|
|
||||||
[view {:style {:flex 1}}
|
|
||||||
[scroll-view {:contentContainerStyle {:paddingLeft (+ sneak (quot gap 2))
|
|
||||||
:paddingRight (+ sneak (quot gap 2))}
|
|
||||||
:automaticallyAdjustContentInsets false
|
|
||||||
:bounces false
|
|
||||||
:decelerationRate 0.9
|
|
||||||
:horizontal true
|
|
||||||
:onScrollBeginDrag #(reset! starting-position (get-current-position %))
|
|
||||||
:onScrollEndDrag #(on-scroll-end % component @starting-position)
|
|
||||||
:showsHorizontalScrollIndicator false
|
|
||||||
:ref (fn [c] (set! (.-scrollView component) c))
|
|
||||||
}
|
|
||||||
pages]]
|
|
||||||
))
|
|
||||||
|
|
||||||
(defn carousel [data children]
|
|
||||||
(let [component-data {:component-did-mount component-did-mount
|
|
||||||
:component-will-mount component-will-mount
|
|
||||||
:component-will-receive-props component-will-receive-props
|
|
||||||
:display-name "carousel"
|
|
||||||
:reagent-render reagent-render}]
|
|
||||||
(log/debug "Creating carousel component: " data children)
|
|
||||||
(reagent.core/create-class component-data)))
|
|
|
@ -0,0 +1,178 @@
|
||||||
|
(ns syng-im.components.carousel
|
||||||
|
(:require [syng-im.components.react :refer [android?
|
||||||
|
view
|
||||||
|
scroll-view
|
||||||
|
touchable-without-feedback
|
||||||
|
text]]
|
||||||
|
[syng-im.components.carousel.styles :as st]
|
||||||
|
[syng-im.utils.logging :as log]))
|
||||||
|
|
||||||
|
|
||||||
|
(defn window-page-width []
|
||||||
|
(.-width (.get (.. js/React -Dimensions) "window")))
|
||||||
|
|
||||||
|
(def defaults {:gap 10
|
||||||
|
:sneak 10
|
||||||
|
:pageStyle {}
|
||||||
|
:scrollThreshold 20})
|
||||||
|
|
||||||
|
(defn get-active-page [data]
|
||||||
|
(get data :activePage 0))
|
||||||
|
|
||||||
|
(defn get-sneak [data]
|
||||||
|
(get data :sneak (:sneak defaults)))
|
||||||
|
|
||||||
|
(defn get-gap [data]
|
||||||
|
(get data :gap (:gap defaults)))
|
||||||
|
|
||||||
|
(defn compute-page-width
|
||||||
|
([gap sneak]
|
||||||
|
(compute-page-width (window-page-width) gap sneak))
|
||||||
|
([window-page-width gap sneak]
|
||||||
|
(- window-page-width (+ (* 2 gap) (* 2 sneak)))))
|
||||||
|
|
||||||
|
(defn get-page-width [data]
|
||||||
|
(get data :pageWidth (compute-page-width (get-gap data) (get-sneak data))))
|
||||||
|
|
||||||
|
(defn get-page-style [data]
|
||||||
|
(let [data-style (get data :pageStyle {})]
|
||||||
|
(merge (:pageStyle defaults) data-style)))
|
||||||
|
|
||||||
|
(defn get-scroll-threshold [data]
|
||||||
|
(get data :scrollThreshold (:scrollThreshold defaults)))
|
||||||
|
|
||||||
|
(defn apply-props [component props]
|
||||||
|
(let [sneak (get-sneak props)
|
||||||
|
page-width (get-page-width props)
|
||||||
|
style (get-page-style props)
|
||||||
|
gap (quot (- (- (window-page-width) (* 2 sneak)) page-width) 2)]
|
||||||
|
(reagent.core/set-state component {:sneak sneak
|
||||||
|
:pageWidth page-width
|
||||||
|
:pageStyle style
|
||||||
|
:gap gap})))
|
||||||
|
|
||||||
|
(defn scroll-to [component x y]
|
||||||
|
(.scrollTo (.-scrollView component) (clj->js {:y y
|
||||||
|
:x x})))
|
||||||
|
|
||||||
|
(defn get-current-position [event]
|
||||||
|
(.-x (.-contentOffset (.-nativeEvent event))))
|
||||||
|
|
||||||
|
(defn go-to-page [component page]
|
||||||
|
(let [props (reagent.core/props component)
|
||||||
|
state (reagent.core/state component)
|
||||||
|
page-width (get-page-width state)
|
||||||
|
gap (get-gap state)
|
||||||
|
page-position (* page (+ page-width gap))]
|
||||||
|
(log/debug "go-to-page: props-page-width=" page-width "; gap=" gap
|
||||||
|
"; page-position=" page-position)
|
||||||
|
(scroll-to component page-position 0)
|
||||||
|
(reagent.core/set-state component {:activePage page})
|
||||||
|
(when (:onPageChange props)
|
||||||
|
((:onPageChange props) page))))
|
||||||
|
|
||||||
|
(defn on-scroll-end [event component starting-position]
|
||||||
|
(let [props (reagent.core/props component)
|
||||||
|
state (reagent.core/state component)
|
||||||
|
scroll-threshold (get-scroll-threshold props)
|
||||||
|
current-page (get-active-page state)
|
||||||
|
current-position (get-current-position event)
|
||||||
|
direction (cond
|
||||||
|
(> current-position (+ starting-position scroll-threshold)) 1
|
||||||
|
(< current-position (- starting-position scroll-threshold)) -1
|
||||||
|
:else 0)
|
||||||
|
new-page (+ current-page direction)
|
||||||
|
]
|
||||||
|
(log/debug state "on-scroll-end: starting position=" starting-position
|
||||||
|
"; current-position=" current-position "; direction=" direction
|
||||||
|
"; current-page=" current-page "; new-page=" new-page)
|
||||||
|
(if (not= current-page new-page)
|
||||||
|
(go-to-page component new-page)
|
||||||
|
(scroll-to component starting-position 0))))
|
||||||
|
|
||||||
|
(defn component-will-mount [component new-args]
|
||||||
|
(let [props (reagent.core/props component)]
|
||||||
|
(log/debug "component-will-mount: new-args="new-args)
|
||||||
|
(apply-props component props)))
|
||||||
|
|
||||||
|
(defn component-did-mount [component]
|
||||||
|
(let [props (reagent.core/props component)
|
||||||
|
initial-page (.-initialPage props)]
|
||||||
|
(log/debug "component-did-mount: initial-page="initial-page)
|
||||||
|
(when (pos? initial-page)
|
||||||
|
(go-to-page component initial-page))))
|
||||||
|
|
||||||
|
(defn component-will-update [component new-argv]
|
||||||
|
(log/debug "component-will-update: "))
|
||||||
|
|
||||||
|
(defn component-did-update [component old-argv]
|
||||||
|
(log/debug "component-did-update"))
|
||||||
|
|
||||||
|
(defn component-will-receive-props [component new-argv]
|
||||||
|
(log/debug "component-will-receive-props: new-argv=" new-argv)
|
||||||
|
(apply-props component new-argv))
|
||||||
|
|
||||||
|
(defn get-event-width [event]
|
||||||
|
(.-width (.-layout (.-nativeEvent event))))
|
||||||
|
|
||||||
|
(defn on-layout-change [event component]
|
||||||
|
(let [state (reagent.core/state component)
|
||||||
|
page-width (compute-page-width (get-event-width event) (get-gap state) (get-sneak state))
|
||||||
|
state-page-width (get-page-width state)
|
||||||
|
active-page (get-active-page state)
|
||||||
|
gap (get-gap state)
|
||||||
|
page-position (* active-page (+ page-width gap))]
|
||||||
|
(log/debug "Layout changed: " " page-width=" page-width "; state-page-width=" state-page-width)
|
||||||
|
(if (not= page-width state-page-width)
|
||||||
|
(do
|
||||||
|
(reagent.core/set-state component {:pageWidth page-width})
|
||||||
|
(.setState component {:layout (.-layout (.-nativeEvent event))})
|
||||||
|
)
|
||||||
|
(scroll-to component page-position 0))))
|
||||||
|
|
||||||
|
(defn get-pages [component data children]
|
||||||
|
(let [page-width (get-page-width data)
|
||||||
|
page-style (get-page-style data)
|
||||||
|
gap (get-gap data)
|
||||||
|
margin (quot gap 2)]
|
||||||
|
(doall (map-indexed (fn [index child]
|
||||||
|
(let [page-index index
|
||||||
|
touchable-data {:key index
|
||||||
|
:onPress #(go-to-page component page-index)}]
|
||||||
|
[touchable-without-feedback touchable-data
|
||||||
|
[view {:style [(st/page page-width margin)
|
||||||
|
page-style]
|
||||||
|
:onLayout #(log/debug "view onLayout" %)}
|
||||||
|
|
||||||
|
child]])) children))))
|
||||||
|
|
||||||
|
(defn reagent-render [data children]
|
||||||
|
(let [starting-position (atom 0)
|
||||||
|
component (reagent.core/current-component)
|
||||||
|
state (reagent.core/state component)
|
||||||
|
sneak (get-sneak state)
|
||||||
|
gap (get-gap state)]
|
||||||
|
(log/debug "reagent-render: " data state)
|
||||||
|
[view {:style st/scroll-view-container}
|
||||||
|
[scroll-view {:contentContainerStyle (st/content-container sneak gap)
|
||||||
|
:automaticallyAdjustContentInsets false
|
||||||
|
:bounces false
|
||||||
|
:decelerationRate 0.9
|
||||||
|
:horizontal true
|
||||||
|
:onLayout #(on-layout-change % component)
|
||||||
|
:onScrollBeginDrag #(reset! starting-position (get-current-position %))
|
||||||
|
:onScrollEndDrag #(on-scroll-end % component @starting-position)
|
||||||
|
:showsHorizontalScrollIndicator false
|
||||||
|
:ref #(set! (.-scrollView component) %)}
|
||||||
|
(get-pages component state children)]]))
|
||||||
|
|
||||||
|
(defn carousel [data children]
|
||||||
|
(let [component-data {:component-did-mount component-did-mount
|
||||||
|
:component-will-mount component-will-mount
|
||||||
|
:component-will-receive-props component-will-receive-props
|
||||||
|
:component-will-update component-will-update
|
||||||
|
:component-did-update component-did-update
|
||||||
|
:display-name "carousel"
|
||||||
|
:reagent-render reagent-render}]
|
||||||
|
(log/debug "Creating carousel component: " data)
|
||||||
|
(reagent.core/create-class component-data)))
|
|
@ -0,0 +1,24 @@
|
||||||
|
(ns syng-im.components.carousel.styles
|
||||||
|
(:require [syng-im.components.styles :refer [font
|
||||||
|
title-font
|
||||||
|
color-white
|
||||||
|
chat-background
|
||||||
|
online-color
|
||||||
|
selected-message-color
|
||||||
|
separator-color
|
||||||
|
text1-color
|
||||||
|
text2-color
|
||||||
|
toolbar-background1]]))
|
||||||
|
|
||||||
|
(def scroll-view-container
|
||||||
|
{:flex 1})
|
||||||
|
|
||||||
|
(defn content-container [sneak gap]
|
||||||
|
{:paddingLeft (+ sneak (quot gap 2))
|
||||||
|
:paddingRight (+ sneak (quot gap 2))})
|
||||||
|
|
||||||
|
(defn page [page-width margin]
|
||||||
|
{:width page-width
|
||||||
|
:justifyContent "center"
|
||||||
|
:marginLeft margin
|
||||||
|
:marginRight margin})
|
|
@ -17,7 +17,6 @@
|
||||||
(log/debug "Got popular tags: " @popular-tags)
|
(log/debug "Got popular tags: " @popular-tags)
|
||||||
(if (> (count @popular-tags) 0)
|
(if (> (count @popular-tags) 0)
|
||||||
[carousel {:pageStyle st/carousel-page-style
|
[carousel {:pageStyle st/carousel-page-style
|
||||||
:pageWidth (- (page-width) 60)
|
|
||||||
:sneak 20}
|
:sneak 20}
|
||||||
(for [tag @popular-tags]
|
(for [tag @popular-tags]
|
||||||
(discovery-popular-list (.-name tag) (.-count tag) navigator))]
|
(discovery-popular-list (.-name tag) (.-count tag) navigator))]
|
||||||
|
|
|
@ -28,14 +28,12 @@
|
||||||
|
|
||||||
(defn discovery-popular-list [tag count navigator]
|
(defn discovery-popular-list [tag count navigator]
|
||||||
(let [discoveries (subscribe [:get-discoveries-by-tag tag 3])]
|
(let [discoveries (subscribe [:get-discoveries-by-tag tag 3])]
|
||||||
(log/debug "Got discoveries for tag (" tag "): " @discoveries)
|
|
||||||
[view {:style st/popular-list-container}
|
[view {:style st/popular-list-container}
|
||||||
[view {:style st/row}
|
[view st/row
|
||||||
[view {:style st/column}
|
[view st/tag-name-container
|
||||||
[touchable-highlight {:onPress #(dispatch [:show-discovery-tag tag navigator :push])}
|
[touchable-highlight {:onPress #(dispatch [:show-discovery-tag tag navigator :push])}
|
||||||
[view {:style st/tag-name-container}
|
|
||||||
[text {:style st/tag-name}
|
[text {:style st/tag-name}
|
||||||
(str " #" (name tag))]]]]
|
(str " #" (name tag))]]]
|
||||||
[view {:style st/tag-count-container}
|
[view {:style st/tag-count-container}
|
||||||
[text {:style st/tag-count}
|
[text {:style st/tag-count}
|
||||||
count]]]
|
count]]]
|
||||||
|
@ -43,5 +41,4 @@
|
||||||
:enableEmptySections true
|
:enableEmptySections true
|
||||||
:renderRow render-row
|
:renderRow render-row
|
||||||
:renderSeparator render-separator
|
:renderSeparator render-separator
|
||||||
:style st/popular-list}]
|
:style st/popular-list}]]))
|
||||||
]))
|
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
)
|
)
|
||||||
|
|
||||||
(defn discovery-popular-list-item [discovery]
|
(defn discovery-popular-list-item [discovery]
|
||||||
(log/debug discovery)
|
|
||||||
(r/as-element [view {:style st/popular-list-item}
|
(r/as-element [view {:style st/popular-list-item}
|
||||||
[view {:style st/popular-list-item-name-container}
|
[view {:style st/popular-list-item-name-container}
|
||||||
[text {:style st/popular-list-item-name} (aget discovery "name")]
|
[text {:style st/popular-list-item-name} (aget discovery "name")]
|
||||||
|
|
|
@ -75,7 +75,8 @@
|
||||||
:justifyContent "center"})
|
:justifyContent "center"})
|
||||||
|
|
||||||
(def tag-name-container
|
(def tag-name-container
|
||||||
{:backgroundColor "#eef2f5"
|
{:flexDirection "column"
|
||||||
|
:backgroundColor "#eef2f5"
|
||||||
:borderRadius 5
|
:borderRadius 5
|
||||||
:padding 4})
|
:padding 4})
|
||||||
|
|
||||||
|
@ -90,6 +91,7 @@
|
||||||
|
|
||||||
(def tag-count-container
|
(def tag-count-container
|
||||||
{:flex 0.2
|
{:flex 0.2
|
||||||
|
:flexDirection "column"
|
||||||
:alignItems "flex-end"
|
:alignItems "flex-end"
|
||||||
:paddingTop 10
|
:paddingTop 10
|
||||||
:paddingRight 9})
|
:paddingRight 9})
|
||||||
|
|
Loading…
Reference in New Issue