-
Notifications
You must be signed in to change notification settings - Fork 56
/
results.cljs
205 lines (192 loc) · 8.71 KB
/
results.cljs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
(ns bluegenes.pages.regions.results
(:require [re-frame.core :refer [subscribe dispatch]]
[reagent.core :as reagent]
[bluegenes.pages.regions.graphs :as graphs]
[bluegenes.components.icons :refer [icon]]
[bluegenes.components.table :as table]
[bluegenes.components.loader :refer [loader]]
[bluegenes.pages.regions.events :refer [prepare-export-query]]
[bluegenes.components.imcontrols.views :as im-controls]
[bluegenes.components.bootstrap :refer [popover tooltip]]
[bluegenes.components.export-query :as export-query]
[bluegenes.pages.regions.utils :refer [strand-indicator]]
[clojure.string :refer [split]]
[oops.core :refer [oget ocall oset!]]
[bluegenes.route :as route]
[goog.functions :refer [debounce]]
[imcljs.query :as im-query]
[goog.dom :as gdom]
[goog.fx.dom :as gfx]
[goog.fx.easing :as geasing]
[goog.style :as gstyle]))
(def result-id "region-result-")
(defn region-header
"Header for each region. includes paginator and number of features."
[idx {:keys [chromosome from to strand results] :as feature} paginator]
[:h3 {:id (str result-id idx)}
[:strong "Region: "]
[:span (str chromosome " " from ".." to " ") [strand-indicator strand]]
[:small.features-count (count results) " overlapping features"]
(when (seq results) paginator)])
(defn table-paginator
"UI component to switch between pages of results"
[pager results]
(let [page-count (int (.ceil js/Math (/ (count results) (:show @pager))))]
[:div.pull-right.paginator
[:button
{:disabled (< (:page @pager) 1)
:on-click (fn [] (swap! pager update :page dec))}
"< Previous"]
[:span.currentpage "Page " (inc (:page @pager)) " of " page-count]
[:button
{:disabled (< (count results) (* (:show @pager) (inc (:page @pager))))
:on-click (fn [] (swap! pager update :page inc))} "Next >"]]))
(defn table-header
"Header for results table."
[]
[:div.grid-3_xs-3
[:div.col [:h4 "Feature"]]
[:div.col [:h4 "Feature Type"]]
[:div.col [:h4 "Location"]]])
(def !dispatch
^{:doc
"This dispatch is shared among all the row's mouseenter and mouseleave
events. Without it, way too many events will fire causing slowdowns for
large result sets."}
(debounce dispatch 50))
(defn table-row
"A single result row for a single region feature."
[idx {:keys [symbol primaryIdentifier class chromosomeLocation objectId] :as result}]
(let [model (subscribe [:model])
current-mine (subscribe [:current-mine])
the-type (get-in @model [(keyword class) :displayName])
{:keys [start end strand locatedOn]} chromosomeLocation]
[:a
{:href (route/href ::route/report
{:mine (name (:id @current-mine))
:type class
:id objectId})
:on-mouse-enter #(!dispatch [:regions/set-highlight idx
{:chromosome (:primaryIdentifier locatedOn)
:start start
:end end}])
:on-mouse-leave #(!dispatch [:regions/clear-highlight idx])}
[:div.grid-3_xs-3.single-feature
[:div.col.feature-name
(when symbol [:strong symbol])
primaryIdentifier]
[:div.col the-type]
[:div.col (str (:primaryIdentifier locatedOn) ":" start ".." end)
[strand-indicator strand]]]]))
(defn create-list [features list-basename]
(let [class->features (group-by :class features)]
[:div.dropdown.create-list-dropdown
[:button.btn.btn-default.btn-raised.btn-xs.dropdown-toggle
{:data-toggle "dropdown"}
"Create list by feature type" [:span.caret]]
[:div.dropdown-menu.dropdown-mixed-content
(into [:ul]
(for [type (sort (keys class->features))
:let [ids (->> (get class->features type)
(map :objectId)
(distinct)
(into []))]]
[:li
{:on-click #(dispatch [:regions/create-list
ids type (str list-basename " " type)])}
(str type " (" (count ids) ")")]))]]))
(defn result-table
"The result table for a region - all features"
[idx]
(let [pager (reagent/atom {:show 20 :page 0})
subquery (subscribe [:regions/subquery idx])]
(fn [idx {:keys [chromosome from to results] :as feature}]
(if (seq (:results feature))
[:div.results
[region-header idx feature [table-paginator pager results]]
[graphs/main idx feature]
[:div.tabulated
[table-header]
(into [:div.results-body]
;; Note: If you change the sort function, make the same change
;; in bluegenes.pages.regions.graphs.
(->> (sort-by (comp :start :chromosomeLocation) results)
(drop (* (:show @pager) (:page @pager)))
(take (:show @pager))
(map (fn [result]
[table-row idx result]))))]
[:hr]
[:div.results-footer
[export-query/main (prepare-export-query @subquery)
:label "Export features: "]
[create-list results (str chromosome ":" from ".." to)]
[:button.btn.btn-default.btn-raised.btn-xs
{:on-click #(dispatch [:regions/view-query @subquery feature])}
"View in results table"]]]
[:div.results.noresults
[region-header idx feature]
[:p "No features returned for this region"]]))))
(defn error-loading-results [error]
[:div.results.error
[icon "wondering"]
[:div.errordetails
[:h3 error]
[:p "Looks like there was a problem fetching results."]
[:ul
[:li "Please check that your search regions are in the correct format."]
[:li "Please check you're connected to the internet."]]]])
(def skip-to-id "region-skip-to-bar")
(defn scroll-into-view! [id]
(when-let [elem (or (nil? id) (gdom/getElement id))]
(let [padding (+ 72 (oget (gdom/getElement skip-to-id) :offsetHeight))
current-scroll (clj->js ((juxt #(oget % :x) #(oget % :y))
(gdom/getDocumentScroll)))
target-scroll (if (nil? id)
#js [0 0] ; Scroll to top if no ID specified.
(clj->js ((juxt #(- (oget % :x) padding) #(- (oget % :y) padding))
(gstyle/getRelativePosition elem (gdom/getDocumentScrollElement)))))]
(doto (gfx/Scroll. (gdom/getDocumentScrollElement)
current-scroll
target-scroll
300
geasing/inAndOut)
(.play)))))
(defn results-count-summary [results]
(when (seq results)
(into [:div.results-counts
[:span.skip-to "Skip to:"]
[:span.results-count
{:on-click #(scroll-into-view! nil)}
"Top"]]
(for [[index result] (map-indexed vector results)
:let [amount (count (:results result))
{:keys [chromosome]} result]]
[:span.results-count
{:class (when (zero? amount) :noresults)
:on-click #(scroll-into-view! (str result-id index))}
[:strong chromosome] ": " amount " results"]))))
(defn results-section []
(let [results (subscribe [:regions/results])
loading? (subscribe [:regions/loading])
error (subscribe [:regions/error])
query (subscribe [:regions/query])]
(fn []
(cond
@loading? [loader "Regions"]
(nil? @error) (let [all-results (mapcat :results @results)]
[:div
(when (seq all-results)
[:div.results-actions
[export-query/main (prepare-export-query @query)
:label "Export features within all regions:"]
[create-list all-results "All Regions"]
[:button.btn.btn-default.btn-raised.btn-xs
{:on-click #(dispatch [:regions/view-query @query])}
"View all in results table"]])
[:div.results-summary {:id skip-to-id}
[results-count-summary @results]]
(into [:div.allresults]
(map-indexed (fn [idx result]
[result-table idx result])
@results))])
:else [error-loading-results @error]))))