Skip to content

Commit 2079bf7

Browse files
Initial commit WIP
1 parent b4a6565 commit 2079bf7

File tree

7 files changed

+411
-2
lines changed

7 files changed

+411
-2
lines changed

.gitignore

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
.DS_Store
2+
.cache/
3+
.clerk/
4+
.cpcache/
5+
.lsp/
6+
node_modules
7+
notebooks/scratch*.clj
8+
/public/js/
9+
/.shadow-cljs/
10+
/target/
11+
.idea/
12+
clerk.iml
13+
clerk-table-stats.iml
14+
public/girouette.css
15+
public/images/
16+
yarn.lock
17+
*~
18+
.work
19+
/build
20+
/public/build
21+
.nrepl-port

LICENSE

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Copyright 2021 Nextjournal GmbH.
2+
3+
Permission to use, copy, modify, and/or distribute this software for any purpose
4+
with or without fee is hereby granted, provided that the above copyright notice
5+
and this permission notice appear in all copies.
6+
7+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
8+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
9+
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
10+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
11+
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
12+
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
13+
THIS SOFTWARE.

README.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1-
# clerk-table-stats
2-
Add simple columns summaries to your Clerk tables
1+
# 📊 clerk-table-stats
2+
3+
Add summary charts to your Clerk tables!

deps.edn

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{:aliases
2+
{:dev {:paths ["dev"]
3+
:extra-deps {meta-csv/meta-csv {:mvn/version "0.1.0"}}}
4+
:nextjournal/clerk {:extra-deps {io.github.nextjournal/clerk {:git/sha "9386339ab6a4b3791d66f56be63871b73dd12dee"}}}}}

dev/user.clj

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
(ns user
2+
(:require [nextjournal.clerk :as clerk]))
3+
4+
(clerk/serve! {:browse? true :port 7779})
5+

notebooks/table_stats.clj

