2  Examples

This namespace lists various usage examples of Clay. Most of the behaviours demonstrated here are defined by the Kindly specification.

(ns clay-book.examples
  (:require
   [clojure.math :as math]
   [scicloj.kindly.v4.kind :as kind]
   [tablecloth.api :as tc]))

2.1 Plain values

By default, when there is no kind information provided by Kindly, values are simply pretty-printed.

(+ 4 5)
9
(str "abcd" "efgh")
"abcdefgh"

2.2 Hidden

Values of :kind/hidden are not shown.

(kind/hidden 9)

2.3 Hiccup

Hiccup, a popular Clojure way to represent HTML, can be specified by kind:

(kind/hiccup
 [:ul
  [:li [:p "hi"]]
  [:li [:big [:big [:p {:style ; https://www.htmlcsscolor.com/hex/7F5F3F
                        {:color "#7F5F3F"}}
                    "hello"]]]]])
  • hi

  • hello

As we can see, this kind is displayed by converting Hiccup to HTML.

2.4 Reagent

(kind/reagent
 ['(fn [numbers]
     [:p {:style {:background "#d4ebe9"}}
      (pr-str (map inc numbers))])
  (vec (range 40))])

From the reagent tutorial:

(kind/reagent
 ['(fn []
     (let [*click-count (reagent.core/atom 0)]
       (fn []
         [:div
          "The atom " [:code "*click-count"] " has value: "
          @*click-count ". "
          [:input {:type "button" :value "Click me!"
                   :on-click #(swap! *click-count inc)}]])))])

2.5 HTML

Raw html can be represented as a kind too:

(kind/html "<div style='height:40px; width:40px; background:purple'></div> ")
(kind/html
 "
<svg height=100 width=100>
<circle cx=50 cy=50 r=40 stroke='purple' stroke-width=3 fill='floralwhite' />
</svg> ")
(kind/html
 ["<svg height=100 width=100>"
  "<circle cx=50 cy=50 r=40 stroke='purple' stroke-width=3 fill='floralwhite' />"
  "</svg>"])
(kind/html
 (list
  "<svg height=100 width=100>"
  "<circle cx=50 cy=50 r=40 stroke='purple' stroke-width=3 fill='floralwhite' />"
  "</svg>"))

2.6 Markdown

Markdown text (a string or a vector of strings) can be handled using a kind too.

(kind/md
 "This is [markdown](https://www.markdownguide.org/).")

This is markdown.

(kind/md
 ["
* This is [markdown](https://www.markdownguide.org/).
  * *Isn't it??*"
  "
* Here is **some more** markdown."])
  • This is markdown.
    • Isn’t it??
  • Here is some more markdown.
(kind/md
 (list
  "
* This is [markdown](https://www.markdownguide.org/).
  * *Isn't it??*"
  "
* Here is **some more** markdown."))
  • This is markdown.
    • Isn’t it??
  • Here is some more markdown.

LaTeX formulae are supported as well.

(kind/md
 "Let $x=9$. Then $$x+11=20$$")

Let \(x=9\). Then \[x+11=20\]

2.7 TeX

(kind/tex "x^2=\\alpha")

\[x^2=\alpha\]

2.8 Code

Clojure code can be annotated with kind/code.

(kind/code "(update {:x 9} :x inc)")
(update {:x 9} :x inc)
(kind/code
 ["(update {:x 9} :x inc)"
  "(update {:x 9} :x dec)"])
(update {:x 9} :x inc)
(update {:x 9} :x dec)
(kind/code
 (list
  "(update {:x 9} :x inc)"
  "(update {:x 9} :x dec)"))
(update {:x 9} :x inc)
(update {:x 9} :x dec)

2.9 Images

Java BufferedImage objects are displayed as images.

(import javax.imageio.ImageIO
        java.net.URL)
java.net.URL
(defonce clay-image
  (->  "https://upload.wikimedia.org/wikipedia/commons/2/2c/Clay-ss-2005.jpg"
       (URL.)
       (ImageIO/read)))
clay-image

Urls to images can be annotated as images as well.

(kind/image
 {:src "https://upload.wikimedia.org/wikipedia/commons/2/2c/Clay-ss-2005.jpg"})

Other image representations are currently not supported.

(kind/image
 "AN IMAGE")

unsupported image format

2.10 Plain data structures

Plain data structures (lists and sequnces, vectors, sets, maps) are pretty printed if there isn’t any value inside which needs to be displayed in special kind of way.

(def people-as-maps
  (->> (range 29)
       (mapv (fn [i]
               {:preferred-language (["clojure" "clojurescript" "babashka"]
                                     (rand-int 3))
                :age (rand-int 100)}))))
(def people-as-vectors
  (->> people-as-maps
       (mapv (juxt :preferred-language :age))))
(take 5 people-as-maps)
({:preferred-language "clojure", :age 93}
 {:preferred-language "clojure", :age 17}
 {:preferred-language "clojurescript", :age 84}
 {:preferred-language "clojure", :age 72}
 {:preferred-language "babashka", :age 36})
(take 5 people-as-vectors)
(["clojure" 93]
 ["clojure" 17]
 ["clojurescript" 84]
 ["clojure" 72]
 ["babashka" 36])
(->> people-as-vectors
     (take 5)
     set)
#{["clojurescript" 84]
  ["clojure" 17]
  ["babashka" 36]
  ["clojure" 93]
  ["clojure" 72]}

When something inside needs to be displayed in a special kind of way, the data structures are printed in a way that makes that clear.

(def nested-structure-1
  {:vector-of-numbers [2 9 -1]
   :vector-of-different-things ["hi"
                                (kind/hiccup
                                 [:big [:big "hello"]])]
   :map-of-different-things {:markdown (kind/md ["*hi*, **hi**"])
                             :number 9999}
   :hiccup (kind/hiccup
            [:big [:big "bye"]])
   :dataset (tc/dataset {:x (range 3)
                         :y [:A :B :C]})})
nested-structure-1

{

:vector-of-numbers [2 9 -1]
:vector-of-different-things

[

"hi"
hello

]

:map-of-different-things

{

:markdown

hi, hi

:number 9999

}

:hiccup
bye
:dataset

_unnamed [3 2]:

:x :y
0 :A
1 :B
2 :C

}

2.11 Pretty printing

The :kind/pprint kind makes sure to simply pretty-print values:

(kind/pprint nested-structure-1)
{:vector-of-numbers [2 9 -1],
 :vector-of-different-things ["hi" [:big [:big "hello"]]],
 :map-of-different-things {:markdown ["*hi*, **hi**"], :number 9999},
 :hiccup [:big [:big "bye"]],
 :dataset _unnamed [3 2]:

| :x | :y |
|---:|----|
|  0 | :A |
|  1 | :B |
|  2 | :C |
}

2.12 Datasets

tech.ml.dataset datasets currently use the default printing of the library,

Let us create such a dataset using Tablecloth.

(require '[tablecloth.api :as tc])
(-> {:x (range 6)
     :y [:A :B :C :A :B :C]}
    tc/dataset)

_unnamed [6 2]:

:x :y
0 :A
1 :B
2 :C
3 :A
4 :B
5 :C
(-> {:x [1 [2 3] 4]
     :y [:A :B :C]}
    tc/dataset)

_unnamed [3 2]:

:x :y
1 :A
[2 3] :B
4 :C
(-> [{:x 1 :y 2 :z 3}
     {:y 4 :z 5}]
    tc/dataset)

_unnamed [2 3]:

:x :y :z
1 2 3
4 5
(-> people-as-maps
    tc/dataset)

_unnamed [29 2]:

:preferred-language :age
clojure 93
clojure 17
clojurescript 84
clojure 72
babashka 36
babashka 77
babashka 65
babashka 29
babashka 62
clojure 59
clojure 99
babashka 51
babashka 79
babashka 77
clojurescript 4
clojure 81
babashka 35
babashka 11
babashka 41
clojure 0
babashka 6

Some kind options of kind/dataset control the way a dataset is printed.

(-> {:x (range 30)}
    tc/dataset
    (kind/dataset {:dataset/print-range 6}))

_unnamed [30 1]:

:x
0
1
2
26
27
28
29

2.13 Tables

The :kind/table kind can be handy for an interactive table view. :kind/table understands many structures which can be rendered as a table.

A map containing either :row-vectors (sequence of sequences) or row-maps (sequence of maps) keys with optional :column-names.

(kind/table
 {:column-names [:preferred-language :age]
  :row-vectors people-as-vectors})
preferred-language age
clojure 93
clojure 17
clojurescript 84
clojure 72
babashka 36
babashka 77
babashka 65
babashka 29
babashka 62
clojure 59
clojure 68
clojurescript 83
clojure 27
clojurescript 35
clojurescript 87
clojure 70
clojurescript 67
clojurescript 30
clojure 99
babashka 51
babashka 79
babashka 77
clojurescript 4
clojure 81
babashka 35
babashka 11
babashka 41
clojure 0
babashka 6

Lack of column names produces table without a header.

(kind/table
 {:row-vectors (take 5 people-as-vectors)})
clojure 93
clojure 17
clojurescript 84
clojure 72
babashka 36

Column names are inferred from a sequence of maps

(kind/table
 {:row-maps (take 5 people-as-maps)})
preferred-language age
clojure 93
clojure 17
clojurescript 84
clojure 72
babashka 36

We can limit displayed columns for sequence of maps case.

(kind/table
 {:column-names [:preferred-language]
  :row-maps (take 5 people-as-maps)})
preferred-language
clojure
clojure
clojurescript
clojure
babashka

Sequence of sequences and sequence of maps also work

(kind/table (take 5 people-as-vectors))
clojure 93
clojure 17
clojurescript 84
clojure 72
babashka 36
(kind/table (take 5 people-as-maps))
preferred-language age
clojure 93
clojure 17
clojurescript 84
clojure 72
babashka 36

Additionally map of sequences is supported (unless it contains :row-vectors or :row-maps key, see such case above).

(kind/table {:x (range 6)
             :y [:A :B :C :A :B :C]})
x y
0 A
1 B
2 C
3 A
4 B
5 C

A dataset can be also treated as a table input.

(def people-as-dataset
  (tc/dataset people-as-maps))
(-> people-as-dataset
    kind/table)
preferred-language age
clojure 93
clojure 17
clojurescript 84
clojure 72
babashka 36
babashka 77
babashka 65
babashka 29
babashka 62
clojure 59
clojure 68
clojurescript 83
clojure 27
clojurescript 35
clojurescript 87
clojure 70
clojurescript 67
clojurescript 30
clojure 99
babashka 51
babashka 79
babashka 77
clojurescript 4
clojure 81
babashka 35
babashka 11
babashka 41
clojure 0
babashka 6

Additional options may hint at way the table should be rendered.

(-> people-as-dataset
    (kind/table {:element/max-height "300px"}))
preferred-language age
clojure 93
clojure 17
clojurescript 84
clojure 72
babashka 36
babashka 77
babashka 65
babashka 29
babashka 62
clojure 59
clojure 68
clojurescript 83
clojure 27
clojurescript 35
clojurescript 87
clojure 70
clojurescript 67
clojurescript 30
clojure 99
babashka 51
babashka 79
babashka 77
clojurescript 4
clojure 81
babashka 35
babashka 11
babashka 41
clojure 0
babashka 6

It is possible to use datatables to reneder kind/table, and in this case the user may specify datatables options (see the full list).

(-> people-as-maps
    tc/dataset
    (kind/table {:use-datatables true}))
preferred-languageage
clojure93
clojure17
clojurescript84
clojure72
babashka36
babashka77
babashka65
babashka29
babashka62
clojure59
clojure68
clojurescript83
clojure27
clojurescript35
clojurescript87
clojure70
clojurescript67
clojurescript30
clojure99
babashka51
babashka79
babashka77
clojurescript4
clojure81
babashka35
babashka11
babashka41
clojure0
babashka6
(-> people-as-dataset
    (kind/table {:use-datatables true
                 :datatables {:scrollY 300}}))
preferred-languageage
clojure93
clojure17
clojurescript84
clojure72
babashka36
babashka77
babashka65
babashka29
babashka62
clojure59
clojure68
clojurescript83
clojure27
clojurescript35
clojurescript87
clojure70
clojurescript67
clojurescript30
clojure99
babashka51
babashka79
babashka77
clojurescript4
clojure81
babashka35
babashka11
babashka41
clojure0
babashka6

2.14 Vega and Vega-Lite

(defn vega-lite-point-plot [data]
  (-> {:data {:values data},
       :mark "point"
       :encoding
       {:size {:field "w" :type "quantitative"}
        :x {:field "x", :type "quantitative"},
        :y {:field "y", :type "quantitative"},
        :fill {:field "z", :type "nominal"}}}
      kind/vega-lite))
(defn random-data [n]
  (->> (repeatedly n #(- (rand) 0.5))
       (reductions +)
       (map-indexed (fn [x y]
                      {:w (rand-int 9)
                       :z (rand-int 9)
                       :x x
                       :y y}))))
(defn random-vega-lite-plot [n]
  (-> n
      random-data
      vega-lite-point-plot))
(random-vega-lite-plot 9)

When the vega/vega-lite data is given in CSV format, Clay will serve it in a separate CSV file alongside the generated HTML.

(-> {:data {:values "x,y
1,1
2,4
3,9
-1,1
-2,4
-3,9"
            :format {:type :csv}},
     :mark "point"
     :encoding
     {:x {:field "x", :type "quantitative"}
      :y {:field "y", :type "quantitative"}}}
    kind/vega-lite)

2.15 Cytoscape

See the Cytoscape docs.

(def cytoscape-example
  {:elements {:nodes [{:data {:id "a" :parent "b"} :position {:x 215 :y 85}}
                      {:data {:id "b"}}
                      {:data {:id "c" :parent "b"} :position {:x 300 :y 85}}
                      {:data {:id "d"} :position {:x 215 :y 175}}
                      {:data {:id "e"}}
                      {:data {:id "f" :parent "e"} :position {:x 300 :y 175}}]
              :edges [{:data {:id "ad" :source "a" :target "d"}}
                      {:data {:id "eb" :source "e" :target "b"}}]}
   :style [{:selector "node"
            :css {:content "data(id)"
                  :text-valign "center"
                  :text-halign "center"}}
           {:selector "parent"
            :css {:text-valign "top"
                  :text-halign "center"}}
           {:selector "edge"
            :css {:curve-style "bezier"
                  :target-arrow-shape "triangle"}}]
   :layout {:name "preset"
            :padding 5}})
(kind/cytoscape
 cytoscape-example)
(-> cytoscape-example
    (kind/cytoscape {:element/style
                     {:width "100px"
                      :height "100px"}}))

2.16 ECharts

This example is taken from Apache ECharts’ Getting Started.

(def echarts-example
  {:title {:text "Echarts Example"}
   :tooltip {}
   :legend {:data ["sales"]}
   :xAxis {:data ["Shirts", "Cardigans", "Chiffons",
                  "Pants", "Heels", "Socks"]}
   :yAxis {}
   :series [{:name "sales"
             :type "bar"
             :data [5 20 36
                    10 10 20]}]})
(kind/echarts
 echarts-example)
(-> echarts-example
    (kind/echarts {:element/style
                   {:width "500px"
                    :height "200px"}}))

2.17 Plotly

See the plotly.js docs.

(def plotly-example
  {:data [{:x [0 1 3 2]
           :y [0 6 4 5]
           :z [0 8 9 7]
           :type :scatter3d
           :mode :lines+markers
           :opacity 0.5
           :line {:width 5}
           :marker {:size 4
                    :colorscale :Viridis}}]
   :layout {:title "Plotly example"}})
(kind/plotly
 plotly-example)
(-> plotly-example
    (kind/plotly {:element/style
                  {:width "300px"
                   :height "300px"}}))

2.18 Highcharts

(kind/highcharts
 {:title {:text "Line chart"}
  :subtitle {:text "By Job Category"}
  :yAxis {:title {:text "Number of Employees"}}
  :series [{:name "Installation & Developers"
            :data [43934, 48656, 65165, 81827, 112143, 142383,
                   171533, 165174, 155157, 161454, 154610]}

           {:name "Manufacturing",
            :data [24916, 37941, 29742, 29851, 32490, 30282,
                   38121, 36885, 33726, 34243, 31050]}

           {:name "Sales & Distribution",
            :data [11744, 30000, 16005, 19771, 20185, 24377,
                   32147, 30912, 29243, 29213, 25663]}

           {:name "Operations & Maintenance",
            :data [nil, nil, nil, nil, nil, nil, nil,
                   nil, 11164, 11218, 10077]}

           {:name "Other",
            :data [21908, 5548, 8105, 11248, 8989, 11816, 18274,
                   17300, 13053, 11906, 10073]}]

  :xAxis {:accessibility {:rangeDescription "Range: 2010 to 2020"}}

  :legend {:layout "vertical",
           :align "right",
           :verticalAlign "middle"}

  :plotOptions {:series {:label {:connectorAllowed false},
                         :pointStart 2010}}

  :responsive {:rules [{:condition {:maxWidth 500},
                        :chartOptions {:legend {:layout "horizontal",
                                                :align "center",
                                                :verticalAlign "bottom"}}}]}})

2.19 Observable

Observable visualizations are supported when rendering through Quarto.

The following is adapted from the Penguins example in Quarto’s documentation.

Note that you can save your Clojure data as a csv file and refer to it from within your Observable code. See Referring to files for more information. In this case, we are using the local file, "notebooks/datasets/palmer-penguins.csv", which is transparently copied by Clay alongside the target HTML.

(kind/observable
 "
//| panel: input
viewof bill_length_min = Inputs.range(
                                      [32, 50],
                                      {value: 35, step: 1, label: 'Bill length (min):'}
                                      )
viewof islands = Inputs.checkbox(
                                 ['Torgersen', 'Biscoe', 'Dream'],
                                 { value: ['Torgersen', 'Biscoe'],
                                  label: 'Islands:'
                                  }
                                 )

Plot.rectY(filtered,
            Plot.binX(
                      {y: 'count'},
                      {x: 'body_mass_g', fill: 'species', thresholds: 20}
                      ))
 .plot({
        facet: {
                data: filtered,
                x: 'sex',
                y: 'species',
                marginRight: 80
                },
        marks: [
                Plot.frame(),
                ]
        }
       )
Inputs.table(filtered)
penguins = FileAttachment('notebooks/datasets/palmer-penguins.csv').csv({ typed: true })
filtered = penguins.filter(function(penguin) {
                                           return bill_length_min < penguin.bill_length_mm &&
                                           islands.includes(penguin.island);
                                           })
")

More examples from Quarto’s Observable documentation:

(kind/observable
 "athletes = FileAttachment('notebooks/datasets/athletes.csv').csv({typed: true})")
(kind/observable
 "athletes")
(kind/observable
 "Inputs.table(athletes)")
(kind/observable
 "
Plot.plot({
  grid: true,
  facet: {
    data: athletes,
    y: 'sex'
  },
  marks: [
    Plot.rectY(
      athletes,
      Plot.binX({y: 'count'}, {x: 'weight', fill: 'sex'})
    ),
    Plot.ruleY([0])
  ]
})
")
(kind/observable
 "population = FileAttachment('notebooks/datasets/population.json').json()")
(kind/observable
 "population")
(kind/observable
 " import { chart } with { population as data } from '@d3/zoomable-sunburst'
 chart")

2.20 Leaflet

This example was adapted from the Leaflet website. Note we are defining a tile layer using leaflet-providers.

(kind/reagent
 ['(fn []
     [:div {:style {:height "200px"}
            :ref (fn [el]
                   (let [m (-> js/L
                               (.map el)
                               (.setView (clj->js [51.505 -0.09])
                                         13))]
                     (-> js/L
                         .-tileLayer
                         (.provider "OpenStreetMap.Mapnik")
                         (.addTo m))
                     (-> js/L
                         (.marker (clj->js [51.5 -0.09]))
                         (.addTo m)
                         (.bindPopup "A pretty CSS popup.<br> Easily customizable.")
                         (.openPopup))))}])]
 ;; Note we need to mention the dependency:
 {:html/deps [:leaflet]})

2.21 D3

The following example is adapted from hiccup-d3. The code is a bit different, e.g. (.scaleOrdinal js/d3 (.-schemeCategory10 js/d3)) instead of (d3/scaleOrdinal d3/schemeCategory10). We still need to figure out how to make hiccup-d3’s examples work as they are.

(let [letter-frequencies [{:letter "A", :frequency 0.08167}
                          {:letter "B", :frequency 0.01492}
                          {:letter "C", :frequency 0.02782}
                          {:letter "D", :frequency 0.04253}
                          {:letter "E", :frequency 0.12702}
                          {:letter "F", :frequency 0.02288}
                          {:letter "G", :frequency 0.02015}
                          {:letter "H", :frequency 0.06094}
                          {:letter "I", :frequency 0.06966}
                          {:letter "J", :frequency 0.00153}
                          {:letter "K", :frequency 0.00772}
                          {:letter "L", :frequency 0.04025}
                          {:letter "M", :frequency 0.02406}
                          {:letter "N", :frequency 0.06749}
                          {:letter "O", :frequency 0.07507}
                          {:letter "P", :frequency 0.01929}
                          {:letter "Q", :frequency 0.00095}
                          {:letter "R", :frequency 0.05987}
                          {:letter "S", :frequency 0.06327}
                          {:letter "T", :frequency 0.09056}
                          {:letter "U", :frequency 0.02758}
                          {:letter "V", :frequency 0.00978}
                          {:letter "W", :frequency 0.0236}
                          {:letter "X", :frequency 0.0015}
                          {:letter "Y", :frequency 0.01974}
                          {:letter "Z", :frequency 0.00074}]]
  (kind/reagent
   ['(fn [data]
       (let [size 400
             x (-> js/d3
                   .scaleLinear
                   (.range (into-array [0 size]))
                   (.domain (into-array [0 (apply max (map :frequency data))])))
             y (-> js/d3
                   .scaleBand
                   (.domain (into-array (map :letter data)))
                   (.range (into-array [0 size])))
             color (.scaleOrdinal js/d3 (.-schemeCategory10 js/d3))]
         [:svg
          {:viewBox (str "0 0 " size " " size)}
          (map
           (fn
             [{:keys [letter frequency]}]
             [:g
              {:key letter, :transform (str "translate(" 0 "," (y letter) ")")}
              [:rect
               {:x (x 0),
                :height (.bandwidth y),
                :fill (color letter),
                :width (x frequency)}]])
           data)]))
    letter-frequencies]
   {:html/deps [:d3]}))

2.22 ggplotly

(WIP)

Clay supports rendering plots through the JS client side of ggplotly - an R package offering a Plotly fronted for ggplot2’s grammar of graphics implementation. This package is part of the htmlwidgets ecosystem, and we represent that in the kind’s name.

The following is a work-in-progress attempt to generate JSON specs of the kind consumed by ggplotly’s client side.

The following spec function was generaged by mimicking R’s ggplotly(ggplot(mtcars, aes(wt, mpg)) + geom_point()). Therefore, some parts are hard-coded and require generalization. Other parts are missing (e.g., specifying colours).

(defn ->ggplotly-spec [{:keys [layers labels]}]
  (kind/htmlwidgets-ggplotly
   (let [;; assuming a single layer for now:
         {:keys [data xmin xmax ymin ymax]} (first layers)
         ;; an auxiliary function to compute tick values:
         ->tickvals (fn [l r]
                      (let [jump (-> (- r l)
                                     (/ 6)
                                     math/floor
                                     int
                                     (max 1))]
                        (-> l
                            math/ceil
                            (range r jump))))]
     {:x
      {:config
       {:doubleClick "reset",
        :modeBarButtonsToAdd ["hoverclosest" "hovercompare"],
        :showSendToCloud false},
       :layout
       {:plot_bgcolor "rgba(235,235,235,1)",
        :paper_bgcolor "rgba(255,255,255,1)",
        :legend
        {:bgcolor "rgba(255,255,255,1)",
         :bordercolor "transparent",
         :borderwidth 1.88976377952756,
         :font {:color "rgba(0,0,0,1)", :family "", :size 11.689497716895}},
        :xaxis (let [tickvals (->tickvals xmin xmax)
                     ticktext (mapv str tickvals)
                     range-len (- xmax xmin)
                     range-expansion (* 0.1 range-len)
                     expanded-range [(- xmin range-expansion)
                                     (+ xmax range-expansion)]]
                 {:linewidth 0,
                  :nticks nil,
                  :linecolor nil,
                  :ticklen 3.65296803652968,
                  :tickcolor "rgba(51,51,51,1)",
                  :tickmode "array",
                  :gridcolor "rgba(255,255,255,1)",
                  :automargin true,
                  :type "linear",
                  :tickvals tickvals
                  :zeroline false,
                  :title
                  {:text (:x labels),
                   :font {:color "rgba(0,0,0,1)", :family "", :size 14.6118721461187}},
                  :tickfont {:color "rgba(77,77,77,1)", :family "", :size 11.689497716895},
                  :autorange false,
                  :showticklabels true,
                  :showline false,
                  :showgrid true,
                  :ticktext ticktext
                  :ticks "outside",
                  :gridwidth 0.66417600664176,
                  :anchor "y",
                  :domain [0 1],
                  :hoverformat ".2f",
                  :tickangle 0,
                  :tickwidth 0.66417600664176,
                  :categoryarray ticktext,
                  :categoryorder "array",
                  :range expanded-range},)
        :font {:color "rgba(0,0,0,1)", :family "", :size 14.6118721461187},
        :showlegend false,
        :barmode "relative",
        :yaxis (let [tickvals (->tickvals ymin ymax)
                     ticktext (mapv str tickvals)
                     range-len (- ymax ymin)
                     range-expansion (* 0.1 range-len)
                     expanded-range [(- ymin range-expansion)
                                     (+ ymax range-expansion)]]
                 {:linewidth 0,
                  :nticks nil,
                  :linecolor nil,
                  :ticklen 3.65296803652968,
                  :tickcolor "rgba(51,51,51,1)",
                  :tickmode "array",
                  :gridcolor "rgba(255,255,255,1)",
                  :automargin true,
                  :type "linear",
                  :tickvals tickvals,
                  :zeroline false,
                  :title
                  {:text (:y labels),
                   :font {:color "rgba(0,0,0,1)", :family "", :size 14.6118721461187}},
                  :tickfont {:color "rgba(77,77,77,1)", :family "", :size 11.689497716895},
                  :autorange false,
                  :showticklabels true,
                  :showline false,
                  :showgrid true,
                  :ticktext ticktext,
                  :ticks "outside",
                  :gridwidth 0.66417600664176,
                  :anchor "x",
                  :domain [0 1],
                  :hoverformat ".2f",
                  :tickangle 0,
                  :tickwidth 0.66417600664176,
                  :categoryarray ticktext,
                  :categoryorder "array",
                  :range expanded-range},)
        :hovermode "closest",
        :margin
        {:t 25.7412480974125,
         :r 7.30593607305936,
         :b 39.6955859969559,
         :l 37.2602739726027},
        :shapes
        [{:yref "paper",
          :fillcolor nil,
          :xref "paper",
          :y1 1,
          :type "rect",
          :line {:color nil, :width 0, :linetype []},
          :y0 0,
          :x1 1,
          :x0 0}]},
       :highlight
       {:on "plotly_click",
        :persistent false,
        :dynamic false,
        :selectize false,
        :opacityDim 0.2,
        :selected {:opacity 1},
        :debounce 0},
       :base_url "https://plot.ly",
       :cur_data "1f2fea5b54d146",
       :source "A",
       :shinyEvents
       ["plotly_hover"
        "plotly_click"
        "plotly_selected"
        "plotly_relayout"
        "plotly_brushed"
        "plotly_brushing"
        "plotly_clickannotation"
        "plotly_doubleclick"
        "plotly_deselect"
        "plotly_afterplot"
        "plotly_sunburstclick"],
       :attrs {:1f2fea5b54d146 {:x {}, :y {}, :type "scatter"}},
       :data
       [{:y (:y data)
         :hoveron "points",
         :frame nil,
         :hoverinfo "text",
         :marker
         {:autocolorscale false,
          :color "rgba(0,0,0,1)",
          :opacity 1,
          :size 5.66929133858268,
          :symbol "circle",
          :line {:width 1.88976377952756, :color "rgba(0,0,0,1)"}},
         :mode "markers"
         :type "scatter",
         :xaxis "x",
         :showlegend false,
         :yaxis "y",
         :x (:x data)
         :text (-> data
                   (tc/select-columns [:x :y])
                   (tc/rows :as-maps)
                   (->> (mapv pr-str)))}]},
      :evals [],
      :jsHooks []})))
(require '[tech.v3.datatype.functional :as fun])

A random walk example:

(let [n 100
      xs (range n)
      ys (reductions + (repeatedly n #(- (rand) 0.5)))
      xmin (fun/reduce-min xs)
      xmax (fun/reduce-max xs)
      ymin (fun/reduce-min ys)
      ymax (fun/reduce-max ys)
      data (tc/dataset {:x xs
                        :y ys})]
  (->ggplotly-spec
   {:layers [{:data data
              :xmin xmin :xmax xmax
              :ymin ymin :ymax ymax}]
    :labels {:x "wt"
             :y "mpg"}}))

2.23 3DMol.js

Embedding a 3Dmol Viewer (original example):

(kind/reagent
 ['(fn [{:keys [data-pdb]}]
     [:div {:style {:height "400px"
                    :width "400px"
                    :position :relative}
            :class "viewer_3Dmoljs"
            :data-pdb data-pdb
            :data-backgroundcolor "0xffffff"
            :data-style "stick"
            :data-ui true}])
  {:data-pdb "2POR"}]
 ;; Note we need to mention the dependency:
 {:html/deps [:three-d-mol]})

Using 3Dmol within your code (inspired by these examples):

(defonce pdb-2POR
(slurp "https://files.rcsb.org/download/2POR.pdb"))
(kind/reagent
 ['(fn [{:keys [pdb-data]}]
     [:div
      {:style {:width "100%"
               :height "500px"
               :position "relative"}
       :ref (fn [el]
              (let [config (clj->js
                            {:backgroundColor "0xffffff"})
                    viewer (.createViewer js/$3Dmol el)]
                (.setViewStyle viewer (clj->js
                                       {:style "outline"}))
                (.addModelsAsFrames viewer pdb-data "pdb")
                (.addSphere viewer (clj->js
                                    {:center {:x 0
                                              :y 0
                                              :z 0}
                                     :radius 5
                                     :color "green"
                                     :alpha 0.2}))
                (.zoomTo viewer)
                (.render viewer)
                (.zoom viewer 0.8 2000)))}])
  {:pdb-data pdb-2POR}]
 ;; Note we need to mention the dependency:
 {:html/deps [:three-d-mol]})

2.24 Video

Videos can be specified as urls (possibly to local files):

(kind/video {:src "https://file-examples.com/storage/fe58a1f07d66f447a9512f1/2017/04/file_example_MP4_480_1_5MG.mp4"})

Videos can also be specified as youtube videos: See, e.g., HTML Youtube Videos on w3schools.

(kind/video {:youtube-id "DAQnvAgBma8"})
(kind/video {:youtube-id "DAQnvAgBma8"
             :allowfullscreen false})
(kind/video {:youtube-id "DAQnvAgBma8"
             :iframe-width 480
             :iframe-height 270})
(kind/video {:youtube-id "DAQnvAgBma8"
             :embed-options {:mute 1
                             :controls 0}})

2.25 Embedded Portal

We may embed Portal’s data-navigating viewers using kind/portal.

(kind/portal {:x (range 3)})

Note that kind/portal applies the kind-portal adapter to nested kinds.

(kind/portal
[(kind/hiccup [:img {:height 50 :width 50
                     :src "https://clojure.org/images/clojure-logo-120b.png"}])
 (kind/hiccup [:img {:height 50 :width 50
                     :src "https://raw.githubusercontent.com/djblue/portal/fbc54632adc06c6e94a3d059c858419f0063d1cf/resources/splash.svg"}])])
(kind/portal
[(kind/hiccup [:big [:big "a plot"]])
 (random-vega-lite-plot 9)])

2.26 Nesting kinds in Hiccup

Kinds are treated recursively inside Hiccup:

(kind/hiccup
 [:div {:style {:background "#f5f3ff"
                :border "solid"}}

  [:hr]
  [:pre [:code "kind/md"]]
  (kind/md "*some text* **some more text**")

  [:hr]
  [:pre [:code "kind/code"]]
  (kind/code "{:x (1 2 [3 4])}")

  [:hr]
  [:pre [:code "kind/dataset"]]
  (tc/dataset {:x (range 33)
               :y (map inc (range 33))})

  [:hr]
  [:pre [:code "kind/table"]]
  (kind/table
   (tc/dataset {:x (range 33)
                :y (map inc (range 33))}))

  [:hr]
  [:pre [:code "kind/vega-lite"]]
  (random-vega-lite-plot 9)

  [:hr]
  [:pre [:code "kind/vega-lite"]]
  (-> {:data {:values "x,y
1,1
2,4
3,9
-1,1
-2,4
-3,9"
              :format {:type :csv}},
       :mark "point"
       :encoding
       {:x {:field "x", :type "quantitative"}
        :y {:field "y", :type "quantitative"}}}
      kind/vega-lite)

  [:hr]
  [:pre [:code "kind/reagent"]]
  (kind/reagent
   ['(fn [numbers]
       [:p {:style {:background "#d4ebe9"}}
        (pr-str (map inc numbers))])
    (vec (range 40))])])

kind/md

some text some more text


kind/code
{:x (1 2 [3 4])}

kind/dataset

_unnamed [33 2]:

:x :y
0 1
1 2
2 3
3 4
4 5
5 6
6 7
7 8
8 9
9 10
... ...
22 23
23 24
24 25
25 26
26 27
27 28
28 29
29 30
30 31
31 32
32 33

kind/table
x y
0 1
1 2
2 3
3 4
4 5
5 6
6 7
7 8
8 9
9 10
10 11
11 12
12 13
13 14
14 15
15 16
16 17
17 18
18 19
19 20
20 21
21 22
22 23
23 24
24 25
25 26
26 27
27 28
28 29
29 30
30 31
31 32
32 33

kind/vega-lite

kind/vega-lite

kind/reagent

2.27 Nesting kinds in Tables

Kinds are treated recursively inside Tables:

(kind/table
{:column-names [(kind/hiccup
                 [:div {:style {:background-color "#ccaabb"}} [:big ":x"]])
                (kind/hiccup
                 [:div {:style {:background-color "#aabbcc"}} [:big ":y"]])]
 :row-vectors [[(kind/md "*some text* **some more text**")
                (kind/code "{:x (1 2 [3 4])}")]
               [(tc/dataset {:x (range 3)
                             :y (map inc (range 3))})
                (random-vega-lite-plot 9)]
               [(kind/hiccup [:div.clay-limit-image-width
                              clay-image])
                (kind/md "$x^2$")]]})
:x
:y
some text some more text
{:x (1 2 [3 4])}
_unnamed [3 2]:
:x:y
01
12
23
\(x^2\)
(kind/table
{:column-names ["size" "square"]
 :row-vectors (for [i (range 20)]
                (let [size (* i 10)
                      px (str size "px")]
                  [size
                   (kind/hiccup
                    [:div {:style {:height px
                                   :width px
                                   :background-color "purple"}}])]))}
{:use-datatables true})
sizesquare
0
10
20
30
40
50
60
70
80
90
100
110
120
130
140
150
160
170
180
190

2.28 More nesting examples

{:plot (random-vega-lite-plot 9)
 :dataset (tc/dataset {:x (range 3)
                       :y (repeatedly 3 rand)})}

{

:plot
:dataset

_unnamed [3 2]:

:x :y
0 0.42423370
1 0.75960742
2 0.23871115

}

[(random-vega-lite-plot 9)
 (tc/dataset {:x (range 3)
              :y (repeatedly 3 rand)})]

[

_unnamed [3 2]:

:x :y
0 0.16428424
1 0.67176938
2 0.58697175

]

source: notebooks/clay_book/examples.clj