Skip to content

Commit

Permalink
nav, popover, badge
Browse files Browse the repository at this point in the history
  • Loading branch information
sritchie committed Aug 13, 2014
1 parent 77a1ee4 commit f8c498a
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 46 deletions.
18 changes: 7 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,41 +25,37 @@ Here's the latest version:
* Grid, Row, Col (grid.cljs)
* ToolTip (random.cljs, except for examples)
* Alert (random.cljs, except for examples)
* Nav, NavItem (nav.cljs)
* Popover (random.cljs)
* Badge (random.cljs)

## In Progress

* Modal, ModalTrigger (modal.cljs)
* ProgressBar (progress-bar.cljs)
* Nav Collapsible Functionality

## Needed Components

* DropdownButton, SplitButton, MenuItem
* DropdownMenu (?)
* Subnav (?)
* Panel (hard), PanelGroup (easy), Accordion (easy)
* Popover (easy)
* Nav, NavItem, NavBar
* NavBar
* TabbedArea, TabPane
* Pager
* Carousel
* CarouselItem
* Badge
* Use Input as a wrapper (easy):

```
If type is not set, child element(s) will be rendered instead of an input
element.getValue() will not work when used this way.
```

## Mixins

* Fade
* Overlay
* Listener (mixins.cljs)
* Timeout (mixins.cljs)

### Needed Mixins

* Fade
* Overlay
* DropdownStateMixin
* CollapsibleMixin

Expand Down
56 changes: 44 additions & 12 deletions docs/src/cljs/om_bootstrap/docs.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@
[om-bootstrap.random :as r]
[om-tools.core :refer-macros [defcomponent defcomponentk]]
[om-tools.dom :as d :include-macros true]
[schema.core :as s]
[secretary.core :as route :include-macros true :refer [defroute]]
[weasel.repl :as ws-repl])
(:require-macros [cljs.core.async.macros :refer [go-loop]])
(:require-macros [cljs.core.async.macros :refer [go-loop]]
[schema.macros :as sm])
(:import [goog.history EventType Html5History]))

;; ## Button Examples
Expand Down Expand Up @@ -227,17 +229,47 @@
;; Fill in.
)

;; ## Popovers

(def popover-example
(d/div {:class "bs-example"}
(d/div {:style {:height 120}}
(r/popover {:placement "right"
:position-left 200
:position-top 50
:title "Popover right"}
"And here's some "
(d/strong "amazing")
" content. It's very engaging. Right?"))))

;; ## Navs

(def nav-example
(let [on-select (fn [k _] (js/alert (str "Selected " k)))
example (fn [style]
(n/nav {:bs-style style}
(n/nav-item {:key 1 :href "/home" :active? true
:on-select on-select}
"nav-item 1 content")
(n/nav-item {:key 2 :href "/home"
:on-select on-select}
"nav-item 2 content")
(n/nav-item {:key 3 :href "/home" :disabled? true
:on-select on-select}
"nav-item 3 content")))]
(d/div
(d/p "Navs come in two styles, pills:")
(example "pills")
(d/p "And tabs:")
(example "tabs"))))

;; ## Badges

(def badge-example
(d/div
(d/p "Navs come in two styles, pills and tabs.")
(n/nav {:bs-style "pills"
:on-select (fn [k _]
(js/alert (str "Selected" k)))}
(n/nav-item {:key 1 :href "/home"} "nav-item 1 content")
(n/nav-item {:key 2 :href "/home"} "nav-item 1 content")
(n/nav-item {:key 3 :href "/home" :disabled? true} "nav-item 1 content"))))
(d/p "Easily highlight new or unread items by adding a <Badge> to links, Bootstrap navs, and more.")
(d/div {:class "bs-example grids-examples"}
(d/p "Badges" (r/badge {} 42)))))

;; ## Final Page Loading

Expand Down Expand Up @@ -266,12 +298,12 @@
(d/h3 "Positioned Tooltip (in progress)")
(d/h3 "Alert")
alert-example
(d/h3 "Popover")
popover-example
(d/h3 "Nav")
nav-example
(i/input
{:type "text" :addon-before "$"
:help "Label before the input field."})
)))
(d/h3 "Badges")
badge-example)))

