diff --git a/src/re_demo/nested_grid.cljs b/src/re_demo/nested_grid.cljs index af58233e..30dc9239 100644 --- a/src/re_demo/nested_grid.cljs +++ b/src/re_demo/nested_grid.cljs @@ -1,8 +1,10 @@ (ns re-demo.nested-grid - (:require [re-com.core :as rc :refer [at h-box v-box box gap line label p p-span hyperlink-href]] - [reagent.core :as r] - [re-com.nested-grid :as nested-grid :refer [nested-grid nested-grid-args-desc nested-grid-parts-desc]] - [re-demo.utils :refer [source-reference panel-title title2 title3 args-table parts-table github-hyperlink status-text new-in-version]])) + (:require + [clojure.string :as str] + [re-com.core :as rc :refer [at h-box v-box box gap line label p p-span hyperlink-href]] + [reagent.core :as r] + [re-com.nested-grid :as nested-grid :refer [nested-grid nested-grid-args-desc nested-grid-parts-desc]] + [re-demo.utils :refer [source-reference panel-title title2 title3 args-table parts-table github-hyperlink status-text new-in-version]])) (def arg-style {:style {:display "inline-block" :font-weight "bold" @@ -39,7 +41,7 @@ "darkyellow" "gold"}) (defn mix-colors [color1 color2] - (name (get-in color-mixer [color1 color2]))) + (name (get-in color-mixer [(keyword color1) (keyword color2)]))) (mix-colors :red :yellow) @@ -48,21 +50,21 @@ :children [[p "Here's a grid with flat columns and rows." " The " [:code ":cell"] " function closes over some external business logic (" - [:code "mix-colors"] ") to express a string value." + [:code "mix-colors"] ") to express a string." " Since there is only one level of nesting, " [:code "column-path"] - " contains a single " [:code "column"] " value - for instance, " + " contains a single " [:i "header value"] " - for instance, " [:code "[:red]"] "."] [:pre "[nested-grid - :columns [:red :yellow :blue] - :rows [:red :yellow :blue] + :columns [\"red\" \"yellow\" \"blue\"] + :rows [\"red\" \"yellow\" \"blue\"] :cell (fn color-cell [{:keys [column-path row-path]}] (mix-colors (last column-path) (last row-path)))]"]]]) (defn color-demo [] [nested-grid - :columns [:red :yellow :blue] - :rows [:red :yellow :blue] + :columns ["red" "yellow" "blue"] + :rows ["red" "yellow" "blue"] :cell (fn color-cell [{:keys [row-path column-path]}] (mix-colors (last row-path) (last column-path)))]) @@ -70,7 +72,8 @@ (defn color-shade-explainer [] [rc/v-box :children - [[p "Since " [:code ":columns"] " is a vector tree with 2 levels of nesting," + [[p "Here, " [:code ":columns"] "is a nested " [:i "configuration"] " of " [:i "header values."]] + [p "Since the " [:i "configuration"] " has 2 levels of nesting," " each " [:code ":column-path"] " is 2-long. For instance, " [:code "[:medium :yellow]"] ". "] [p [:code ":cell"] " returns a hiccup."] @@ -78,11 +81,11 @@ "should return a " [:span {:style {:color "green"}} "green"] " hiccup."] [:pre "[nested-grid - {:columns [:medium [:red :yellow :blue] - :light [:red :yellow :blue] - :dark [:red :yellow :blue]] - :rows [:red :yellow :blue] - :cell color-shade-cell}]"]]]) + :columns [:medium [:red :yellow :blue] + :light [:red :yellow :blue] + :dark [:red :yellow :blue]] + :rows [:red :yellow :blue] + :cell color-shade-cell]"]]]) (defn color-shade-cell [{:keys [row-path column-path]}] (let [[row-hue] row-path @@ -133,7 +136,7 @@ (def lookup-table [["🚓" "🛵" "🚲" "🛻" "🚚"] ["🍐" "🍎" "🍌" "🥝" "🍇"] - ["🐈" "🐕" "🐟" "🐎" "🧸"]]) + ["🐕" "🐎" "🧸" "🐈" "🐟"]]) (def add {:operator + :label "Addition"}) (def multiply {:operator * :label "Multiplication"}) @@ -157,96 +160,91 @@ [title3 "Cells are Functions"] [p "Each cell is a " [:i "function"] " of its grid position."] [nested-grid - :columns [:a :b :c] - :rows [:x :y :z] + :columns ["a" "b" "c"] + :rows ["x" "y" "z"] :column-width 40 :column-header-height 25 :row-header-width 30 - :cell (fn [{:keys [column-path row-path]}] (pr-str (concat column-path row-path)))] + :cell (fn [{:keys [column-path row-path]}] + (str "(" (last column-path) + ", " (last row-path) ")"))] [title3 "Headers are Nested"] - [p "You can declare a tree of nested header values. "] + [p "You can declare headers as a nested " [:i "configuration."]] [nested-grid :columns [:a [:a1 :a2] :b [:b1 :b2]] :rows [:x [:x1 :x2] :y [:y1 :y2]] :column-header-height 25 :row-header-width 30 :column-width 90 - :cell (fn [{:keys [column-path row-path]}] - (pr-str (list column-path row-path)))] - [p [:code ":columns"] " is a tree of nested " [:code "column"] " values. For instance: "] - [:pre ":columns [:a [:a1 :a2] :b [:b1 :b2]] -:rows [:x [:x1 :x2] :y [:y1 :y2]]"] - [p "That means each vertical partition you see is defined by a " [:code ":column-path"] - "(not simply a " [:code "column"] "). " + :cell (comp str seq (juxt :column-path :row-path))] + [p "Each vertical partition you see is defined by a " [:code ":column-path"] "." "For instance, " [:code "[:a :a1]"] " is the first " [:code ":column-path"] "."] [p "Same goes for rows. For instance, " [:code "[:y :y2]"] " is the last " [:code ":row-path"] "."] - [title3 "Headers can be Richly Declarative"] - - [p "A " [:code ":column-path"] " is a vector of " [:code "column"] " values."] - - [p "Anything can be a " [:code "column"] " value, " - [:i "except"] " a " [:code "list"] " or " [:code "vector"] " (those express nesting)."] - - [nested-grid - :columns [add [one two] - multiply [one two] - lookup [one two]] - :rows [three four] - :row-header (comp :label last :row-path) - :column-header (comp :label last :column-path) - :row-header 20 - :column-header-height 25 - :row-header-width 100 - :parts {:cell-wrapper {:style {:text-align "center"}}} - :cell (fn [{:keys [column-path row-path]}] - (let [{:keys [operator left right]} (->> (into row-path column-path) - (apply merge))] - (operator left right)))] - [:pre "(def lookup-table [[\"🚓\" \"🛵\" \"🚲\" \"🛻\" \"🚚\"] - [\"🍐\" \"🍎\" \"🍌\" \"🥝\" \"🍇\"] - [\"🐈\" \"🐕\" \"🐟\" \"🐎\" \"🧸\"]]) -(def add {:operator + :label \"Addition\"}) -(def multiply {:operator * :label \"Multiplication\"}) -(def lookup {:operator (fn [l r] (get-in lookup-table [l r])) - :label \"Lookup\"}) -(def one {:left 1 :label \"1\"}) -(def two {:left 2 :label \"2\"}) -(def three {:right 3 :label \" 3 \"}) -(def four {:right 4 :label \" 4 \"}) - -[nested-grid - :columns [add [one two] - multiply [one two] - lookup [one two]] - :rows [three four] - :column-header (comp :label last :column-path) - :row-header (comp :label last :row-path) - :cell (fn [{:keys [column-path row-path]}] - (let [{:keys [operator left right]} (->> column-path - (into row-path) - (apply merge))] - (operator left right)))]"] - [title3 "Header Cells are Functions"] + [title3 "Cells are Views of Header Paths"] + [p "Each cell is a function of its location."] + [h-box + :gap "25px" + :children + [[nested-grid + :columns [:a :b :c] + :rows [:x [:x1 :x2] + :y [:y1 :y2]] + :column-width 70 + :column-header-height 25 + :row-header-width 30 + :cell (fn [{:keys [column-path row-path]}] + [:i (str (list column-path row-path))])] + [v-box + :children + [[gap :size "20px"] + [:pre "[nested-grid + :columns [:a :b :c] + :rows [:x [:x1 :x2] + :y [:y1 :y2]] + :cell + (fn [{:keys [column-path row-path]}] + [:i (str (list column-path row-path))])]"]]]]] + [p "Specifically, the " [:code ":cell"] " prop accepts a function " + "of two keyword arguments: " [:code ":column-path"] " and " [:code ":row-path"] "."] + [p "The function counts as a " + [:a {:href "https://github.com/reagent-project/reagent/blob/master/doc/CreatingReagentComponents.md"} + "reagent component"] ", returning either a string or a hiccup."] + [title3 "Header Cells are Views, Too"] [p "Just like " [:code ":cell"] ", the " [:code ":column-header"] " and " [:code ":row-header"] " props " - "are functions of paths."] + "are functions of their location."] [p "The difference is, a " [:code ":column-header"] " only has a " [:code ":column-path"] " and a " [:code ":row-header"] " only has a " [:code ":row-path"] "."] + [title3 "Headers are Richly Declarative"] + [p "A " [:code ":column-path"] " is a vector of " [:i "header values."]] + [p "Anything can be a " [:i "header value"] ", " + [:i "except"] " a " [:code "list"] " or " [:code "vector"] " (those express " [:i "configuration"] ")."] + [p "So far, we've looked at simple " [:i "header values"] ", like " [:code ":a"] " or " [:code "\"blue\""] "."] + [p "Another common use-case is a map, like " [:code "{:id :a :label \"A\" :type :letter}"] "." + "We consider a value like this to be a " [:i "header spec"] "."] [title3 "Nested-grid + Domain Logic = Pivot Table"] - [:i {:style {:max-width "400px"}} "A pivot table is a table of values which are aggregations of groups of individual values from a more extensive table... within one or more discrete categories. (" [:a {:href "https://en.wikipedia.org/wiki/Pivot_table"} "Wikipedia"] ")"] + [:i {:style {:max-width "400px"}} + "A pivot table is a table of values which are aggregations of groups of individual values from a more extensive table..." + "within one or more discrete categories. (" [:a {:href "https://en.wikipedia.org/wiki/Pivot_table"} "Wikipedia"] ")"] [:br] [p "The pivot table is our driving use-case. " "By separating UI presentation from data presentation, we hope " [:code "nested-grid"] " makes it simple to build robust and flexible pivot tables."] - [p "In particular, your " [:code ":cell"] " function can close over concepts like " [:i "aggregations, groups, categories, dimensions, measures,"] " etc."] - [p "In the example above, " [:code "lookup-table"] "demonstrates the concept of " - [:i "a more extensive table."] - " Your " [:code ":columns"] " and " [:code ":rows"] - " can declare the necessary domain concepts, and your " [:code ":cell"] " function can dispatch on each concept, " - "accessing various " [:i "aggregations"] " and " [:i "groupings"] " of that table."] + [p "In " [:strong "Demo #3: Header Specifications"] ", " [:code "lookup-table"] "declares " + [:i "\"a more extensive table,\""] + " and the " [:code "lookup"] [:i "column spec"] " declares how to use that table."] + [p + "More generally:" [:br] + [:ul + [:li "Your " [:code ":columns"] " and " [:code ":rows"] + " declare the necessary domain concepts, such as " + [:i "\"aggregations\""] " and " [:i "\"groups.\""]] + [:li "Your " [:code ":cell"] " function dispatches on each concept, " + "deriving these " [:i "\"aggregations\""] " and " [:i "\"groups\""] " from " + [:i "\"a more extensive table.\""]]]] [p "We also envision building an interactive, configurable pivot table. " - "By changing the value of " [:code ":columns"] " and " [:code ":rows"] ", you can reconfigure the UI presentation, and " - "your data presentation can simply follow along. " - "This can be done either programmatically or via a dedicated user interface."]]]) + "By changing " [:code ":columns"] " and " [:code ":rows"] ", you could reconfigure the UI presentation, and " + "your data presentation would simply follow along. " + "This could be done either programmatically or via a dedicated user interface."]]]) ;; core holds a reference to panel, so need one level of indirection to get figwheel updates (defn panel @@ -285,7 +283,7 @@ [v-box :src (at) :children - [[title2 "Demo #1"] + [[title2 "Demo #1: Flat Headers"] [gap :src (at) :size "15px"] @@ -298,7 +296,7 @@ :src (at) :size "40px"] [line :src (at)] - [title2 "Demo #2"] + [title2 "Demo #2: Nested Headers"] [gap :src (at) :size "15px"] @@ -306,5 +304,50 @@ [source-reference "for above nested-grid" "src/re_demo/nested_grid.cljs"] - [color-shade-explainer]]]]] + [color-shade-explainer] + [title2 "Demo #3: Header Specifications"] + [nested-grid + :columns [add [one two] + multiply [one two] + lookup [one two]] + :rows [three four] + :row-header (comp :label last :row-path) + :column-header (comp :label last :column-path) + :row-header 20 + :column-header-height 25 + :row-header-width 100 + :parts {:cell-wrapper {:style {:text-align "center"}}} + :cell (fn [{:keys [column-path row-path]}] + (let [{:keys [operator left right]} (->> (into row-path column-path) + (apply merge))] + (operator left right)))] + [source-reference + "for above nested-grid" + "src/re_demo/nested_grid.cljs"] + [p "Here, we use " [:i "header specs"] " like " [:code "multiply"] + " and " [:code "lookup"] " to build a multi-modal view of the source data."] + [:pre "(def lookup-table [[\"🚓\" \"🛵\" \"🚲\" \"🛻\" \"🚚\"] + [\"🍐\" \"🍎\" \"🍌\" \"🥝\" \"🍇\"] + [\"🐕\" \"🐎\" \"🧸\" \"🐈\" \"🐟\"]]) +(def add {:label \"Addition\" :operator +}) +(def multiply {:label \"Multiplication\" :operator *}) +(def lookup {:label \"Lookup\" + :operator (fn [l r] (get-in lookup-table [l r]))}) +(def one {:label \"1\" :left 1}) +(def two {:label \"2\" :left 2}) +(def three {:label \"3\" :right 3}) +(def four {:label \"4\" :right 4}) + +[nested-grid + :columns [add [one two] + multiply [one two] + lookup [one two]] + :rows [three four] + :column-header (comp :label last :column-path) + :row-header (comp :label last :row-path) + :cell (fn [{:keys [column-path row-path]}] + (let [{:keys [operator left right]} (->> column-path + (into row-path) + (apply merge))] + (operator left right)))]"]]]]] #_[parts-table "nested-grid" nested-grid-grid-parts-desc]]])))