(ns re-com.box (:require [clojure.string :as string] [re-com.validate :refer [justify-style? justify-options-list align-style? align-options-list scroll-style? scroll-options-list string-or-hiccup? css-style? html-attr?] :refer-macros [validate-args-macro]])) (def debug false) ;; ------------------------------------------------------------------------------------ ;; Private Helper functions ;; ------------------------------------------------------------------------------------ (defn flex-child-style "Determines the value for the 'flex' attribute (which has grow, shrink and basis), based on the :size parameter. IMPORTANT: The term 'size' means width of the item in the case of flex-direction 'row' OR height of the item in the case of flex-direction 'column'. Flex property explanation: - grow Integer ratio (used with other siblings) to determined how a flex item grows it's size if there is extra space to distribute. 0 for no growing. - shrink Integer ratio (used with other siblings) to determined how a flex item shrinks it's size if space needs to be removed. 0 for no shrinking. - basis Initial size (width, actually) of item before any growing or shrinking. Can be any size value, e.g. 60%, 100px, auto Note: auto will cause the initial size to be calculated to take up as much space as possible, in conjunction with it's siblings :flex settings. Supported values: - initial '0 1 auto' - Use item's width/height for dimensions (or content dimensions if w/h not specifed). Never grow. Shrink (to min-size) if necessary. Good for creating boxes with fixed maximum size, but that can shrink to a fixed smaller size (min-width/height) if space becomes tight. NOTE: When using initial, you should also set a width/height value (depending on flex-direction) to specify it's default size and an optional min-width/height value to specify the size it can shrink to. - auto '1 1 auto' - Use item's width/height for dimensions. Grow if necessary. Shrink (to min-size) if necessary. Good for creating really flexible boxes that will gobble as much available space as they are allowed or shrink as much as they are forced to. - none '0 0 auto' - Use item's width/height for dimensions (or content dimensions if not specifed). Never grow. Never shrink. Good for creating rigid boxes that stick to their width/height if specified, otherwise their content size. - 100px '0 0 100px' - Non flexible 100px size (in the flex direction) box. Good for fixed headers/footers and side bars of an exact size. - 60% '60 1 0px' - Set the item's size (it's width/height depending on flex-direction) to be 60% of the parent container's width/height. NOTE: If you use this, then all siblings with percentage values must add up to 100%. - 60 '60 1 0px' - Same as percentage above. - grow shrink basis 'grow shrink basis' - If none of the above common valaues above meet your needs, this gives you precise control. If number of words is not 1 or 3, an exception is thrown. Reference: http://www.w3.org/TR/css3-flexbox/#flexibility Diagram: http://www.w3.org/TR/css3-flexbox/#flex-container Regex101 testing: ^(initial|auto|none)|(\\d+)(px|%|em)|(\\d+)\\w(\\d+)\\w(.*) - remove double backslashes" [size] ;; TODO: Could make initial/auto/none into keywords??? (let [split-size (string/split (string/trim size) #"\s+") ;; Split into words separated by whitespace split-count (count split-size) _ (assert (contains? #{1 3} split-count) "Must pass either 1 or 3 words to flex-child-style") size-only (when (= split-count 1) (first split-size)) ;; Contains value when only one word passed (e.g. auto, 60px) split-size-only (when size-only (string/split size-only #"(\d+)(.*)")) ;; Split into number + string [_ num units] (when size-only split-size-only) ;; grab number and units pass-through? (nil? num) ;; If we can't split, then we'll pass this straign through grow-ratio? (or (= units "%") (= units "") (nil? units)) ;; Determine case for using grow ratio grow (if grow-ratio? num "0") ;; Set grow based on percent or integer, otherwise no grow shrink (if grow-ratio? "1" "0") ;; If grow set, then set shrink to even shrinkage as well basis (if grow-ratio? "0px" size) ;; If grow set, then even growing, otherwise set basis size to the passed in size (e.g. 100px, 5em) flex (if (and size-only (not pass-through?)) (str grow " " shrink " " basis) size)] {:-webkit-flex flex :flex flex})) (defn flex-flow-style "A cross-browser helper function to output flex-flow with all it's potential browser prefixes" [flex-flow] {:-webkit-flex-flow flex-flow :flex-flow flex-flow}) (defn justify-style "Determines the value for the flex 'justify-content' attribute. This parameter determines how children are aligned along the main axis. The justify parameter is a keyword. Reference: http://www.w3.org/TR/css3-flexbox/#justify-content-property" [justify] (let [js (case justify :start "flex-start" :end "flex-end" :center "center" :between "space-between" :around "space-around")] {:-webkit-justify-content js :justify-content js})) (defn align-style "Determines the value for the flex align type attributes. This parameter determines how children are aligned on the cross axis. The justify parameter is a keyword. Reference: http://www.w3.org/TR/css3-flexbox/#align-items-property" [attribute align] (let [attribute-wk (->> attribute name (str "-webkit-") keyword) as (case align :start "flex-start" :end "flex-end" :center "center" :baseline "baseline" :stretch "stretch")] {attribute-wk as attribute as})) (defn scroll-style "Determines the value for the 'overflow' attribute. The scroll parameter is a keyword. Because we're translating scroll into overflow, the keyword doesn't appear to match the attribute value" [attribute scroll] {attribute (case scroll :auto "auto" :off "hidden" :on "scroll" :spill "visible")}) ;; ------------------------------------------------------------------------------------ ;; Private Component: box-base (debug color: lightblue) ;; ------------------------------------------------------------------------------------ (defn- box-base "This should generally NOT be used as it is the basis for the box, scroller and border components" [& {:keys [size scroll h-scroll v-scroll width height min-width min-height max-width max-height justify align align-self margin padding border l-border r-border t-border b-border radius bk-color child class-name class style attr]}] (let [s (merge (flex-flow-style "inherit") (flex-child-style size) (when scroll (scroll-style :overflow scroll)) (when h-scroll (scroll-style :overflow-x h-scroll)) (when v-scroll (scroll-style :overflow-y v-scroll)) (when width {:width width}) (when height {:height height}) (when min-width {:min-width min-width}) (when min-height {:min-height min-height}) (when max-width {:max-width max-width}) (when max-height {:max-height max-height}) (when justify (justify-style justify)) (when align (align-style :align-items align)) (when align-self (align-style :align-self align-self)) (when margin {:margin margin}) ;; margin and padding: "all" OR "top&bottom right&left" OR "top right bottom left" (when padding {:padding padding}) (when border {:border border}) (when l-border {:border-left l-border}) (when r-border {:border-right r-border}) (when t-border {:border-top t-border}) (when b-border {:border-bottom b-border}) (when radius {:border-radius radius}) (if bk-color {:background-color bk-color} (if debug {:background-color "lightblue"} {})) style)] [:div (merge {:class (str class-name "display-flex " class) :style s} attr) child])) ;; ------------------------------------------------------------------------------------ ;; Component: gap (debug color: chocolate) ;; ------------------------------------------------------------------------------------ (def gap-args-desc [{:name :size :required true :type "string" :validate-fn string? :description "the length of the whitespace. Typically, an absolute CSS length like 10px or 10em, but can be a stretchy proportional amount like 2"} {:name :width :required false :type "string" :validate-fn string? :description "a CSS width style"} {:name :height :required false :type "string" :validate-fn string? :description "a CSS height style"} {:name :class :required false :type "string" :validate-fn string? :description "CSS class names, space separated"} {:name :style :required false :type "CSS style map" :validate-fn css-style? :description "CSS styles to add or override"} {: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"]}]) (defn gap "Returns a component which produces a gap between children in a v-box/h-box along the main axis" [& {:keys [size width height class style attr] :as args}] {:pre [(validate-args-macro gap-args-desc args "gap")]} (let [s (merge (when size (flex-child-style size)) (when width {:width width}) (when height {:height height}) (when debug {:background-color "chocolate"}) style)] [:div (merge {:class (str "rc-gap " class) :style s} attr)])) ;; ------------------------------------------------------------------------------------ ;; Component: line ;; ------------------------------------------------------------------------------------ (def line-args-desc [{:name :size :required false :default "1px" :type "string" :validate-fn string? :description "a CSS style for the thickness of the line. Usually px, % or em"} {:name :color :required false :default "lightgray" :type "string" :validate-fn string? :description "a CSS color"} {:name :class :required false :type "string" :validate-fn string? :description "CSS class names, space separated"} {:name :style :required false :type "CSS style map" :validate-fn css-style? :description "CSS styles to add or override"} {: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"]}]) (defn line "Returns a component which produces a line between children in a v-box/h-box along the main axis. Specify size in pixels and a stancard CSS color. Defaults to a 1px lightgray line" [& {:keys [size color class style attr] :or {size "1px" color "lightgray"} :as args}] {:pre [(validate-args-macro line-args-desc args "line")]} (let [s (merge (flex-child-style (str "0 0 " size)) {:background-color color} style)] [:div (merge {:class (str "rc-line " class) :style s} attr)])) ;; ------------------------------------------------------------------------------------ ;; Component: h-box (debug color: gold) ;; ------------------------------------------------------------------------------------ (def h-box-args-desc [{:name :children :required true :type "vector" :validate-fn sequential? :description "a vector (or list) of components"} {:name :size :required false :default "none" :type "string" :validate-fn string? :description [:span "equivalent to CSS style " [:span.bold "flex"] "." [:br] "Examples: " [:code "initial"] ", " [:code "auto"] ", " [:code "none"]", " [:code "100px"] ", " [:code "2"] " or a generic triple of " [:code "grow shrink basis"]]} {:name :width :required false :type "string" :validate-fn string? :description "a CSS width style"} {:name :height :required false :type "string" :validate-fn string? :description "a CSS height style"} {:name :min-width :required false :type "string" :validate-fn string? :description "a CSS width style. The minimum width to which the box can shrink"} {:name :min-height :required false :type "string" :validate-fn string? :description "a CSS height style. The minimum height to which the box can shrink"} {:name :max-width :required false :type "string" :validate-fn string? :description "a CSS width style. The maximum width to which the box can grow"} {:name :max-height :required false :type "string" :validate-fn string? :description "a CSS height style. The maximum height to which the box can grow"} {:name :justify :required false :default :start :type "keyword" :validate-fn justify-style? :description [:span "equivalent to CSS style " [:span.bold "justify-content"] "." [:br] "One of " justify-options-list]} {:name :align :required false :default :stretch :type "keyword" :validate-fn align-style? :description [:span "equivalent to CSS style " [:span.bold "align-items"] "." [:br] " One of " align-options-list]} {:name :align-self :required false :type "keyword" :validate-fn align-style? :description [:span "equivalent to CSS style " [:span.bold "align-self"] "." [:br] "Used when a child must override the parent's align-items setting."]} {:name :margin :required false :type "string" :validate-fn string? :description "a CSS margin style"} {:name :padding :required false :type "string" :validate-fn string? :description "a CSS padding style"} {:name :gap :required false :type "string" :validate-fn string? :description "the amount of whitespace to put between each child. Typically, an absolute CSS length like 10px or 10em, but can be a stretchy proportional amount like 2"} {:name :class :required false :type "string" :validate-fn string? :description "CSS class names, space separated"} {:name :style :required false :type "CSS style map" :validate-fn css-style? :description "CSS styles to add or override"} {: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"]}]) (defn h-box "Returns hiccup which produces a horizontal box. It's primary role is to act as a container for components and lays it's children from left to right. By default, it also acts as a child under it's parent" [& {:keys [size width height min-width min-height max-width max-height justify align align-self margin padding gap children class style attr] :or {size "none" justify :start align :stretch} :as args}] {:pre [(validate-args-macro h-box-args-desc args "h-box")]} (let [s (merge (flex-flow-style "row nowrap") (flex-child-style size) (when width {:width width}) (when height {:height height}) (when min-width {:min-width min-width}) (when min-height {:min-height min-height}) (when max-width {:max-width max-width}) (when max-height {:max-height max-height}) (justify-style justify) (align-style :align-items align) (when align-self (align-style :align-self align-self)) (when margin {:margin margin}) ;; margin and padding: "all" OR "top&bottom right&left" OR "top right bottom left" (when padding {:padding padding}) (when debug {:background-color "gold"}) style) gap-form (when gap [re-com.box/gap :size gap :width gap]) ;; TODO: required to get around a Chrome bug: https://code.google.com/p/chromium/issues/detail?id=423112. Remove once fixed. children (if gap (interpose gap-form (filter identity children)) ;; filter is to remove possible nils so we don't add unwanted gaps children)] (into [:div (merge {:class (str "rc-h-box display-flex " class) :style s} attr)] children))) ;; ------------------------------------------------------------------------------------ ;; Component: v-box (debug color: antiquewhite) ;; ------------------------------------------------------------------------------------ (def v-box-args-desc [{:name :children :required true :type "vector" :validate-fn sequential? :description "a vector (or list) of components"} {:name :size :required false :default "none" :type "string" :validate-fn string? :description [:span "equivalent to CSS style " [:span.bold "flex"] "." [:br] "Examples: " [:code "initial"] ", " [:code "auto"] ", " [:code "none"]", " [:code "100px"] ", " [:code "2"] " or a generic triple of " [:code "grow shrink basis"]]} {:name :width :required false :type "string" :validate-fn string? :description "a CSS width style"} {:name :height :required false :type "string" :validate-fn string? :description "a CSS height style"} {:name :min-width :required false :type "string" :validate-fn string? :description "a CSS width style. The minimum width to which the box can shrink"} {:name :min-height :required false :type "string" :validate-fn string? :description "a CSS height style. The minimum height to which the box can shrink"} {:name :max-width :required false :type "string" :validate-fn string? :description "a CSS width style. The maximum width to which the box can grow"} {:name :max-height :required false :type "string" :validate-fn string? :description "a CSS height style. The maximum height to which the box can grow"} {:name :justify :required false :default :start :type "keyword" :validate-fn justify-style? :description [:span "equivalent to CSS style " [:span.bold "justify-content"] "." [:br] "One of " justify-options-list]} {:name :align :required false :default :stretch :type "keyword" :validate-fn align-style? :description [:span "equivalent to CSS style " [:span.bold "align-items"] "." [:br] " One of " align-options-list]} {:name :align-self :required false :type "keyword" :validate-fn align-style? :description [:span "equivalent to CSS style " [:span.bold "align-self"] "." [:br] "Used when a child must override the parent's align-items setting."]} {:name :margin :required false :type "string" :validate-fn string? :description "a CSS margin style"} {:name :padding :required false :type "string" :validate-fn string? :description "a CSS padding style"} {:name :gap :required false :type "string" :validate-fn string? :description "the amount of whitespace to put between each child. Typically, an absolute CSS length like 10px or 10em, but can be a stretchy proportional amount like 2"} {:name :class :required false :type "string" :validate-fn string? :description "CSS class names, space separated"} {:name :style :required false :type "CSS style map" :validate-fn css-style? :description "CSS styles to add or override"} {: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"]}]) (defn v-box "Returns hiccup which produces a vertical box. It's primary role is to act as a container for components and lays it's children from top to bottom. By default, it also acts as a child under it's parent" [& {:keys [size width height min-width min-height max-width max-height justify align align-self margin padding gap children class style attr] :or {size "none" justify :start align :stretch} :as args}] {:pre [(validate-args-macro v-box-args-desc args "v-box")]} (let [s (merge (flex-flow-style "column nowrap") (flex-child-style size) (when width {:width width}) (when height {:height height}) (when min-width {:min-width min-width}) (when min-height {:min-height min-height}) (when max-width {:max-width max-width}) (when max-height {:max-height max-height}) (justify-style justify) (align-style :align-items align) (when align-self (align-style :align-self align-self)) (when margin {:margin margin}) ;; margin and padding: "all" OR "top&bottom right&left" OR "top right bottom left" (when padding {:padding padding}) (when debug {:background-color "antiquewhite"}) style) gap-form (when gap [re-com.box/gap :size gap :height gap]) ;; TODO: required to get around a Chrome bug: https://code.google.com/p/chromium/issues/detail?id=423112. Remove once fixed. children (if gap (interpose gap-form (filter identity children)) ;; filter is to remove possible nils so we don't add unwanted gaps children)] (into [:div (merge {:class (str "rc-v-box display-flex " class) :style s} attr)] children))) ;; ------------------------------------------------------------------------------------ ;; Component: box ;; ------------------------------------------------------------------------------------ (def box-args-desc [{:name :child :required true :type "string | hiccup" :validate-fn string-or-hiccup? :description "a component (or string)"} {:name :size :required false :default "none" :type "string" :validate-fn string? :description [:span "equivalent to CSS style " [:span.bold "flex"] "." [:br] "Examples: " [:code "initial"] ", " [:code "auto"] ", " [:code "none"]", " [:code "100px"] ", " [:code "2"] " or a generic triple of " [:code "grow shrink basis"]]} {:name :width :required false :type "string" :validate-fn string? :description "a CSS width style"} {:name :height :required false :type "string" :validate-fn string? :description "a CSS height style"} {:name :min-width :required false :type "string" :validate-fn string? :description "a CSS width style. The minimum width to which the box can shrink"} {:name :min-height :required false :type "string" :validate-fn string? :description "a CSS height style. The minimum height to which the box can shrink"} {:name :max-width :required false :type "string" :validate-fn string? :description "a CSS width style. The maximum width to which the box can grow"} {:name :max-height :required false :type "string" :validate-fn string? :description "a CSS height style. The maximum height to which the box can grow"} {:name :justify :required false :default :start :type "keyword" :validate-fn justify-style? :description [:span "equivalent to CSS style " [:span.bold "justify-content"] "." [:br] "One of " justify-options-list]} {:name :align :required false :default :stretch :type "keyword" :validate-fn align-style? :description [:span "equivalent to CSS style " [:span.bold "align-items"] "." [:br] " One of " align-options-list]} {:name :align-self :required false :type "keyword" :validate-fn align-style? :description [:span "equivalent to CSS style " [:span.bold "align-self"] "." [:br] "Used when a child must override the parent's align-items setting."]} {:name :margin :required false :type "string" :validate-fn string? :description "a CSS margin style"} {:name :padding :required false :type "string" :validate-fn string? :description "a CSS padding style"} {:name :class :required false :type "string" :validate-fn string? :description "CSS class names, space separated"} {:name :style :required false :type "CSS style map" :validate-fn css-style? :description "CSS styles to add or override"} {: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"]}]) (defn box "Returns hiccup which produces a box, which is generally used as a child of a v-box or an h-box. By default, it also acts as a container for further child compenents, or another h-box or v-box" [& {:keys [size width height min-width min-height max-width max-height justify align align-self margin padding child class style attr] :or {size "none"} :as args}] {:pre [(validate-args-macro box-args-desc args "box")]} (box-base :size size :width width :height height :min-width min-width :min-height min-height :max-width max-width :max-height max-height :justify justify :align align :align-self align-self :margin margin :padding padding :child child :class-name "rc-box " :class class :style style :attr attr)) ;; ------------------------------------------------------------------------------------ ;; Component: scroller ;; ------------------------------------------------------------------------------------ (def scroller-args-desc [{:name :child :required true :type "string | hiccup" :validate-fn string-or-hiccup? :description "a component (or string)"} {:name :size :required false :default "auto" :type "string" :validate-fn string? :description [:span "equivalent to CSS style " [:span.bold "flex"] "." [:br] "Examples: " [:code "initial"] ", " [:code "auto"] ", " [:code "none"]", " [:code "100px"] ", " [:code "2"] " or a generic triple of " [:code "grow shrink basis"]]} {:name :scroll :required false :default "auto" :type "keyword" :validate-fn scroll-style? :description [:span "Sets both h-scroll and v-scroll at once: " [:br] [:code ":auto"] ": only show scroll bar(s) if the content is larger than the scroller" [:br] [:code ":on"] ": always show scroll bars" [:br] [:code ":off"] ": never show scroll bar(s). Content which is not in the bounds of the scroller can not be seen" [:br] [:code ":spill"] ": never show scroll bar(s). Content which is not in the bounds of the scroller spills all over the place"]} {:name :h-scroll :required false :type "keyword" :validate-fn scroll-style? :description [:span "see " [:code ":scroll"] ". Overrides that setting"]} {:name :v-scroll :required false :type "keyword" :validate-fn scroll-style? :description [:span "see " [:code ":scroll"] ". Overrides that setting"]} {:name :width :required false :type "string" :validate-fn string? :description "initial width"} {:name :height :required false :type "string" :validate-fn string? :description "initial height"} {:name :min-width :required false :type "string" :validate-fn string? :description "a CSS width style. The minimum width to which the box can shrink"} {:name :min-height :required false :type "string" :validate-fn string? :description "a CSS height style. The minimum height to which the box can shrink"} {:name :max-width :required false :type "string" :validate-fn string? :description "a CSS width style. The maximum width to which the box can grow"} {:name :max-height :required false :type "string" :validate-fn string? :description "a CSS height style. The maximum height to which the box can grow"} {:name :justify :required false :default :start :type "keyword" :validate-fn justify-style? :description [:span "equivalent to CSS style " [:span.bold "justify-content"] "." [:br] "One of " justify-options-list]} {:name :align :required false :default :stretch :type "keyword" :validate-fn align-style? :description [:span "equivalent to CSS style " [:span.bold "align-items"] "." [:br] " One of " align-options-list]} {:name :align-self :required false :type "keyword" :validate-fn align-style? :description [:span "equivalent to CSS style " [:span.bold "align-self"] "." [:br] "Used when a child must override the parent's align-items setting."]} {:name :margin :required false :type "string" :validate-fn string? :description "a CSS margin style"} {:name :padding :required false :type "string" :validate-fn string? :description "a CSS padding style"} {:name :class :required false :type "string" :validate-fn string? :description "CSS class names, space separated"} {:name :style :required false :type "CSS style map" :validate-fn css-style? :description "CSS styles to add or override"} {: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"]}]) (defn scroller "Returns hiccup which produces a scoller component. This is the way scroll bars are added to boxes, in favour of adding the scroll attributes directly to the boxes themselves. IMPORTANT: Because this component becomes the flex child in place of the component it is wrapping, you must copy the size attibutes to this componenet. There are three scroll types: - h-scroll Determines how the horizontal scroll bar will be displayed. - v-scroll Determines how the vertical scroll bar will be displayed. - scroll Sets both h-scroll and v-scroll at once. Syntax: :auto [DEFAULT] Only show scroll bar(s) if the content is larger than the scroller. :on Always show scroll bar(s). :off Never show scroll bar(s). Content which is not in the bounds of the scroller can not be seen. :spill Never show scroll bar(s). Content which is not in the bounds of the scroller spills all over the place. Note: If scroll is set, then setting h-scroll or v-scroll overrides the scroll value" [& {:keys [size scroll h-scroll v-scroll width height min-width min-height max-width max-height justify align align-self margin padding child class style attr] :or {size "auto"} :as args}] {:pre [(validate-args-macro scroller-args-desc args "scroller")]} (let [not-v-or-h (and (nil? v-scroll) (nil? h-scroll)) scroll (if (and (nil? scroll) not-v-or-h) :auto scroll)] (box-base :size size :scroll scroll :h-scroll h-scroll :v-scroll v-scroll :width width :height height :min-width min-width :min-height min-height :max-width max-width :max-height max-height :justify justify :align align :align-self align-self :margin margin :padding padding :child child :class-name "rc-scroller " :class class :style style :attr attr))) ;; ------------------------------------------------------------------------------------ ;; Component: border ;; ------------------------------------------------------------------------------------ (def border-args-desc [{:name :child :required true :type "string | hiccup" :validate-fn string-or-hiccup? :description "a component (or string)"} {:name :border :required false :default "1px solid lightgrey" :type "string" :validate-fn string? :description "a CSS border style. A convenience to describe all borders in one parameter"} {:name :l-border :required false :type "string" :validate-fn string? :description [:span "a CSS border style for the left border. Overrides " [:code ":border"]]} {:name :r-border :required false :type "string" :validate-fn string? :description [:span "a CSS border style for the right border. Overrides " [:code ":border"]]} {:name :t-border :required false :type "string" :validate-fn string? :description [:span "a CSS border style for the top border. Overrides " [:code ":border"]]} {:name :b-border :required false :type "string" :validate-fn string? :description [:span "a CSS border style for the bottom. Overrides " [:code ":border"]]} {:name :radius :required false :type "string" :validate-fn string? :description "a CSS radius style eg.\"2px\""} {:name :size :required false :default "none" :type "string" :validate-fn string? :description [:span "equivalent to CSS style " [:span.bold "flex"] "." [:br] "Examples: " [:code "initial"] ", " [:code "auto"] ", " [:code "none"]", " [:code "100px"] ", " [:code "2"] " or a generic triple of " [:code "grow shrink basis"]]} {:name :width :required false :type "string" :validate-fn string? :description "a CSS style describing the initial width"} {:name :height :required false :type "string" :validate-fn string? :description "a CSS style describing the initial height"} {:name :min-width :required false :type "string" :validate-fn string? :description "a CSS width style. The minimum width to which the box can shrink"} {:name :min-height :required false :type "string" :validate-fn string? :description "a CSS height style. The minimum height to which the box can shrink"} {:name :max-width :required false :type "string" :validate-fn string? :description "a CSS width style. The maximum width to which the box can grow"} {:name :max-height :required false :type "string" :validate-fn string? :description "a CSS height style. The maximum height to which the box can grow"} {:name :margin :required false :type "string" :validate-fn string? :description "a CSS margin style"} {:name :padding :required false :type "string" :validate-fn string? :description "a CSS padding style"} {:name :class :required false :type "string" :validate-fn string? :description "CSS class names, space separated"} {:name :style :required false :type "CSS style map" :validate-fn css-style? :description "CSS styles to add or override"} {: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"]}]) (defn border "Returns hiccup which produces a border component. This is the way borders are added to boxes, in favour of adding the border attributes directly to the boxes themselves. border property syntax: ' || || ' - border-width: thin, medium, thick or standard CSS size (e.g. 2px, 0.5em) - border-style: none, hidden, dotted, dashed, solid, double, groove, ridge, inset, outset - color: standard CSS color (e.g. grey #88ffee)" [& {:keys [size width height min-width min-height max-width max-height margin padding border l-border r-border t-border b-border radius child class style attr] :or {size "none"} :as args}] {:pre [(validate-args-macro border-args-desc args "border")]} (let [no-border (every? nil? [border l-border r-border t-border b-border]) default-border "1px solid lightgrey"] (box-base :size size :width width :height height :min-width min-width :min-height min-height :max-width max-width :max-height max-height :margin margin :padding padding :border (if no-border default-border border) :l-border l-border :r-border r-border :t-border t-border :b-border b-border :radius radius :child child :class-name "rc-border " :class class :style style :attr attr)))