+130
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
(ns ^:nextjournal.clerk/no-cache table-stats
2+
(:require [nextjournal.clerk :as clerk]
3+
[nextjournal.clerk-table-stats :as table-stats]
4+
[meta-csv.core :as csv]))
5+
6+
(clerk/add-viewers! [table-stats/table+stats-viewer])
7+
8+
^{:nextjournal.clerk/visibility :hide}
9+
(clerk/with-viewer {:render-fn '(fn []
10+
(defn table-col-bars [{:keys [col-type category-count distribution width height]}]
11+
(reagent/with-let [selected-bar (reagent/atom nil)]
12+
(let [width 140
13+
height 30
14+
last-index (dec (count distribution))]
15+
[:div
16+
[:div.text-slate-500.dark:text-slate-400.font-normal
17+
{:class "text-[12px] h-[24px] leading-[24px]"}
18+
(if-let [{:keys [count percentage]} @selected-bar]
19+
(str count " rows (" (.toFixed (* 100 percentage) 2) "%)")
20+
col-type)]
21+
(into
22+
[:div.flex.relative
23+
{:style {:width width :height height}
24+
:class "rounded-sm overflow-hidden items-center "}]
25+
(map-indexed
26+
(fn [i {:as bar :keys [label count percentage range]}]
27+
(let [bar-width (* width percentage)]
28+
[:div.relative.overflow-hidden
29+
{:on-mouse-enter #(reset! selected-bar bar)
30+
:on-mouse-leave #(reset! selected-bar nil)
31+
:class (case label
32+
:unique "bg-gray-100 hover:bg-gray-200 dark:bg-gray-800 dark:hover:bg-gray-700 "
33+
:empty "bg-orange-200 hover:bg-orange-300 dark:bg-pink-900 dark:bg-opacity-[0.7] dark:hover:bg-pink-800 "
34+
"bg-indigo-200 hover:bg-indigo-300 dark:bg-sky-700 dark:hover:bg-sky-500")
35+
:style {:width bar-width
36+
:height height}}
37+
(when (and (contains? #{:unique :empty} label) (< 30 bar-width))
38+
[:div.text-slate-500.dark:text-slate-300.font-normal.absolute.left-0.top-0.right-0.bottom-0.flex.items-center.justify-center.whitespace-nowrap
39+
{:class "text-[12px]"}
40+
(str (.toFixed (* 100 percentage) 2) "%"
41+
(when (and (= label :unique) (< 80 bar-width))
42+
" unique")
43+
(when (and (= label :empty) (< 110 bar-width))
44+
" empty/nil"))])
45+
(when-not (= i last-index)
46+
[:div.absolute.top-0.right-0.bottom-0
47+
{:class "bg-white bg-opacity-[0.7] dark:bg-black w-[1px]"}])]))
48+
distribution))
49+
[:div.text-slate-500.dark:text-slate-400.font-normal.truncate
50+
{:class "text-[12px] h-[24px] mt-[1px] leading-[24px] "
51+
:style {:width width}}
52+
(if-let [{:keys [count label]} @selected-bar]
53+
(case label
54+
:unique (str count " unique values")
55+
:empty (str count " empty/nil values")
56+
label)
57+
(str "(" category-count " categories)"))]])))
58+
59+
(defn table-col-histogram [{:keys [col-type distribution width height]}]
60+
(reagent/with-let [!selected-bar (reagent/atom nil)
61+
fmt identity #_(goog.i18n.NumberFormat. (j/get-in goog.i18n.NumberFormat [:Format :COMPACT_SHORT]))]
62+
(let [max (:count (apply max-key :count distribution))
63+
last-index (dec (count distribution))
64+
from (-> distribution first :range first)
65+
to (-> distribution last :range last)]
66+
[:div
67+
[:div.text-slate-500.dark:text-slate-400.font-normal
68+
{:class "text-[12px] h-[24px] leading-[24px]"}
69+
(if-let [{:keys [count percentage]} @!selected-bar]
70+
(str count " rows (" (.toFixed (* percentage 100) 2) "%)")
71+
col-type)]
72+
(into
73+
[:div.flex.relative
74+
{:style {:width width :height height}}]
75+
(map-indexed
76+
(fn [i {:as bar row-count :count :keys [range]}]
77+
(let [bar-width (/ width (count distribution))
78+
selected? (= @!selected-bar bar)
79+
last? (= i last-index)]
80+
[:div.relative.group
81+
{:on-mouse-enter #(reset! !selected-bar bar)
82+
:on-mouse-leave #(reset! !selected-bar nil)
83+
:style {:width bar-width
84+
:height (+ height 24)}}
85+
[:div.w-full.flex.items-end
86+
{:style {:height height}}
87+
[:div.w-full.relative
88+
{:style {:height (* (/ row-count max) height)}
89+
:class "bg-indigo-200 group-hover:bg-indigo-400 dark:bg-sky-700 dark:group-hover:bg-sky-500 "}
90+
(when-not last?
91+
[:div.absolute.top-0.right-0.bottom-0
92+
{:class "bg-white dark:bg-black w-[1px]"}])]]
93+
[:div.relative
94+
{:class "mt-[1px] h-[1px] bg-slate-300 dark:bg-slate-700"}
95+
(when selected?
96+
[:div.absolute.left-0.top-0.bg-black.dark:bg-white
97+
{:class (str "h-[2px] " (if last? "right-0" "right-[1px]"))}])]
98+
(when selected?
99+
[:<>
100+
[:div.absolute.left-0.text-left.text-slate-500.dark:text-slate-400.font-normal.pointer-events-none
101+
{:class "text-[12px] h-[24px] leading-[24px] -translate-x-full"
102+
:style {:top height}}
103+
(first range)
104+
#_(.format fmt (first range))]
105+
[:div.absolute.right-0.text-right.text-slate-500.dark:text-slate-400.font-normal.pointer-events-none
106+
{:class "text-[12px] h-[24px] leading-[24px] translate-x-full"
107+
:style {:top height}}
108+
(last range)
109+
#_(.format fmt (last range))]])]))
110+
distribution))
111+
[:div.text-slate-500.dark:text-slate-400.font-normal.truncate
112+
{:class "text-[12px] h-[24px] leading-[24px] "
113+
:style {:width width}}
114+
(when-not @!selected-bar
115+
[:div.relative.pointer-events-none
116+
[:div.absolute.left-0.top-0 from #_(.format fmt from)]
117+
[:div.absolute.right-0.top-0 to #_(.format fmt to)]])]])))
118+
119+
(defn table-col-summary [{:as summary :keys [continuous?]}]
120+
(let [summary (assoc summary :width 140 :height 30)]
121+
(if continuous?
122+
[table-col-histogram summary]
123+
[table-col-bars summary])))
124+
125+
(v/html [:div "Hello"]))}
126+
{:testalizer 123})
127+
128+
(clerk/table {:head [:first-name :last-name :age]
129+
:rows [["Suzy" "McGyver" 30]
130+
["Frank" "Rottenreiter" 12]]})

0 commit comments

Comments
 (0)