(ns re-com.alert (:require-macros [re-com.core :refer [handler-fn]]) (:require [re-com.util :refer [deref-or-value]] [re-com.buttons :refer [button]] [re-com.box :refer [h-box v-box box scroller border flex-child-style]] [re-com.validate :refer [string-or-hiccup? alert-type? alert-types-list vector-of-maps? css-style? html-attr?] :refer-macros [validate-args-macro]])) ;;-------------------------------------------------------------------------------------------------- ;; Component: alert ;;-------------------------------------------------------------------------------------------------- (def alert-box-args-desc [{:name :id :required false :type "anything" :description [:span "a unique identifier, usually an integer or string."]} {:name :alert-type :required false :default :info :type "keyword" :validate-fn alert-type? :description [:span "one of " alert-types-list]} {:name :heading :required false :type "string | hiccup" :validate-fn string-or-hiccup? :description [:span "displayed as a larger heading. One of " [:code ":heading"] " or " [:code ":body"] " should be provided"]} {:name :body :required false :type "string | hiccup" :validate-fn string-or-hiccup? :description "displayed within the body of the alert"} {:name :padding :required false :default "15px" :type "string" :validate-fn string? :description "padding surounding the alert"} {:name :closeable? :required false :default false :type "boolean" :description [:span "if true, render a close button. " [:code ":on-close"] " should be supplied"]} {:name :on-close :required false :type ":id -> nil" :validate-fn fn? :description [:span "called when the user clicks the close 'X' button. Passed the " [:code ":id"] " of the alert to close"]} {:name :class :required false :type "string" :validate-fn string? :description "CSS classes (whitespace separated). Applied to outer container"} {:name :style :required false :type "CSS style map" :validate-fn css-style? :description "CSS styles. Applied to outer container"} {:name :attr :required false :type "HTML attr map" :validate-fn html-attr? :description [:span "HTML attributes, like " [:code ":on-mouse-move"] [:br] "No " [:code ":class"] " or " [:code ":style"] "allowed. Applied to outer container"]}]) (defn alert-box "Displays one alert box. A close button allows the message to be removed" [& {:keys [id alert-type heading body padding closeable? on-close class style attr] :or {alert-type :info} :as args}] {:pre [(validate-args-macro alert-box-args-desc args "alert-box")]} (let [close-button [button :label [:i {:class "zmdi created zmdi-hc-fw-rc zmdi-close" :style {:font-size "20px"}}] ;"×" :on-click (handler-fn (on-close id)) :class "close"] alert-class (alert-type {:none "" :info "alert-success" :warning "alert-warning" :danger "alert-danger"})] [:div (merge {:class (str "rc-alert alert fade in " alert-class " " class) :style (merge (flex-child-style "none") {:padding (when padding padding)} style)} attr) (when heading [h-box :justify :between :align :center :style {:margin-bottom (if body "10px" "0px")} :children [[:h4 {:style {:margin-bottom "0px"}} ;; Override h4 heading] (when (and closeable? on-close) close-button)]]) (when body [h-box :justify :between :align :center :children [[:div body] (when (and (not heading) closeable? on-close) close-button)]])])) ;;-------------------------------------------------------------------------------------------------- ;; Component: alert-list ;;-------------------------------------------------------------------------------------------------- (def alert-list-args-desc [{:name :alerts :required true :type "vector of maps | atom" :validate-fn vector-of-maps? :description "alerts to render (in the order supplied). Can also be a list of maps"} {:name :on-close :required true :type ":id -> nil" :validate-fn fn? :description [:span "called when the user clicks the close 'X' button. Passed the alert's " [:code ":id"]]} {:name :max-height :required false :type "string" :validate-fn string? :description "CSS style for maximum list height. By default, it grows forever"} {:name :padding :required false :default "4px" :type "string" :validate-fn string? :description "CSS padding within the alert"} {:name :border-style :required false :default "1px solid lightgrey" :type "string" :validate-fn string? :description "CSS border style surrounding the list"} {:name :class :required false :type "string" :validate-fn string? :description "CSS class names, space separated. Applied to outer container"} {:name :style :required false :type "CSS style map" :validate-fn css-style? :description "CSS styles. Applied to outer container"} {:name :attr :required false :type "HTML attr map" :validate-fn html-attr? :description [:span "HTML attributes, like " [:code ":on-mouse-move"] [:br] "No " [:code ":class"] " or " [:code ":style"] "allowed. Applied to outer container"]}]) (defn alert-list "Displays a list of alert-box components in a v-box. Sample alerts object: [{:id 2 :alert-type :warning :heading \"Heading\" :body \"Body\" :padding \"8px\" :closeable? true} {:id 1 :alert-type :info :heading \"Heading\" :body \"Body\"}]" [& {:keys [alerts on-close max-height padding border-style class style attr] :or {padding "4px"} :as args}] {:pre [(validate-args-macro alert-list-args-desc args "alert-list")]} (let [alerts (deref-or-value alerts)] [box :child [border :padding padding :border border-style :class class :style style :attr attr :child [scroller :v-scroll :auto :style {:max-height max-height} :child [v-box :size "auto" :children [(for [alert alerts] (let [{:keys [id alert-type heading body padding closeable?]} alert] ^{:key id} [alert-box :id id :alert-type alert-type :heading heading :body body :padding padding :closeable? closeable? :on-close on-close]))]]]]]))