(defonce app-state
(atom {:text "Hi!"}))
Expand Down
43 changes: 26 additions & 17 deletions src/om_bootstrap/nav.cljs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
(ns om-bootstrap.nav
(:require [om-bootstrap.types :as t]
(:require [clojure.string :as st]
[om.core :as om]
[om-bootstrap.types :as t]
[om-bootstrap.util :as u]
[om-tools.dom :as d :include-macros true]
[schema.core :as s])
Expand All @@ -15,12 +17,14 @@

;; TODO: Why can't I put a ref inside the anchor tag without throwing
;; an invariant violation?

(sm/defn nav-item
"Generates a nav item for use inside of a nav element. It LOOKS like
Key might be a required thing here."
[opts :- NavItem & children]
(let [[bs props] (t/separate NavItem opts {:href "#"})
classes {:active (:active? bs)}
classes {:active (:active? bs)
:disabled (:disabled? bs)}
handle-click (fn [e]
(when-let [f (:on-select bs)]
(.preventDefault e)
Expand All @@ -35,22 +39,27 @@

(def Nav
{:bs-style (s/enum "tabs" "pills")
:on-select (sm/=> s/Any s/Any)
:stacked? s/Bool
:justified? s/Bool
:collapsible? s/Bool
:expanded? s/Bool
:navbar? s/Bool
:pull-right? s/Bool})
(s/optional-key :stacked?) s/Bool
(s/optional-key :justified?) s/Bool
(s/optional-key :collapsible?) s/Bool
(s/optional-key :expanded?) s/Bool
(s/optional-key :navbar?) s/Bool
(s/optional-key :pull-right?) s/Bool})

;; NOTE: Compared to the nav in react-bootstrap, this nav doesn't
;; handle setting the navItem property on child dropdown buttons, or
;; work on the activeHref or activeKey property.
;;
;; We're also missing support for the :on-select property, because I
;; can't mutate and propagate the properties on down.
(sm/defn nav
[opts :- Nav & children]
(let [[bs opts] (t/separate Nav opts {:bs-class "nav"})]
(d/nav {:class (d/class-set (t/bs-class-set bs))}
(d/ul {:ref "ul"
:class (d/class-set
{:nav-stacked (:stacked? bs)
:nav-justified (:justified? bs)
:navbar-nav (:navbar? bs)
:pull-right (:pull-right? bs)})})
children)))
(d/nav
(d/ul {:class (d/class-set
(merge (t/bs-class-set bs)
{:nav-stacked (:stacked? bs)
:nav-justified (:justified? bs)
:navbar-nav (:navbar? bs)
:pull-right (:pull-right? bs)}))}
children))))
45 changes: 45 additions & 0 deletions src/om_bootstrap/random.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,48 @@
(om/build alert* {:bs bs
:props props
:children children})))

;; ## Popover

(def Popover
(t/bootstrap
{(s/optional-key :title) t/Renderable
(s/optional-key :placement) Placement
(s/optional-key :position-left) s/Int
(s/optional-key :position-top) s/Int
(s/optional-key :arrow-offset-left) s/Int
(s/optional-key :arrow-offset-top) s/Int}))

;; TODO: Abstract out shared style generation between here and
;; tooltip.
(sm/defn popover :- t/Component
[opts :- Popover & children]
(let [[bs _] (t/separate Popover opts {:placement "right"})
classes {:popover true
(:placement bs) true
:in (or (:position-left bs)
(:position-top bs))}]
(d/div {:class (d/class-set classes)
:style {:left (:position-left bs)
:top (:position-top bs)
:display "block"}}
(d/div {:class "arrow"
:style {:left (:arrow-offset-left bs)
:top (:arrow-offset-top bs)}})
(when-let [title (:title bs)]
(d/h3 {:class "popover-title"} title))
(d/div {:class "popover-content"}
children))))

;; ## Badge

(def Badge
{(s/optional-key :pull-right?) s/Bool})

(sm/defn badge :- t/Component
[opts :- Badge & children]
(let [[bs _] (t/separate Badge opts)
classes {:pull-right (:pull-right? bs)
:badge (u/some-valid-component? children)}]
(d/span (u/merge-props props {:class (d/class-set classes)})
children)))
7 changes: 4 additions & 3 deletions src/om_bootstrap/types.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"link" "link"
"inline" "inline"
"tabs" "tabs"
"pills" "pill"})
"pills" "pills"})

(def size-map
{"large" "lg"
Expand Down Expand Up @@ -96,8 +96,9 @@
;; Separate follows the best practices set out here:
;; https://gist.github.com/sebmarkbage/a6e220b7097eb3c79ab7

(sm/defn separate :- (s/pair {s/Any s/Any} "om-bootstrap options."
{s/Any s/Any} "all other props.")
(sm/defn separate :- (s/pair
{s/Any s/Any} "om-bootstrap options."
{s/Any s/Any} "all other props.")
"Returns two maps; the first is all of the schema options, the
second is the REST of the options."
([schema opts]
Expand Down
30 changes: 27 additions & 3 deletions src/om_bootstrap/util.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,26 @@
;;
;; Some of these are rewritten from various React addons.

(sm/defn some-valid-component? :- s/Bool
(sm/defn valid-component? :- s/Bool
"Returns true if the supplied argument is a valid React component,
false otherwise."
[child]
(or (string? child)
(number? child)
(.isValidComponent js/React child)))

(sm/defn some-valid-component? :- s/Bool
"Returns true if the supplied sequence contains some valid React component,
false otherwise."
[children]
(boolean
(some #(.isValidComponent js/React %) children)))
(boolean (some valid-component? children)))

(defn map-valid-components
"Only applies the supplied function to valid components. Leaves the
rest undisturbed."
[f xs]
(map (fn [x] (if (valid-component? x) (f x) x))
xs))

(def react-merges
"Map of React keyword to a custom function for its merge."
Expand Down Expand Up @@ -75,3 +89,13 @@
(when-let [children (:children props)]
{:children children}))]
(.constructor child (clj->js new-props))))))

(defn chain-fns
"Generates a new function that calls each supplied side-effecting
function."
[l r]
(if (and l r)
(fn [& args]
(apply l args)
(apply r args))
(or l r)))
8 changes: 8 additions & 0 deletions test/om_bootstrap/util_test.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,11 @@
{:face "cake" :className "first second" :cake "face"})
"When properties merge, they normalize :class -> :className and
properly merge classes."))


(deftest valid-component-test
(is (true? (valid-component? 1)))
(is (true? (valid-component? "string")))
(is (false? (valid-component? null)))
(is (true? (some-valid-component? [1 2 null])))
(is (false? (some-valid-component? [null null]))))

0 comments on commit f8c498a

Please sign in to comment.