From 32a2a18434f32d5735413efeb8e309e8a96790ea Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Mon, 3 Oct 2022 10:02:28 -0700 Subject: [PATCH 01/65] CSS detached from first component --- src/re_com/buttons.cljs | 64 +++++++++++++++++++++-------------------- src/re_com/util.cljs | 36 ++++++++++++++++++++++- 2 files changed, 68 insertions(+), 32 deletions(-) diff --git a/src/re_com/buttons.cljs b/src/re_com/buttons.cljs index 502a108d..e830ea51 100644 --- a/src/re_com/buttons.cljs +++ b/src/re_com/buttons.cljs @@ -2,7 +2,7 @@ (:require-macros [re-com.core :refer [handler-fn at reflect-current-component]]) (:require - [re-com.util :refer [deref-or-value px]] + [re-com.util :refer [deref-or-value px merge-css add-map-to-hiccup-call]] [re-com.config :refer [include-args-desc?]] [re-com.debug :refer [->attr]] [re-com.validate :refer [position? position-options-list button-size? button-sizes-list @@ -21,6 +21,12 @@ {:name :tooltip :level 1 :class "rc-button-tooltip" :impl "[popover-tooltip]" :notes "Tooltip, if enabled."} {:type :legacy :level 1 :class "rc-button" :impl "[:button]" :notes "The actual button."}])) +(def button-css-desc + {:main {:class ["rc-button" "btn"] + :style (flex-child-style "none") } + :wrapper {:class ["rc-button-wrapper" "display-inline-flex"]} + :tooltip {:class ["rc-button-tooltip"]}}) + (def button-parts (when include-args-desc? (-> (map :name button-parts-desc) set))) @@ -52,41 +58,37 @@ (do (when-not tooltip (reset! showing? false)) ;; To prevent tooltip from still showing after button drag/drop (let [disabled? (deref-or-value disabled?) + cmerger (merge-css button-css-desc args) the-button [:button (merge - {:class (str "rc-button btn " class) - :style (merge - (flex-child-style "none") - style) - :disabled disabled? - :on-click (handler-fn - (when (and on-click (not disabled?)) - (on-click event)))} - (when tooltip - {:on-mouse-over (handler-fn (reset! showing? true)) - :on-mouse-out (handler-fn (reset! showing? false))}) - attr) + (cmerger :main nil) + {:disabled disabled? + :on-click (handler-fn + (when (and on-click (not disabled?)) + (on-click event)))} + (when tooltip + {:on-mouse-over (handler-fn (reset! showing? true)) + :on-mouse-out (handler-fn (reset! showing? false))}) + attr) label]] (when disabled? (reset! showing? false)) - [box - :src src - :debug-as (or debug-as (reflect-current-component)) - :class (str "rc-button-wrapper display-inline-flex " (get-in parts [:wrapper :class])) - :style (get-in parts [:wrapper :style]) - :attr (get-in parts [:wrapper :attr]) - :align :start - :child (if tooltip - [popover-tooltip - :src (at) - :label tooltip - :position (or tooltip-position :below-center) - :showing? showing? - :anchor the-button - :class (str "rc-button-tooltip " (get-in parts [:tooltip :class])) - :style (get-in parts [:tooltip :style]) - :attr (get-in parts [:tooltip :attr])] - the-button)])))))) + (add-map-to-hiccup-call + (cmerger :wrapper nil) + [box + :src src + :debug-as (or debug-as (reflect-current-component)) + :align :start + :child (if tooltip + (add-map-to-hiccup-call + (cmerger :tooltip) + [popover-tooltip + :src (at) + :label tooltip + :position (or tooltip-position :below-center) + :showing? showing? + :anchor the-button]) + the-button)]))))))) ;;-------------------------------------------------------------------------------------------------- diff --git a/src/re_com/util.cljs b/src/re_com/util.cljs index 7a143aba..94153d3e 100644 --- a/src/re_com/util.cljs +++ b/src/re_com/util.cljs @@ -2,7 +2,8 @@ (:require [reagent.ratom :refer [RAtom Reaction RCursor Track Wrapper]] [goog.date.DateTime] - [goog.date.UtcDateTime])) + [goog.date.UtcDateTime] + [clojure.string :as string])) (defn fmap "Takes a function 'f' amd a map 'm'. Applies 'f' to each value in 'm' and returns. @@ -217,3 +218,36 @@ (.getMonth local-date-time) (.getDate local-date-time) 0 0 0 0))) + + +(defn merge-css [css-desc {:as params :keys [class style parts]}] + (fn [tag options] + (let [defaults (get css-desc (or tag :main)) + user (if tag + (get parts tag) + {:class class :style style})] + (into {} + [(when (or (:class defaults) (:class user)) + (let [d (:class defaults) + d (if (fn? d) (d options) d) + u (:class user) + u (if (string? u) [u] u)] + [:class (reduce into [] [d u])])) + (when (or (:style defaults) (:style user)) + (let [d (:style defaults) + d (if (fn? d) (d options) d) + u (:style user)] + [:style (reduce into {} [d u])])) + (when (or (:attr defaults) (:attr user)) + (let [d (:attr defaults) + d (if (fn? d) (d options) d) + u (:attr user)] + [:attr (reduce into {} [d u])]))])))) + +(defn add-map-to-hiccup-call [map hiccup] + (reduce into [[(first hiccup)] + (for [[k v] map + :let [v (if (= k :class) (string/join " " v) v)] + itm [k v]] + itm) + (rest hiccup)])) From 2f5fbb354efd70473a7dc3e52ef0b5e2495ec2ca Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Wed, 5 Oct 2022 16:14:45 -0700 Subject: [PATCH 02/65] CSS split out --- src/re_com/buttons.cljs | 433 +++++++++++++++++++++------------------- src/re_com/util.cljs | 4 +- 2 files changed, 232 insertions(+), 205 deletions(-) diff --git a/src/re_com/buttons.cljs b/src/re_com/buttons.cljs index e830ea51..c4197683 100644 --- a/src/re_com/buttons.cljs +++ b/src/re_com/buttons.cljs @@ -61,7 +61,7 @@ cmerger (merge-css button-css-desc args) the-button [:button (merge - (cmerger :main nil) + (cmerger :main) {:disabled disabled? :on-click (handler-fn (when (and on-click (not disabled?)) @@ -74,7 +74,7 @@ (when disabled? (reset! showing? false)) (add-map-to-hiccup-call - (cmerger :wrapper nil) + (cmerger :wrapper) [box :src src :debug-as (or debug-as (reflect-current-component)) @@ -102,6 +102,27 @@ {:type :legacy :level 1 :class "rc-md-circle-icon-button" :impl "[:div]" :notes "The actual button."} {:name :icon :level 2 :class "rc-md-circle-icon-button-icon" :impl "[:i]" :notes "The button icon."}])) +(def md-circle-icon-button-css-desc + {:main {:class + (fn [{:keys [size emphasise? disabled?]}] + ["noselect" "rc-md-circle-icon-button" + (case size + :smaller "rc-circle-smaller" + :larger "rc-circle-larger") + (when emphasise? "rc-circle-emphasis") + (when disabled? "rc-circle-disabled")]) + :style + (fn [{:keys [disabled?]}] + (if disabled? + {} + {:cursor "pointer"}))} + :wrapper {:class ["display-inline-flex" "rc-md-circle-icon-button-wrapper"]} + :tooltip {:class ["rc-md-circle-icon-button-tooltip"]} + :icon {:class + (fn [{:keys [md-icon-name]}] + ["zmdi" "zmdi-hc-fw-rc" md-icon-name "rc-md-circle-icon-button-icon"])} + }) + (def md-circle-icon-button-parts (when include-args-desc? (-> (map :name md-circle-icon-button-parts-desc) set))) @@ -134,50 +155,34 @@ (validate-args-macro md-circle-icon-button-args-desc args) (do (when-not tooltip (reset! showing? false)) ;; To prevent tooltip from still showing after button drag/drop - (let [the-button [:div + (let [cmerger (merge-css md-circle-icon-button-css-desc args) + the-button [:div (merge - {:class (str - "noselect rc-md-circle-icon-button " - (case size - :smaller "rc-circle-smaller " - :larger "rc-circle-larger " - " ") - (when emphasise? "rc-circle-emphasis ") - (when disabled? "rc-circle-disabled ") - class) - :style (merge - {:cursor (when-not disabled? "pointer")} - style) - :on-click (handler-fn - (when (and on-click (not disabled?)) - (on-click event)))} + (cmerger :main {:emphasise? emphasise? :disabled? disabled? :size size}) + {:on-click (handler-fn + (when (and on-click (not disabled?)) + (on-click event)))} (when tooltip {:on-mouse-over (handler-fn (reset! showing? true)) :on-mouse-out (handler-fn (reset! showing? false))}) attr) - [:i - (merge - {:class (str "zmdi zmdi-hc-fw-rc " md-icon-name " rc-md-circle-icon-button-icon " (get-in parts [:icon :class])) - :style (get-in parts [:icon :style] {})} - (get-in parts [:icon :attr]))]]] - [box - :src src - :debug-as (or debug-as (reflect-current-component)) - :align :start - :class (str "display-inline-flex rc-md-circle-icon-button-wrapper " (get-in parts [:wrapper :class])) - :style (get-in parts [:wrapper :style]) - :attr (get-in parts [:wrapper :attr]) - :child (if tooltip - [popover-tooltip - :src (at) - :label tooltip - :position (or tooltip-position :below-center) - :showing? showing? - :anchor the-button - :class (str "rc-md-circle-icon-button-tooltip " (get-in parts [:tooltip :class])) - :style (get-in parts [:tooltip :style]) - :attr (get-in parts [:tooltip :attr])] - the-button)])))))) + [:i (cmerger :main {:md-icon-name md-icon-name})]]] + (add-map-to-hiccup-call + (cmerger :wrapper) + [box + :src src + :debug-as (or debug-as (reflect-current-component)) + :align :start + :child (if tooltip + (add-map-to-hiccup-call + (cmerger :tooltip) + [popover-tooltip + :src (at) + :label tooltip + :position (or tooltip-position :below-center) + :showing? showing? + :anchor the-button]) + the-button)]))))))) ;;-------------------------------------------------------------------------------------------------- @@ -191,6 +196,27 @@ {:type :legacy :level 1 :class "rc-md-icon-button" :impl "[:div]" :notes "The actual button."} {:name :icon :level 2 :class "rc-md-icon-button-icon" :impl "[:i]" :notes "The button icon."}])) +(def md-icon-button-css-desc + {:main {:class + (fn [{:keys [size emphasise? disabled?]}] + ["noselect" "rc-md-icon-button" + (case size + :smaller "rc-icon-smaller" + :larger "rc-icon-larger") + (when emphasise? "rc-icon-emphasis") + (when disabled? "rc-icon-disabled")]) + :style + (fn [{:keys [disabled?]}] + (if disabled? + {} + {:cursor "pointer"}))} + :wrapper {:class ["display-inline-flex" "rc-md-icon-button-wrapper"]} + :tooltip {:class ["rc-md-icon-button-tooltip"]} + :icon {:class + (fn [{:keys [md-icon-name]}] + ["zmdi" "zmdi-hc-fw-rc" md-icon-name "rc-md-circle-icon-button-icon"])} + }) + (def md-icon-button-parts (when include-args-desc? (-> (map :name md-icon-button-parts-desc) set))) @@ -223,50 +249,34 @@ (validate-args-macro md-icon-button-args-desc args) (do (when-not tooltip (reset! showing? false)) ;; To prevent tooltip from still showing after button drag/drop - (let [the-button [:div + (let [cmerger (merge-css md-circle-icon-button-css-desc args) + the-button [:div (merge - {:class (str - "noselect rc-md-icon-button " - (case size - :smaller "rc-icon-smaller " - :larger "rc-icon-larger " - " ") - (when emphasise? "rc-icon-emphasis ") - (when disabled? "rc-icon-disabled ") - class) - :style (merge - {:cursor (when-not disabled? "pointer")} - style) - :on-click (handler-fn + (cmerger :main {:size size :emphasise? emphasise? :disabled? disabled?}) + {:on-click (handler-fn (when (and on-click (not disabled?)) (on-click event)))} (when tooltip {:on-mouse-over (handler-fn (reset! showing? true)) :on-mouse-out (handler-fn (reset! showing? false))}) attr) - [:i - (merge - {:class (str "zmdi zmdi-hc-fw-rc " md-icon-name " rc-md-icon-button-icon " (get-in parts [:icon :class])) - :style (get-in parts [:icon :style] {})} - (get-in parts [:icon :attr]))]]] - [box - :src src - :debug-as (or debug-as (reflect-current-component)) - :align :start - :class (str "display-inline-flex rc-md-icon-button-wrapper " (get-in parts [:wrapper :class])) - :style (get-in parts [:wrapper :style]) - :attr (get-in parts [:wrapper :attr]) - :child (if tooltip - [popover-tooltip - :src (at) - :label tooltip - :position (or tooltip-position :below-center) - :showing? showing? - :anchor the-button - :class (str "rc-md-icon-button-tooltip " (get-in parts [:tooltip :class])) - :style (get-in parts [:tooltip :style]) - :attr (get-in parts [:tooltip :attr])] - the-button)])))))) + [:i (cmerger :icon {:md-icon-name md-icon-name})]]] + (add-map-to-hiccup-call + (cmerger :wrapper) + [box + :src src + :debug-as (or debug-as (reflect-current-component)) + :align :start + :child (if tooltip + (add-map-to-hiccup-call + (cmerger :tooltip) + [popover-tooltip + :src (at) + :label tooltip + :position (or tooltip-position :below-center) + :showing? showing? + :anchor the-button]) + the-button)]))))))) ;;-------------------------------------------------------------------------------------------------- @@ -279,6 +289,15 @@ {:type :legacy :level 1 :class "rc-info-button" :impl "[:div]" :notes "The actual button."} {:name :icon :level 2 :class "rc-info-button-icon" :impl "[:svg]" :notes "The button icon."}])) +(def info-button-css-desc + {:main {:class (fn [{:keys [disabled?]}] + ["noselect" "rc-info-button" (when disabled? "rc-icon-disabled")]) + :style (fn [{:keys [disabled?]}] + (if disabled? + {:cursor "pointer"}))} + :tooltip {:class ["rc-info-button-popover-anchor-wrapper"]} + :icon {:class ["rc-info-button-icon"]}}) + (def info-button-parts (when include-args-desc? (-> (map :name info-button-parts-desc) set))) @@ -307,40 +326,33 @@ [& {:keys [info position width disabled? class style attr parts src debug-as] :as args}] (or (validate-args-macro info-button-args-desc args) - [popover-tooltip - :src src - :debug-as (or debug-as (reflect-current-component)) - :label info - :status :info - :position (or position :right-below) - :width (or width "250px") - :showing? showing? - :on-cancel #(swap! showing? not) - :class (str "rc-info-button-popover-anchor-wrapper " (get-in parts [:tooltip :class])) - :style (get-in parts [:tooltip :style]) - :attr (get-in parts [:tooltip :attr]) - :anchor [:div - (merge - {:class (str "noselect rc-info-button " - (when disabled? "rc-icon-disabled ") - class) - :style (merge - {:cursor (when-not disabled? "pointer")} - style) - :on-click (handler-fn - (when (not disabled?) - (swap! showing? not)))} - attr) - [:svg - (merge - {:width "11" - :height "11" - :class (str "rc-info-button-icon " (get-in parts [:icon :class])) - :style (get-in parts [:icon :style] {})} - (get-in parts [:icon :attr])) - [:circle {:cx "5.5" :cy "5.5" :r "5.5"}] - [:circle {:cx "5.5" :cy "2.5" :r "1.4" :fill "white"}] - [:line {:x1 "5.5" :y1 "5.2" :x2 "5.5" :y2 "9.7" :stroke "white" :stroke-width "2.5"}]]]])))) + (let [cmerger (merge-css info-button-css-desc args)] + (add-map-to-hiccup-call + (cmerger :tooltip) + [popover-tooltip + :src src + :debug-as (or debug-as (reflect-current-component)) + :label info + :status :info + :position (or position :right-below) + :width (or width "250px") + :showing? showing? + :on-cancel #(swap! showing? not) + :anchor [:div + (merge + (cmerger :main {:disabled? disabled?}) + {:on-click (handler-fn + (when (not disabled?) + (swap! showing? not)))} + attr) + [:svg + (merge + (cmerger :icon) + {:width "11" + :height "11"}) + [:circle {:cx "5.5" :cy "5.5" :r "5.5"}] + [:circle {:cx "5.5" :cy "2.5" :r "1.4" :fill "white"}] + [:line {:x1 "5.5" :y1 "5.2" :x2 "5.5" :y2 "9.7" :stroke "white" :stroke-width "2.5"}]]]])))))) ;;-------------------------------------------------------------------------------------------------- @@ -354,6 +366,16 @@ {:type :legacy :level 1 :class "rc-row-button" :impl "[:div]" :notes "The actual button."} {:name :icon :level 2 :class "rc-row-button-icon" :impl "[:i]" :notes "The button icon."}])) +(def row-button-css-desc + {:main {:class (fn [{:keys [disabled? mouse-over-row?]}] + ["noselect" "rc-row-button" + (when mouse-over-row? "rc-row-mouse-over-row") + (when disabled? "rc-row-disabled")])} + :wrapper {:class ["display-inline-flex" "rc-row-button-wrapper"]} + :tooltip {:class ["rc-row-button-tooltip"]} + :icon {:class (fn [{:keys [md-icon-name]}] + ["zmdi" "zmdi-hc-fw-rc" md-icon-name "rc-row-button-icon"])}}) + (def row-button-parts (when include-args-desc? (-> (map :name row-button-parts-desc) set))) @@ -385,44 +407,34 @@ (validate-args-macro row-button-args-desc args) (do (when-not tooltip (reset! showing? false)) ;; To prevent tooltip from still showing after button drag/drop - (let [the-button [:div + (let [cmerger (merge-css row-button-css-desc args) + the-button [:div (merge - {:class (str - "noselect rc-row-button " - (when mouse-over-row? "rc-row-mouse-over-row ") - (when disabled? "rc-row-disabled ") - class) - :style style - :on-click (handler-fn + (cmerger :main {:mouse-over-row? mouse-over-row? :disabled? disabled?}) + {:on-click (handler-fn (when (and on-click (not disabled?)) (on-click event)))} (when tooltip {:on-mouse-over (handler-fn (reset! showing? true)) :on-mouse-out (handler-fn (reset! showing? false))}) ;; Need to return true to ALLOW default events to be performed attr) - [:i - (merge - {:class (str "zmdi zmdi-hc-fw-rc " md-icon-name " rc-row-button-icon " (get-in parts [:icon :class])) - :style (get-in parts [:icon :style] {})} - (get-in parts [:icon :attr]))]]] - [box - :src src - :debug-as (reflect-current-component) - :align :start - :class (str "display-inline-flex rc-row-button-wrapper " (get-in parts [:wrapper :class])) - :style (get-in parts [:wrapper :style] {}) - :attr (get-in parts [:wrapper :attr] {}) - :child (if tooltip - [popover-tooltip - :src (at) - :label tooltip - :position (or tooltip-position :below-center) - :showing? showing? - :anchor the-button - :class (str "rc-row-button-tooltip " (get-in parts [:tooltip :class])) - :style (get-in parts [:tooltip :style]) - :attr (get-in parts [:tooltip :attr])] - the-button)])))))) + [:i (cmerger :icon {:md-icon-name md-icon-name})]]] + (add-map-to-hiccup-call + (cmerger :wrapper) + [box + :src src + :debug-as (reflect-current-component) + :align :start + :child (if tooltip + (add-map-to-hiccup-call + (cmerger :tooltip) + [popover-tooltip + :src (at) + :label tooltip + :position (or tooltip-position :below-center) + :showing? showing? + :anchor the-button]) + the-button)]))))))) ;;-------------------------------------------------------------------------------------------------- @@ -436,6 +448,20 @@ {:name :container :level 1 :class "rc-hyperlink-container" :impl "[box]"} {:type :legacy :level 2 :class "rc-hyperlink" :impl "[:a]" :notes "The anchor."}])) +(def hyperlink-css-desc + {:main {:class ["noselect" "rc-hyperlink"] + :style (fn [{:keys [disabled?]}] + (merge + (flex-child-style "none") + (if disabled? + {:cursor "default" + :pointer-events "none" + :color "grey"} + {:cursor "pointer"})))} + :wrapper {:class ["display-inline-flex" "rc-hyperlink-wrapper"]} + :tooltip {:class ["rc-hyperlink-tooltip"]} + :container {:class ["rc-hyperlink-container"]}}) + (def hyperlink-parts (when include-args-desc? (-> (map :name hyperlink-parts-desc) set))) @@ -468,45 +494,39 @@ (when-not tooltip (reset! showing? false)) ;; To prevent tooltip from still showing after button drag/drop (let [label (deref-or-value label) disabled? (deref-or-value disabled?) - the-button [box - :src (at) - :align :start - :class (str "rc-hyperlink-container " (get-in parts [:container :class])) - :child [:a - (merge - {:class (str "noselect rc-hyperlink " class) - :style (merge - (flex-child-style "none") - {:cursor (if disabled? "default" "pointer") - :pointer-events (when disabled? "none") - :color (when disabled? "grey")} - style) - :on-click (handler-fn - (when (and on-click (not disabled?)) - (on-click event)))} + cmerger (merge-css hyperlink-css-desc args) + the-button (add-map-to-hiccup-call + (cmerger :container) + [box + :src (at) + :align :start + :child [:a + (merge + (cmerger :main {:disabled? disabled?}) + {:on-click (handler-fn + (when (and on-click (not disabled?)) + (on-click event)))} (when tooltip {:on-mouse-over (handler-fn (reset! showing? true)) :on-mouse-out (handler-fn (reset! showing? false))}) attr) - label]]] - [box - :src src - :debug-as (or debug-as (reflect-current-component)) - :align :start - :class (str "display-inline-flex rc-hyperlink-wrapper " (get-in parts [:wrapper :class])) - :style (get-in parts [:wrapper :style]) - :attr (get-in parts [:wrapper :attr]) - :child (if tooltip - [popover-tooltip - :src (at) - :label tooltip - :position (or tooltip-position :below-center) - :showing? showing? - :anchor the-button - :class (str "rc-hyperlink-tooltip " (get-in parts [:tooltip :class])) - :style (get-in parts [:tooltip :style]) - :attr (get-in parts [:tooltip :attr])] - the-button)])))))) + label]])] + (add-map-to-hiccup-call + (cmerger :wrapper) + [box + :src src + :debug-as (or debug-as (reflect-current-component)) + :align :start + :child (if tooltip + (add-map-to-hiccup-call + (cmerger :tooltip) + [popover-tooltip + :src (at) + :label tooltip + :position (or tooltip-position :below-center) + :showing? showing? + :anchor the-button]) + the-button)]))))))) ;;-------------------------------------------------------------------------------------------------- @@ -519,6 +539,19 @@ {:name :tooltip :level 1 :class "rc-hyperlink-href-tooltip" :impl "[popover-tooltip]" :notes "Tooltip, if enabled."} {:type :legacy :level 2 :class "rc-hyperlink-href" :impl "[:a]" :notes "The anchor."}])) +(def hyperlink-href-css-desc + {:main {:class ["rc-hyperlink-href" "noselect"] + :style (fn [{:keys [disabled?]}] + (merge + (flex-child-style "none") + (if disabled? + {:cursor "default" + :pointer-events "none" + :color "grey"} + {:cursor "pointer"})))} + :wrapper {:class ["rc-hyperlink-href-wrapper" "display-inline-flex"]} + :tooltip {:class ["rc-hyperlink-href-tooltip"]}}) + (def hyperlink-href-parts (when include-args-desc? (-> (map :name hyperlink-href-parts-desc) set))) @@ -554,14 +587,10 @@ href (deref-or-value href) target (deref-or-value target) disabled? (deref-or-value disabled?) + cmerger (merge-css hyperlink-href-css-desc args) the-button [:a - (merge {:class (str "rc-hyperlink-href noselect " class) - :style (merge (flex-child-style "none") - {:cursor (if disabled? "default" "pointer") - :pointer-events (when disabled? "none") - :color (when disabled? "grey")} - style) - :target target} + (merge (cmerger :main {:disabled? disabled?}) + {:target target} ;; As of HTML5 the href attribute on a elements is not required; when those elements do ;; not have href attributes they do not create hyperlinks. These are also known as a ;; 'placeholder link'. A placeholder link resembles a traditional hyperlink, but does not @@ -575,21 +604,19 @@ attr) label]] - [box - :src src - :debug-as (or debug-as (reflect-current-component)) - :align :start - :class (str "rc-hyperlink-href-wrapper display-inline-flex " (get-in parts [:wrapper :class])) - :style (get-in parts [:wrapper :style] {}) - :attr (get-in parts [:wrapper :attr] {}) - :child (if tooltip - [popover-tooltip - :src (at) - :label tooltip - :position (or tooltip-position :below-center) - :showing? showing? - :anchor the-button - :class (str "rc-hyperlink-href-tooltip " (get-in parts [:tooltip :class])) - :style (get-in parts [:tooltip :style] {}) - :attr (get-in parts [:tooltip :attr] {})] - the-button)])))))) + (add-map-to-hiccup-call + (cmerger :wrapper) + [box + :src src + :debug-as (or debug-as (reflect-current-component)) + :align :start + :child (if tooltip + (add-map-to-hiccup-call + (cmerger :tooltip) + [popover-tooltip + :src (at) + :label tooltip + :position (or tooltip-position :below-center) + :showing? showing? + :anchor the-button]) + the-button)]))))))) diff --git a/src/re_com/util.cljs b/src/re_com/util.cljs index 94153d3e..0ed569b7 100644 --- a/src/re_com/util.cljs +++ b/src/re_com/util.cljs @@ -221,9 +221,9 @@ (defn merge-css [css-desc {:as params :keys [class style parts]}] - (fn [tag options] + (fn [tag & options] (let [defaults (get css-desc (or tag :main)) - user (if tag + user (if (and tag (not (= tag :main))) (get parts tag) {:class class :style style})] (into {} From d84d115dc94db28eb8afc9019bf4f8f0ce445c4f Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Wed, 5 Oct 2022 16:15:38 -0700 Subject: [PATCH 03/65] css-desc table created --- src/re_com/v_table.cljs | 182 ++++++++++++++++++++++++++++++---------- 1 file changed, 136 insertions(+), 46 deletions(-) diff --git a/src/re_com/v_table.cljs b/src/re_com/v_table.cljs index 6b242b2a..9c146948 100644 --- a/src/re_com/v_table.cljs +++ b/src/re_com/v_table.cljs @@ -8,7 +8,7 @@ [re-com.config :refer [debug? include-args-desc?]] [re-com.debug :refer [->attr]] [re-com.box :as box] - [re-com.util :as util :refer [deref-or-value px-n]] + [re-com.util :as util :refer [deref-or-value px-n add-map-to-hiccup-call merge-css]] [re-com.validate :refer [vector-atom? ifn-or-nil? map-atom? parts?]] [re-com.dmm-tracker :refer [make-dmm-tracker captureMouseMoves]])) @@ -30,6 +30,28 @@ (when (.-altKey event) (js/console.log (str "ROW-INDEX[" row-index "]") row))) +(def scrollbar-css-desc + {:main {:class (fn [{:keys [horizontal?]}] + [(str (if horizontal? "horizontal" "vertical") "-scrollbar")]) + :style (fn [{:keys [width horizontal? show? mouse-over? dragging?]}] + (merge {:border-radius (px (/ width 2)) + :overflow "hidden"} + (when show? + {:background-color (if (or mouse-over? dragging?) + ;;scrollbar-hover-color + "#ccc" + ;;scrollbar-color + "#eee")})))} + :thumb {:style (fn [{:keys [width mouse-over? dragging? horizontal? scroll-position]}] + {:border-radius (px (/ width 2)) + :cursor "default" + :background-color + (case + dragging? "#777" ;thumb-drag-color + mouse-over? "#999" ;thumb-hover-color + :else "#bbb") ;thumb-color + (if horizontal? :margin-left :margin-top) + (px scroll-position)})}}) (defn scrollbar "Render a horizontal or vertical scrollbar @@ -46,12 +68,6 @@ [& {:keys [type width on-change] :or {width 10}}] (let [horizontal? (= type :horizontal) - radius (px (/ width 2)) - scrollbar-color "#eee" ;; "#f3f3f3" "rgba(0,0,0,0.05)" ;; These colors could be passed in as a single map, - scrollbar-hover-color "#ccc" ;; "#cccccc" "rgba(0,0,0,0.20)" ;; or we could add :style and :thumb-style args (wouldn't work for hover colors) - thumb-color "#bbb" ;; "#b7b7b7" "rgba(0,0,0,0.25)" - thumb-hover-color "#999" ;; "#9a9a9a" "rgba(0,0,0,0.30)" - thumb-drag-color "#777" ;; "#707070" "rgba(0,0,0,0.45)" mouse-over? (reagent/atom false) dragging? (reagent/atom false) pos-on-scrollbar (reagent/atom 0) @@ -107,14 +123,16 @@ (reset! dragging? true) (.stopPropagation event)))] ;; Prevents parent div getting this mouse-down as well (fn scrollbar-renderer - [& {:keys [length width content-length scroll-pos style src] - :or {width 10}}] + [& {:keys [length width content-length scroll-pos class style parts src] + :or {width 10} + :as args}] (let [thumb-ratio (/ content-length length) thumb-length (max (* 1.5 width) (/ length thumb-ratio)) show? (> content-length length) max-scroll-pos (- length thumb-length) scrollbar-content-ratio (/ (- content-length length) max-scroll-pos) - internal-scroll-pos (/ scroll-pos scrollbar-content-ratio)] + internal-scroll-pos (/ scroll-pos scrollbar-content-ratio) + cmerger (merge-css scrollbar-css-desc args)] (reset! calcs {:length length :scroll-pos scroll-pos :thumb-ratio thumb-ratio @@ -122,42 +140,34 @@ :max-scroll-pos max-scroll-pos :scrollbar-content-ratio scrollbar-content-ratio :internal-scroll-pos internal-scroll-pos}) - [box/box - :src src - :width (if horizontal? - (when length (px length)) - (px width)) - :height (if horizontal? - (px width) - (when length (px length))) - :class (str (if horizontal? "horizontal" "vertical") "-scrollbar") - :style (merge {:background-color (when show? (if (or @mouse-over? @dragging?) - scrollbar-hover-color - scrollbar-color)) - :border-radius radius - :overflow "hidden"} - style) - :attr {:on-mouse-enter on-mouse-enter - :on-mouse-leave on-mouse-leave - :on-mouse-down (handler-fn (when show? (scrollbar-mouse-down event)))} ;; TODO: Best way to move this fn to outer fn? (closes over show?) - :child [box/box - :src (at) - :width (if horizontal? - (px (if show? thumb-length 0)) - (px width)) - :height (if horizontal? - (px width) - (px (if show? thumb-length 0))) - :style {:background-color (if (or @mouse-over? @dragging?) - (if @dragging? thumb-drag-color thumb-hover-color) - thumb-color) - :cursor "default" - :border-radius radius - (if horizontal? - :margin-left - :margin-top) (px internal-scroll-pos)} - :attr {:on-mouse-down (handler-fn (thumb-mouse-down event internal-scroll-pos))} ;; TODO: Best way to move this fn to outer fn? (closes over internal-scroll-pos) - :child ""]])))) + (add-map-to-hiccup-call + (cmerger :main {:width width :horizontal? horizontal? :show? show? :mouse-over? @mouse-over? + :dragging? @dragging?}) + [box/box + :src src + :width (if horizontal? + (when length (px length)) + (px width)) + :height (if horizontal? + (px width) + (when length (px length))) + :attr {:on-mouse-enter on-mouse-enter + :on-mouse-leave on-mouse-leave + :on-mouse-down (handler-fn (when show? (scrollbar-mouse-down event)))} ;; TODO: Best way to move this fn to outer fn? (closes over show?) + :child (add-map-to-hiccup-call + (cmerger :thumb {:width width :horizontal? horizontal? :show? show? + :mouse-over? @mouse-over? :dragging? @dragging? + :scroll-position internal-scroll-pos}) + [box/box + :src (at) + :width (if horizontal? + (px (if show? thumb-length 0)) + (px width)) + :height (if horizontal? + (px width) + (px (if show? thumb-length 0))) + :attr {:on-mouse-down (handler-fn (thumb-mouse-down event internal-scroll-pos))} ;; TODO: Best way to move this fn to outer fn? (closes over internal-scroll-pos) + :child ""])]))))) ;; ================================================================================== SECTION 1 - top-left @@ -504,6 +514,86 @@ {:type :legacy :level 2 :name-label "-" :impl "[box]" :notes "Legacy"} {:name :v-scroll :level 3 :class "rc-v-table-v-scroll" :impl "[box]" :notes "The vertical scrollbar"}])) +(def v-table-css-desc + {:main + :wrapper {:class ["rc-v-table"] + :style (fn [{:keys [max-width max-height]}] + {:max-width max-width :max-height max-height})} + + :left-section {:class ["rc-v-table-left-section"]} + :top-left {:class ["rc-v-table-top-left" "rc-v-table-content"] + :style {:overflow "hidden"}} + :row-headers {:class ["rc-v-table-row-headers" "rc-v-table-viewport"] + :style (fn [{:keys [max-height]}] + {:position "relative" + :overflow "hidden" + :max-height (px max-height)})} + :row-header-selection-rect + :row-header-content {:class ["rc-v-table-row-header-content" "rc-v-table-content"] + :style (fn [{:keys [scroll-y]}] + {:margin-top (px scroll-y :negative)})} + :bottom-left {:class ["rc-v-table-bottom-left" "rc-v-table-content"] + :style {:overflow "hidden"}} + + :middle-section {:class ["rc-v-table-middle-section"] + :style (fn [{:keys [max-width]}] + {:max-width max-width})} + :column-headers {:class ["rc-v-table-column-headers" "rc-v-table-viewport"] + :style {:overflow "hidden" + :position "relative"}} + :column-header-selection-rect + :column-header-content {:class ["rc-v-table-column-header-content" "rc-v-table-content"] + :style (fn [{:keys [scroll-x]}] + {:margin-left (px scroll-x :negative)})} + + :rows {:class ["rc-v-table-rows" "rc-v-table-viewport"] + :style (fn [{:keys [max-height]}] + {:overflow "hidden" + :position "relative" + :max-height (px max-height)})} + :row-selection-rect + :row-content {:class ["rc-v-table-row-content" "rc-v-table-content"] + :style (fn [{:keys [scroll-x scroll-y]}] + {:margin-left (px scroll-x :negative) + :margin-top (px scroll-y :negative)})} + :column-footers {:class ["rc-v-table-column-footers" "rc-v-table-viewport"] + :style {:overflow "hidden"}} + :column-footer-content {:class ["rc-v-table-column-footer-content" "rc-v-table-content"] + :style (fn [{:keys [scroll-x]}] + {:margin-left (px scroll-x :negative)})} + + :h-scroll {:class ["rc-v-table-h-scroll"] + :style (fn [{:keys [scrollbar-margin]}] + {:margin (px-n scrollbar-margin 0)})} + + :right-section {:class ["rc-v-table-right-section"]} + :top-right {:class ["rc-v-table-top-right" "rc-v-table-content"] + :style {:overflow "hidden"}} + :row-footers {:class ["rc-v-table-row-footers" "rc-v-table-viewport"] + :style (fn [{:keys [max-height]}] + {:max-height (px max-height)})} + :row-footer-content {:class ["rc-v-table-row-footer-content" "rc-v-table-content"] + :style (fn [{:keys [scroll-y]}] + {:margin-top (px scroll-y :negative)})} + :bottom-right {:class ["rc-v-table-bottom-right" "rc-v-table-content"] + :style {:overflow "hidden"}} + :v-scroll-section {:class ["rc-v-table-v-scroll-section"]} + :v-scroll {:class ["rc-v-table-v-scroll"] + :style (fn [{:keys [scrollbar-margin]}] + {:margin (px-n scrollbar-margin 0)})} + }) + +;;This is for the selection-renderer component embedded in v-table. Perhaps it should be a key in +;; v-table-css-desc, above. But it doesn't seem to be addressable using `parts` so for now just has +;; its defaults defined in the separate structure below. +(def v-table-selection-css-desc + {:main {:class ["rc-v-table-selection"] + :style (fn [{:keys [top left width height]}] + {:top (px top) :left (px left) :width (px width) :height (px height) + :position "absolute" + :z-index 1 + :background-color "rgba(0,0,255,0.1)" + :border "1px solid rgba(0,0,255,0.4)"})}}) (def v-table-parts From 9d217ae993a4dba11f061102fa6f7e9281a603af Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Sun, 9 Oct 2022 05:55:44 -0700 Subject: [PATCH 04/65] Split out css in v-table - added local :class, :style, :attr handling to merge-css --- src/re_com/util.cljs | 56 ++- src/re_com/v_table.cljs | 899 ++++++++++++++++++---------------------- 2 files changed, 440 insertions(+), 515 deletions(-) diff --git a/src/re_com/util.cljs b/src/re_com/util.cljs index 0ed569b7..cdeede67 100644 --- a/src/re_com/util.cljs +++ b/src/re_com/util.cljs @@ -221,28 +221,40 @@ (defn merge-css [css-desc {:as params :keys [class style parts]}] - (fn [tag & options] - (let [defaults (get css-desc (or tag :main)) - user (if (and tag (not (= tag :main))) - (get parts tag) - {:class class :style style})] - (into {} - [(when (or (:class defaults) (:class user)) - (let [d (:class defaults) - d (if (fn? d) (d options) d) - u (:class user) - u (if (string? u) [u] u)] - [:class (reduce into [] [d u])])) - (when (or (:style defaults) (:style user)) - (let [d (:style defaults) - d (if (fn? d) (d options) d) - u (:style user)] - [:style (reduce into {} [d u])])) - (when (or (:attr defaults) (:attr user)) - (let [d (:attr defaults) - d (if (fn? d) (d options) d) - u (:attr user)] - [:attr (reduce into {} [d u])]))])))) + (defn fetch-merged-css + ([tag] + (fetch-merged-css tag {})) + ([tag options] + (let [xoptions (reduce (partial dissoc options) [:class :style :attr]) + defaults (get css-desc (or tag :main)) + user (if (and tag (not (= tag :main))) + (get parts tag) + {:class class :style style})] + (into {} + [(let [d (:class defaults) + d (if (fn? d) (d xoptions) d) + u (:class user) + u (if (string? u) [u] u) + o (:class options) + o (if (string? o) [o] o) + res (reduce into [] [d u o])] + (when-not (empty? res) + [:class res])) + (let [d (:style defaults) + d (if (fn? d) (d xoptions) d) + u (:style user) + o (:style options) + res (reduce into {} [d u o])] + (when-not (empty? res) + [:style res])) + (let [d (:attr defaults) + d (if (fn? d) (d xoptions) d) + u (:attr user) + o (:attr options) + res (reduce into {} [d u o])] + (when-not (empty? res) + [:attr res]))])))) + fetch-merged-css) (defn add-map-to-hiccup-call [map hiccup] (reduce into [[(first hiccup)] diff --git a/src/re_com/v_table.cljs b/src/re_com/v_table.cljs index 9c146948..c9c7bbed 100644 --- a/src/re_com/v_table.cljs +++ b/src/re_com/v_table.cljs @@ -142,7 +142,11 @@ :internal-scroll-pos internal-scroll-pos}) (add-map-to-hiccup-call (cmerger :main {:width width :horizontal? horizontal? :show? show? :mouse-over? @mouse-over? - :dragging? @dragging?}) + :dragging? @dragging? + :attr {:on-mouse-enter on-mouse-enter + :on-mouse-leave on-mouse-leave + ;; TODO: Best way to move this fn to outer fn? (closes over show?) + :on-mouse-down (handler-fn (when show? (scrollbar-mouse-down event)))}}) [box/box :src src :width (if horizontal? @@ -151,13 +155,14 @@ :height (if horizontal? (px width) (when length (px length))) - :attr {:on-mouse-enter on-mouse-enter - :on-mouse-leave on-mouse-leave - :on-mouse-down (handler-fn (when show? (scrollbar-mouse-down event)))} ;; TODO: Best way to move this fn to outer fn? (closes over show?) :child (add-map-to-hiccup-call (cmerger :thumb {:width width :horizontal? horizontal? :show? show? :mouse-over? @mouse-over? :dragging? @dragging? - :scroll-position internal-scroll-pos}) + :scroll-position internal-scroll-pos + ;; TODO: Best way to move this fn to outer fn? (closes over internal-scroll-pos) + :attr {:on-mouse-down + (handler-fn + (thumb-mouse-down event internal-scroll-pos))}}) [box/box :src (at) :width (if horizontal? @@ -166,7 +171,6 @@ :height (if horizontal? (px width) (px (if show? thumb-length 0))) - :attr {:on-mouse-down (handler-fn (thumb-mouse-down event internal-scroll-pos))} ;; TODO: Best way to move this fn to outer fn? (closes over internal-scroll-pos) :child ""])]))))) @@ -174,15 +178,13 @@ (defn top-left-content "Render section 1 - the content component" - [top-left-renderer column-header-height class style attr] - [box/box ;; content component - :src (at) - :class (str "rc-v-table-top-left rc-v-table-content " class) - :style (merge {:overflow "hidden"} - style) - :attr attr - :height (px (or column-header-height 0)) - :child (if top-left-renderer [top-left-renderer] "")]) + [top-left-renderer column-header-height cmerger] + (add-map-to-hiccup-call + (cmerger :top-left) + [box/box ;; content component + :src (at) + :height (px (or column-header-height 0)) + :child (if top-left-renderer [top-left-renderer] "")])) ;; ================================================================================== SECTION 2 - row-headers @@ -200,18 +202,16 @@ - rows a vector of row maps (or objects) to render the row-headers from - scroll-y current horizontal scrollbar position in px " - [row-header-renderer key-fn top-row-index rows scroll-y class style attr] - [box/v-box - :src (at) - :class (str "rc-v-table-row-header-content rc-v-table-content " class) - :style (merge {:margin-top (px scroll-y :negative)} - style) - :attr attr - :children (map + [row-header-renderer key-fn top-row-index rows scroll-y cmerger] + (add-map-to-hiccup-call + (cmerger :row-header-content {:scroll-y scroll-y}) + [box/v-box + :src (at) + :children (map (fn [index row] ^{:key (if key-fn (key-fn row) index)} [row-header-renderer index row]) (iterate inc top-row-index) - rows)]) + rows)])) (defn row-header-viewport @@ -219,43 +219,38 @@ [row-header-renderer key-fn top-row-index rows scroll-y row-header-selection-fn [selection-renderer on-mouse-down on-mouse-enter on-mouse-leave] selection-allowed? row-viewport-height content-rows-height - class style attr - sel-class sel-style sel-attr - content-class content-style content-attr] - [box/v-box ;; viewport component - :src (at) - :class (str "rc-v-table-row-headers rc-v-table-viewport " class) - :style (merge {:position "relative" - :overflow "hidden" - :max-height (px content-rows-height)} - style) - :attr (merge (when row-header-selection-fn - {:on-mouse-down (handler-fn (on-mouse-down :row-header row-header-selection-fn content-rows-height 0 event)) ;; TODO: width set to 0 because we don't have it - could probably measure it - :on-mouse-enter (handler-fn (on-mouse-enter :row-header)) - :on-mouse-leave (handler-fn (on-mouse-leave :row-header))}) - attr) - :size (if row-viewport-height "none" "auto") - :height (when row-viewport-height (px row-viewport-height)) - :children [(when selection-allowed? - [selection-renderer sel-class sel-style sel-attr]) ;; selection rectangle component - (if row-header-renderer - [row-header-content row-header-renderer key-fn top-row-index rows scroll-y content-class content-style content-attr] ;; content component - "")]]) + cmerger] + (add-map-to-hiccup-call + (cmerger + :row-headers + {:max-height content-rows-height + :attr (when row-header-selection-fn + {:on-mouse-down (handler-fn (on-mouse-down :row-header row-header-selection-fn content-rows-height 0 event)) ;; TODO: width set to 0 because we don't have it - could probably measure it + :on-mouse-enter (handler-fn (on-mouse-enter :row-header)) + :on-mouse-leave (handler-fn (on-mouse-leave :row-header))})}) + [box/v-box ;; viewport component + :src (at) + :size (if row-viewport-height "none" "auto") + :height (when row-viewport-height (px row-viewport-height)) + :children [(when selection-allowed? + (let [{:keys [class style attr]} (cmerger :row-header-selection-rect)] + [selection-renderer class style attr])) ;; selection rectangle component + (if row-header-renderer + [row-header-content row-header-renderer key-fn top-row-index rows scroll-y cmerger] ;; content component + "")]])) ;; ================================================================================== SECTION 3 - bottom-left (defn bottom-left-content "Render section 3 - the content component" - [bottom-left-renderer column-footer-height class style attr] - [box/box ;; content component - :src (at) - :class (str "rc-v-table-bottom-left rc-v-table-content " class) - :style (merge {:overflow "hidden"} - style) - :attr attr - :height (px (or column-footer-height 0)) - :child (if bottom-left-renderer [bottom-left-renderer] "")]) + [bottom-left-renderer column-footer-height cmerger] + (add-map-to-hiccup-call + (cmerger :bottom-left) + [box/box ;; content component + :src (at) + :height (px (or column-footer-height 0)) + :child (if bottom-left-renderer [bottom-left-renderer] "")])) ;; ================================================================================== SECTION 4 - column-headers @@ -269,14 +264,12 @@ - column-header-renderer function that knows how to render column-headers - scroll-x current horizontal scrollbar position in px " - [column-header-renderer scroll-x class style attr] - [box/box - :src (at) - :class (str "rc-v-table-column-header-content rc-v-table-content " class) - :style (merge {:margin-left (px scroll-x :negative)} - style) - :attr attr - :child [column-header-renderer]]) + [column-header-renderer scroll-x cmerger] + (add-map-to-hiccup-call + (cmerger :column-header-content {:scroll-x scroll-x}) + [box/box + :src (at) + :child [column-header-renderer]])) (defn column-header-viewport @@ -284,27 +277,23 @@ [column-header-renderer scroll-x column-header-selection-fn [selection-renderer on-mouse-down on-mouse-enter on-mouse-leave] selection-allowed? row-viewport-width column-header-height content-rows-width - class style attr - sel-class sel-style sel-attr - content-class content-style content-attr] - [box/v-box ;; viewport component - :src (at) - :class (str "rc-v-table-column-headers rc-v-table-viewport " class) - :style (merge {:overflow "hidden" - :position "relative"} - style) - :attr (merge (when column-header-selection-fn - {:on-mouse-down (handler-fn (on-mouse-down :column-header column-header-selection-fn column-header-height content-rows-width event)) - :on-mouse-enter (handler-fn (on-mouse-enter :column-header)) - :on-mouse-leave (handler-fn (on-mouse-leave :column-header))}) - attr) - :width (when row-viewport-width (px row-viewport-width)) - :height (px (or column-header-height 0)) - :children [(when selection-allowed? - [selection-renderer sel-class sel-style sel-attr]) ;; selection rectangle component - (if column-header-renderer - [column-header-content column-header-renderer scroll-x content-class content-style content-attr] ;; content component - "")]]) + cmerger] + (add-map-to-hiccup-call + (cmerger :column-headers {:attr + (when column-header-selection-fn + {:on-mouse-down (handler-fn (on-mouse-down :column-header column-header-selection-fn column-header-height content-rows-width event)) + :on-mouse-enter (handler-fn (on-mouse-enter :column-header)) + :on-mouse-leave (handler-fn (on-mouse-leave :column-header))})}) + [box/v-box ;; viewport component + :src (at) + :width (when row-viewport-width (px row-viewport-width)) + :height (px (or column-header-height 0)) + :children [(when selection-allowed? + (let [{:keys [class style attr]} (cmerger :column-header-selection-rect)] + [selection-renderer class style attr])) ;; selection rectangle component + (if column-header-renderer + [column-header-content column-header-renderer scroll-x cmerger] ;; content component + "")]])) ;; ================================================================================== SECTION 5 - rows @@ -322,19 +311,16 @@ - scroll-x current horizontal scrollbar position in px - scroll-y current horizontal scrollbar position in px " - [row-renderer key-fn top-row-index rows scroll-x scroll-y class style attr] - [box/v-box - :src (at) - :class (str "rc-v-table-row-content rc-v-table-content " class) - :style (merge {:margin-left (px scroll-x :negative) - :margin-top (px scroll-y :negative)} - style) - :attr attr - :children (map + [row-renderer key-fn top-row-index rows scroll-x scroll-y cmerger] + (add-map-to-hiccup-call + (cmerger :row-content {:scroll-x scroll-x :scroll-y scroll-y}) + [box/v-box + :src (at) + :children (map (fn [index row] ^{:key (if key-fn (key-fn row) index)} [row-renderer index row]) (iterate inc top-row-index) - rows)]) + rows)])) (defn row-viewport @@ -342,28 +328,25 @@ [row-renderer key-fn top-row-index rows scroll-x scroll-y row-selection-fn [selection-renderer on-mouse-down on-mouse-enter on-mouse-leave] selection-allowed? row-viewport-height row-viewport-width row-viewport-id content-rows-height content-rows-width - class style attr - sel-class sel-style sel-attr - content-class content-style content-attr] - [box/v-box ;; viewport component - :src (at) - :class (str "rc-v-table-rows rc-v-table-viewport " class) - :style (merge {:overflow "hidden" - :position "relative" - :max-height (px content-rows-height)} - style) - :attr (merge (when row-selection-fn + cmerger] + (add-map-to-hiccup-call + (cmerger :rows {:max-height content-rows-height + :attr + (merge + (when row-selection-fn {:on-mouse-down (handler-fn (on-mouse-down :row row-selection-fn content-rows-height content-rows-width event)) :on-mouse-enter (handler-fn (on-mouse-enter :row)) :on-mouse-leave (handler-fn (on-mouse-leave :row))}) - attr - {:id row-viewport-id}) ;; Can't be overriding the internally generated id - :size (if row-viewport-height "none" "auto") - :width (when row-viewport-width (px row-viewport-width)) - :height (when row-viewport-height (px row-viewport-height)) - :children [(when selection-allowed? - [selection-renderer sel-class sel-style sel-attr]) ;; selection rectangle component - [row-content row-renderer key-fn top-row-index rows scroll-x scroll-y content-class content-style content-attr]]]) ;; content component + {:id row-viewport-id})}) ;; Can't be overriding the internally generated id + [box/v-box ;; viewport component + :src (at) + :size (if row-viewport-height "none" "auto") + :width (when row-viewport-width (px row-viewport-width)) + :height (when row-viewport-height (px row-viewport-height)) + :children [(when selection-allowed? + (let [{:keys [class style attr]} (cmerger :row-selection-rect)] + [selection-renderer class style attr])) ;; selection rectangle component + [row-content row-renderer key-fn top-row-index rows scroll-x scroll-y cmerger]]])) ;; content component ;; ================================================================================== SECTION 6 - column-footers @@ -377,47 +360,42 @@ - column-footer-renderer function that knows how to render column-footers - scroll-x current horizontal scrollbar position in px " - [column-footer-renderer scroll-x class style attr] - [box/box - :src (at) - :class (str "rc-v-table-column-footer-content rc-v-table-content " class) - :style (merge {:margin-left (px scroll-x :negative)} - style) - :attr attr - :child [column-footer-renderer]]) + [column-footer-renderer scroll-x cmerger] + (add-map-to-hiccup-call + (cmerger :column-footer-content {:scroll-x scroll-x}) + [box/box + :src (at) + :child [column-footer-renderer]])) (defn column-footer-viewport "Render section 6 - the viewport component (which renders the content component as its child)" [column-footer-renderer scroll-x row-viewport-width column-footer-height + cmerger class style attr content-class content-style content-attr] - [box/box ;; viewport component - :src (at) - :class (str "rc-v-table-column-footers rc-v-table-viewport " class) - :style (merge {:overflow "hidden"} - style) - :attr attr - :width (when row-viewport-width (px row-viewport-width)) - :height (px (or column-footer-height 0)) - :child (if column-footer-renderer - [column-footer-content column-footer-renderer scroll-x content-class content-style content-attr] ;; content component - "")]) + (add-map-to-hiccup-call + (cmerger :column-footers) + [box/box ;; viewport component + :src (at) + :width (when row-viewport-width (px row-viewport-width)) + :height (px (or column-footer-height 0)) + :child (if column-footer-renderer + [column-footer-content column-footer-renderer scroll-x cmerger] ;; content component + "")])) ;; ================================================================================== SECTION 7 - top-right (defn top-right-content "Render section 7 - the content component" - [top-right-renderer column-header-height class style attr] - [box/box ;; content component - :src (at) - :class (str "rc-v-table-top-right rc-v-table-content " class) - :style (merge {:overflow "hidden"} - style) - :attr attr - :height (px (or column-header-height 0)) - :child (if top-right-renderer [top-right-renderer] "")]) + [top-right-renderer column-header-height cmerger] + (add-map-to-hiccup-call + (cmerger :top-right) + [box/box ;; content component + :src (at) + :height (px (or column-header-height 0)) + :child (if top-right-renderer [top-right-renderer] "")])) ;; ================================================================================== SECTION 8 - row-footers @@ -435,53 +413,45 @@ - rows a vector of row maps (or objects) to render the row-footers from - scroll-y current horizontal scrollbar position in px " - [row-footer-renderer key-fn top-row-index rows scroll-y class style attr] - [box/v-box - :src (at) - :class (str "rc-v-table-row-footer-content rc-v-table-content " class) - :style (merge {:margin-top (px scroll-y :negative)} - style) - :attr attr - :children (map + [row-footer-renderer key-fn top-row-index rows scroll-y cmerger] + (add-map-to-hiccup-call + (cmerger :row-footer-content {:scroll-y scroll-y}) + [box/v-box + :src (at) + :children (map (fn [index row] ^{:key (if key-fn (key-fn row) index)} [row-footer-renderer index row]) (iterate inc top-row-index) - rows)]) + rows)])) (defn row-footer-viewport "Render section 8 - the viewport component (which renders the content component as its child)" [row-footer-renderer key-fn top-row-index rows scroll-y row-viewport-height content-rows-height - class style attr - content-class content-style content-attr] - [box/box ;; viewport component - :src (at) - :class (str "rc-v-table-row-footers rc-v-table-viewport " class) - :style (merge {:overflow "hidden" - :max-height (px content-rows-height)} - style) - :attr attr - :size (if row-viewport-height "none" "auto") - :height (when row-viewport-height (px row-viewport-height)) - :child (if row-footer-renderer - [row-footer-content row-footer-renderer key-fn top-row-index rows scroll-y content-class content-style content-attr] ;; content component - "")]) + cmerger] + (add-map-to-hiccup-call + (cmerger :row-footers {:max-height content-rows-height}) + [box/box ;; viewport component + :src (at) + :size (if row-viewport-height "none" "auto") + :height (when row-viewport-height (px row-viewport-height)) + :child (if row-footer-renderer + [row-footer-content row-footer-renderer key-fn top-row-index rows scroll-y cmerger] ;; content component + "")])) ;; ================================================================================== SECTION 9 - bottom-left (defn bottom-right-content "Render section 9 - the content component" - [bottom-right-renderer column-footer-height class style attr] - [box/box ;; content component - :src (at) - :class (str "rc-v-table-bottom-right rc-v-table-content " class) - :style (merge {:overflow "hidden"} - style) - :attr attr - :height (px (or column-footer-height 0)) - :child (if bottom-right-renderer [bottom-right-renderer] "")]) + [bottom-right-renderer column-footer-height cmerger] + (add-map-to-hiccup-call + (cmerger :bottom-right) + [box/box ;; content component + :src (at) + :height (px (or column-footer-height 0)) + :child (if bottom-right-renderer [bottom-right-renderer] "")])) ;;============================ PUBLIC API =================================== @@ -1000,20 +970,13 @@ (- @sel-content-y-start @scroll-y height)) left (if selecting-right? (- @sel-content-x-start @scroll-x) - (- @sel-content-x-start @scroll-x width))] + (- @sel-content-x-start @scroll-x width)) + cmerger (merge-css + v-table-selection-css-desc + {:class class :style style :attr attr})] [:div - (merge - {:class (str "rc-v-table-selection " class) - :style (merge {:position "absolute" - :z-index 1 - :top (px top) - :left (px left) - :width (px width) - :height (px height) - :background-color "rgba(0,0,255,0.1)" - :border "1px solid rgba(0,0,255,0.4)"} - style)} - attr) + (cmerger :main {:width width :height height + :top top :left left}) ""])) coords-debug (reagent/atom nil) ;; Handy when debugging - used to show selection coords on the left-hand debug section @@ -1221,312 +1184,262 @@ ;; TODO: [DJ] Suggested that the many merges below could be placed in the let above as reaction for performance improvements (readability would suffer a bit) - [box/h-box - :src src - :debug-as (or debug-as (reflect-current-component)) - :class (str "rc-v-table " class " " (get-in parts [:wrapper :class])) - :style (merge - {:max-width max-width ;; Can't do equivalent of :max-height because we don't know column-header-width or column-footer-width - :max-height (when remove-empty-row-space? - (+ - (or column-header-height 0) - (or max-row-viewport-height (inc @content-rows-height)) ;; TODO: The inc prevents content scrollbar. Need to inc more if more than 1px borders specified - (or column-footer-height 0) - scrollbar-tot-thick))} - - ;; TODO: Currently, scrolling a v-table with the mouse wheel also scrolls parent scrollbars (usually the one on the ) - ;; The solution seems to be to use CSS overscroll-behavior - ;; https://developers.google.com/web/updates/2017/11/overscroll-behavior - ;; The following should be in the right place but it makes no difference (also tried the block version) - ;; More research required to solve this - - ;:overscroll-behavior "contain" - ;:overscroll-behavior-block "none" - - (get-in parts [:wrapper :style])) - :attr (merge {:on-wheel (handler-fn (on-wheel event))} - (get-in parts [:wrapper :attr])) - :size "auto" - :children [ - ;; ========== LEFT SECTION (1, 2, 3) - row header area - - [box/v-box - :src (at) - :class (str "rc-v-table-left-section " (get-in parts [:left-section :class])) - :style (get-in parts [:left-section :style]) - :attr (get-in parts [:left-section :attr]) - :children [ - ;; ========== SECTION 1 - top-left - - [top-left-content - top-left-renderer - ;----------------- - column-header-height - ;----------------- - (get-in parts [:top-left :class]) - (get-in parts [:top-left :style]) - (get-in parts [:top-left :attr])] - - ;; ========== SECTION 2 - row-headers - - [row-header-viewport - row-header-renderer - key-fn - @top-row-index - (if virtual? @virtual-rows @model) ;; rows - (if virtual? @virtual-scroll-y @scroll-y) ;; scroll-y - ;----------------- - row-header-selection-fn - selection-fns - (and row-header-selection-fn @sel-parent-bounding-rect (= @selection-target :row-header)) ;; selection-allowed? - ;----------------- - row-viewport-height - @content-rows-height - ;----------------- - (get-in parts [:row-headers :class]) - (get-in parts [:row-headers :style]) - (get-in parts [:row-headers :attr]) - (get-in parts [:row-header-selection-rect :class]) - (get-in parts [:row-header-selection-rect :style]) - (get-in parts [:row-header-selection-rect :attr]) - (get-in parts [:row-header-content :class]) - (get-in parts [:row-header-content :style]) - (get-in parts [:row-header-content :attr])] - - ;; ========== SECTION 3 - bottom-left - - [bottom-left-content - bottom-left-renderer - ;----------------- - column-footer-height - ;----------------- - (get-in parts [:bottom-left :class]) - (get-in parts [:bottom-left :style]) - (get-in parts [:bottom-left :attr])] - - [box/gap - :src (at) - :size (px scrollbar-tot-thick)]]] - - ;; ========== MIDDLE SECTION (4, 5, 6) - column header/footer and content area - - [box/v-box - :src (at) - :class (str "rc-v-table-middle-section " (get-in parts [:middle-section :class])) - :style (merge {:max-width (px @content-rows-width)} - (get-in parts [:middle-section :style])) - :attr (get-in parts [:middle-section :attr]) - :size (if row-viewport-width "none" "auto") - :children [ - ;; ========== SECTION 4 - column-headers - - [column-header-viewport - column-header-renderer - @scroll-x - ;----------------- - column-header-selection-fn - selection-fns - (and column-header-selection-fn @sel-parent-bounding-rect (= @selection-target :column-header)) ;; selection-allowed? - ;----------------- - row-viewport-width - column-header-height - @content-rows-width - ;----------------- - (get-in parts [:column-headers :class]) - (get-in parts [:column-headers :style]) - (get-in parts [:column-headers :attr]) - (get-in parts [:column-header-selection-rect :class]) - (get-in parts [:column-header-selection-rect :style]) - (get-in parts [:column-header-selection-rect :attr]) - (get-in parts [:column-header-content :class]) - (get-in parts [:column-header-content :style]) - (get-in parts [:column-header-content :attr])] - - ;; ========== SECTION 5 - rows (main content area) - - [row-viewport - row-renderer - key-fn - @top-row-index - (if virtual? @virtual-rows @model) ;; rows - @scroll-x - (if virtual? @virtual-scroll-y @scroll-y) ;; scroll-y - ;----------------- - row-selection-fn - selection-fns - (and row-selection-fn @sel-parent-bounding-rect (= @selection-target :row)) ;; selection-allowed? - ;----------------- - row-viewport-height - row-viewport-width - row-viewport-id - @content-rows-height - @content-rows-width - ;----------------- - (get-in parts [:rows :class]) - (get-in parts [:rows :style]) - (get-in parts [:rows :attr]) - (get-in parts [:row-selection-rect :class]) - (get-in parts [:row-selection-rect :style]) - (get-in parts [:row-selection-rect :attr]) - (get-in parts [:row-content :class]) - (get-in parts [:row-content :style]) - (get-in parts [:row-content :attr])] - - ;; ========== SECTION 6 - column-footers - - [column-footer-viewport - column-footer-renderer - @scroll-x - ;----------------- - row-viewport-width - column-footer-height - ;----------------- - (get-in parts [:column-footers :class]) - (get-in parts [:column-footers :style]) - (get-in parts [:column-footers :attr]) - (get-in parts [:column-footer-content :class]) - (get-in parts [:column-footer-content :style]) - (get-in parts [:column-footer-content :attr])] - - ;; ========== Horizontal scrollbar section - - [scrollbar - :src (at) - :class (str "rc-v-table-h-scroll " (get-in parts [:h-scroll :class])) - :type :horizontal - :length @rl-row-viewport-width - :width scrollbar-thickness - :content-length @content-rows-width - :scroll-pos @scroll-x - :on-change on-h-scroll-change - :style (merge {:margin (px-n scrollbar-margin 0)} - (get-in parts [:h-scroll :style])) - :attr (get-in parts [:h-scroll :attr])]]] - - ;; ========== Right section (7, 8, 9) - row footer area - - [box/v-box - :src (at) - :class (str "rc-v-table-right-section " (get-in parts [:right-section :class])) - :style (get-in parts [:right-section :style]) - :attr (get-in parts [:right-section :attr]) - :children [ - ;; ========== SECTION 7 - top-right - - [top-right-content - top-right-renderer - ;----------------- - column-header-height - ;----------------- - (get-in parts [:top-right :class]) - (get-in parts [:top-right :style]) - (get-in parts [:top-right :attr])] - - ;; ========== SECTION 8 - row-footers - - [row-footer-viewport - row-footer-renderer - key-fn - @top-row-index - (if virtual? @virtual-rows @model) ;; rows - (if virtual? @virtual-scroll-y @scroll-y) ;; scroll-y - ;----------------- - row-viewport-height - @content-rows-height - ;----------------- - (get-in parts [:row-footers :class]) - (get-in parts [:row-footers :style]) - (get-in parts [:row-footers :attr]) - (get-in parts [:row-footer-content :class]) - (get-in parts [:row-footer-content :style]) - (get-in parts [:row-footer-content :attr])] - - ;; ========== SECTION 9 - bottom-right - - [bottom-right-content - bottom-right-renderer - ;----------------- - column-footer-height - ;----------------- - (get-in parts [:bottom-right :class]) - (get-in parts [:bottom-right :style]) - (get-in parts [:bottom-right :attr])] - - [box/gap - :src (at) - :size (px scrollbar-tot-thick)]]] - - ;; ========== Vertical scrollbar section - - [box/v-box - :src (at) - :class (str "rc-v-table-v-scroll-section " (get-in parts [:v-scroll-section :class])) - :style (get-in parts [:v-scroll-section :style]) - :attr (get-in parts [:v-scroll-section :attr]) - :children [[box/gap - :src (at) - :size (px (or column-header-height 0))] - [box/box - :src (at) - :size "auto" - :child [scrollbar - :src (at) - :class (str "rc-v-table-v-scroll " (get-in parts [:v-scroll :class])) - :type :vertical - :length @rl-row-viewport-height - :width scrollbar-thickness - :content-length @content-rows-height - :scroll-pos @scroll-y - :on-change on-v-scroll-change - :style (merge {:margin (px-n 0 scrollbar-margin)} - (get-in parts [:v-scroll :style])) - :attr (get-in parts [:v-scroll :attr])]] - [box/gap - :src (at) - :size (px (or column-footer-height 0))] - [box/gap - :src (at) - :size (px scrollbar-tot-thick)]]] - - ;; ========== Debug section - - #_[:pre - {:style {:font-size "11px" - :min-width "220px"}} - (str - "virtual?: " virtual? "\n" - "row-height: " row-height "\n" - "rows-per-viewport: " @rows-per-viewport "\n" - "rows: " (if virtual? (count @virtual-rows) (count @model)) " of " (count @model) "\n" - "\n" - - "top-row-index: " @top-row-index "\n" - "bot-row-index: " @bot-row-index "\n" - "max-scroll-y: " @max-scroll-y "\n" - "scroll-y: " @scroll-y "\n" - "v-scroll-y: " @virtual-scroll-y "\n" - "\n" - - "left-col-px: " @scroll-x "\n" - "right-col-px: " (+ @scroll-x @rl-row-viewport-width -1) "\n" - "max-scroll-x: " @max-scroll-x "\n" - "scroll-x: " @scroll-x "\n" - "\n" - - "selection-target: " (if @dragging? @selection-target "-") "\n" - "sel-parent-l/t: " (if @dragging? (str "(" (.-left @sel-parent-bounding-rect) "," (.-top @sel-parent-bounding-rect) ")") "-") "\n" - "sel-parent-r/b: " (if @dragging? (str "(" (.-right @sel-parent-bounding-rect) "," (.-bottom @sel-parent-bounding-rect) ")") "-") "\n" - "sel-parent-w/h: " (if @dragging? (str "(" (.-width @sel-parent-bounding-rect) "," (.-height @sel-parent-bounding-rect) ")") "-") "\n" - "\n" - - "sel-x/y-start: " (if @dragging? (str "(" @sel-content-x-start "," @sel-content-y-start ")") "-") "\n" - "sel-x/y-end: " (if @dragging? (str "(" @sel-content-x-end "," @sel-content-y-end ")") "-") "\n" - "dragging-outside?: " @dragging-outside? "\n" - "sel-rows: " (if @dragging? (str "(" (:start-row @coords-debug) "," (:end-row @coords-debug) ")") "-") "\n" - "sel-cols: " (if @dragging? (str "(" (:start-col @coords-debug) "," (:end-col @coords-debug) ")") "-") "\n" - "clientXY: " (if @dragging? (str "(" (.-clientX @event-debug) "," (.-clientY @event-debug) ")") "-") "\n" - - "viewport-wh: " (str "(" (.-innerWidth js/window) "," (.-innerHeight js/window) ")") "\n" - "content-rows-wh: " (str "(" @content-rows-width "," @content-rows-height ")") "\n")]]])))}))))) + (let [cmerger (merge-css v-table-css-desc args)] + (add-map-to-hiccup-call + (cmerger :main {:max-width max-width ;; Can't do equivalent of :max-height because we don't know column-header-width or column-footer-width + :max-height + (when remove-empty-row-space? + (+ + (or column-header-height 0) + (or max-row-viewport-height (inc @content-rows-height)) ;; TODO: The inc prevents content scrollbar. Need to inc more if more than 1px borders specified + (or column-footer-height 0) + scrollbar-tot-thick)) + + ;; TODO: Currently, scrolling a v-table with the mouse wheel also scrolls parent scrollbars (usually the one on the ) + ;; The solution seems to be to use CSS overscroll-behavior + ;; https://developers.google.com/web/updates/2017/11/overscroll-behavior + ;; The following should be in the right place but it makes no difference (also tried the block version) + ;; More research required to solve this + + ;:overscroll-behavior "contain" + ;:overscroll-behavior-block "none" + + + :attr {:on-wheel (handler-fn (on-wheel event))}}) + [box/h-box + :src src + :debug-as (or debug-as (reflect-current-component)) + :size "auto" + :children [ + ;; ========== LEFT SECTION (1, 2, 3) - row header area + + (add-map-to-hiccup-call + (cmerger :left-section) + [box/v-box + :src (at) + :children [ + ;; ========== SECTION 1 - top-left + + [top-left-content + top-left-renderer + ;----------------- + column-header-height + ;----------------- + cmerger] + + ;; ========== SECTION 2 - row-headers + + [row-header-viewport + row-header-renderer + key-fn + @top-row-index + (if virtual? @virtual-rows @model) ;; rows + (if virtual? @virtual-scroll-y @scroll-y) ;; scroll-y + ;----------------- + row-header-selection-fn + selection-fns + (and row-header-selection-fn @sel-parent-bounding-rect (= @selection-target :row-header)) ;; selection-allowed? + ;----------------- + row-viewport-height + @content-rows-height + ;----------------- + cmerger] + + ;; ========== SECTION 3 - bottom-left + + [bottom-left-content + bottom-left-renderer + ;----------------- + column-footer-height + ;----------------- + cmerger] + + [box/gap + :src (at) + :size (px scrollbar-tot-thick)]]]) + + ;; ========== MIDDLE SECTION (4, 5, 6) - column header/footer and content area + + (add-map-to-hiccup-call + (cmerger :middle-section {:max-width @content-rows-width}) + [box/v-box + :src (at) + :size (if row-viewport-width "none" "auto") + :children [ + ;; ========== SECTION 4 - column-headers + + [column-header-viewport + column-header-renderer + @scroll-x + ;----------------- + column-header-selection-fn + selection-fns + (and column-header-selection-fn @sel-parent-bounding-rect (= @selection-target :column-header)) ;; selection-allowed? + ;----------------- + row-viewport-width + column-header-height + @content-rows-width + ;----------------- + cmerger] + + ;; ========== SECTION 5 - rows (main content area) + + [row-viewport + row-renderer + key-fn + @top-row-index + (if virtual? @virtual-rows @model) ;; rows + @scroll-x + (if virtual? @virtual-scroll-y @scroll-y) ;; scroll-y + ;----------------- + row-selection-fn + selection-fns + (and row-selection-fn @sel-parent-bounding-rect (= @selection-target :row)) ;; selection-allowed? + ;----------------- + row-viewport-height + row-viewport-width + row-viewport-id + @content-rows-height + @content-rows-width + ;----------------- + cmerger] + + ;; ========== SECTION 6 - column-footers + + [column-footer-viewport + column-footer-renderer + @scroll-x + ;----------------- + row-viewport-width + column-footer-height + ;----------------- + cmerger] + + ;; ========== Horizontal scrollbar section + + (add-map-to-hiccup-call + (cmerger :h-scroll {:scrollbar-margin scrollbar-margin}) + [scrollbar + :src (at) + :type :horizontal + :length @rl-row-viewport-width + :width scrollbar-thickness + :content-length @content-rows-width + :scroll-pos @scroll-x + :on-change on-h-scroll-change])]]) + + ;; ========== Right section (7, 8, 9) - row footer area + + (add-map-to-hiccup-call + (cmerger :right-section) + [box/v-box + :src (at) + :children [ + ;; ========== SECTION 7 - top-right + + [top-right-content + top-right-renderer + ;----------------- + column-header-height + ;----------------- + cmerger] + + ;; ========== SECTION 8 - row-footers + + [row-footer-viewport + row-footer-renderer + key-fn + @top-row-index + (if virtual? @virtual-rows @model) ;; rows + (if virtual? @virtual-scroll-y @scroll-y) ;; scroll-y + ;----------------- + row-viewport-height + @content-rows-height + ;----------------- + cmerger] + + ;; ========== SECTION 9 - bottom-right + + [bottom-right-content + bottom-right-renderer + ;----------------- + column-footer-height + ;----------------- + cmerger] + + [box/gap + :src (at) + :size (px scrollbar-tot-thick)]]]) + + ;; ========== Vertical scrollbar section + + (add-map-to-hiccup-call + (cmerger :v-scroll-section) + [box/v-box + :src (at) + :children [[box/gap + :src (at) + :size (px (or column-header-height 0))] + [box/box + :src (at) + :size "auto" + :child (add-map-to-hiccup-call + (cmerger + :v-scroll {:scrollbar-margin scrollbar-margin}) + [scrollbar + :src (at) + :type :vertical + :length @rl-row-viewport-height + :width scrollbar-thickness + :content-length @content-rows-height + :scroll-pos @scroll-y + :on-change on-v-scroll-change])] + [box/gap + :src (at) + :size (px (or column-footer-height 0))] + [box/gap + :src (at) + :size (px scrollbar-tot-thick)]]]) + + ;; ========== Debug section + + #_[:pre + {:style {:font-size "11px" + :min-width "220px"}} + (str + "virtual?: " virtual? "\n" + "row-height: " row-height "\n" + "rows-per-viewport: " @rows-per-viewport "\n" + "rows: " (if virtual? (count @virtual-rows) (count @model)) " of " (count @model) "\n" + "\n" + + "top-row-index: " @top-row-index "\n" + "bot-row-index: " @bot-row-index "\n" + "max-scroll-y: " @max-scroll-y "\n" + "scroll-y: " @scroll-y "\n" + "v-scroll-y: " @virtual-scroll-y "\n" + "\n" + + "left-col-px: " @scroll-x "\n" + "right-col-px: " (+ @scroll-x @rl-row-viewport-width -1) "\n" + "max-scroll-x: " @max-scroll-x "\n" + "scroll-x: " @scroll-x "\n" + "\n" + + "selection-target: " (if @dragging? @selection-target "-") "\n" + "sel-parent-l/t: " (if @dragging? (str "(" (.-left @sel-parent-bounding-rect) "," (.-top @sel-parent-bounding-rect) ")") "-") "\n" + "sel-parent-r/b: " (if @dragging? (str "(" (.-right @sel-parent-bounding-rect) "," (.-bottom @sel-parent-bounding-rect) ")") "-") "\n" + "sel-parent-w/h: " (if @dragging? (str "(" (.-width @sel-parent-bounding-rect) "," (.-height @sel-parent-bounding-rect) ")") "-") "\n" + "\n" + + "sel-x/y-start: " (if @dragging? (str "(" @sel-content-x-start "," @sel-content-y-start ")") "-") "\n" + "sel-x/y-end: " (if @dragging? (str "(" @sel-content-x-end "," @sel-content-y-end ")") "-") "\n" + "dragging-outside?: " @dragging-outside? "\n" + "sel-rows: " (if @dragging? (str "(" (:start-row @coords-debug) "," (:end-row @coords-debug) ")") "-") "\n" + "sel-cols: " (if @dragging? (str "(" (:start-col @coords-debug) "," (:end-col @coords-debug) ")") "-") "\n" + "clientXY: " (if @dragging? (str "(" (.-clientX @event-debug) "," (.-clientY @event-debug) ")") "-") "\n" + + "viewport-wh: " (str "(" (.-innerWidth js/window) "," (.-innerHeight js/window) ")") "\n" + "content-rows-wh: " (str "(" @content-rows-width "," @content-rows-height ")") "\n")]]])))))}))))) ;"call-count: " @call-count "\n" From d56b9e61927d5a3ab364d69155ccad818b1f6753 Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Fri, 14 Oct 2022 07:22:04 -0700 Subject: [PATCH 05/65] Bugfix Worked in original, but evidently we can get a null here. --- src/re_com/buttons.cljs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/re_com/buttons.cljs b/src/re_com/buttons.cljs index c4197683..fb6e6036 100644 --- a/src/re_com/buttons.cljs +++ b/src/re_com/buttons.cljs @@ -202,7 +202,8 @@ ["noselect" "rc-md-icon-button" (case size :smaller "rc-icon-smaller" - :larger "rc-icon-larger") + :larger "rc-icon-larger" + "rc-icon-larger") (when emphasise? "rc-icon-emphasis") (when disabled? "rc-icon-disabled")]) :style From 94922f8f2c87c816fdecaab4e339783533186c85 Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Fri, 14 Oct 2022 07:27:26 -0700 Subject: [PATCH 06/65] Temporary debugging stuff --- src/re_com/v_table.cljs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/re_com/v_table.cljs b/src/re_com/v_table.cljs index c9c7bbed..8621f5ec 100644 --- a/src/re_com/v_table.cljs +++ b/src/re_com/v_table.cljs @@ -922,6 +922,8 @@ [event] (let [target (-> event .-target) bounding-rect (if (nil? target) {} (.getBoundingClientRect target))] + (println "in on-viewport-resize") + (println (.-height bounding-rect)) (reset! rl-row-viewport-width (or row-viewport-width (.-width bounding-rect))) (reset! rl-row-viewport-height (or row-viewport-height (.-height bounding-rect))) (reset! scroll-x (max 0 (min @max-scroll-x @scroll-x))) @@ -1403,7 +1405,7 @@ ;; ========== Debug section - #_[:pre + [:pre {:style {:font-size "11px" :min-width "220px"}} (str From 979363c1add8dbbd9183ca3d49802849bb187cfe Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Wed, 19 Oct 2022 09:29:25 -0700 Subject: [PATCH 07/65] Bugfix - Didn't detect malformed -css-desc --- src/re_com/util.cljs | 3 +++ src/re_com/v_table.cljs | 12 +++++------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/re_com/util.cljs b/src/re_com/util.cljs index cdeede67..48a505e9 100644 --- a/src/re_com/util.cljs +++ b/src/re_com/util.cljs @@ -221,6 +221,9 @@ (defn merge-css [css-desc {:as params :keys [class style parts]}] + (for [[k v] css-desc + :when (not (and (keyword? k) (map? v)))] + (throw (js/Error. "CSS description must contain only keywords and maps"))) (defn fetch-merged-css ([tag] (fetch-merged-css tag {})) diff --git a/src/re_com/v_table.cljs b/src/re_com/v_table.cljs index 8621f5ec..6f52df2a 100644 --- a/src/re_com/v_table.cljs +++ b/src/re_com/v_table.cljs @@ -485,7 +485,7 @@ {:name :v-scroll :level 3 :class "rc-v-table-v-scroll" :impl "[box]" :notes "The vertical scrollbar"}])) (def v-table-css-desc - {:main + {:main {} :wrapper {:class ["rc-v-table"] :style (fn [{:keys [max-width max-height]}] {:max-width max-width :max-height max-height})} @@ -498,7 +498,7 @@ {:position "relative" :overflow "hidden" :max-height (px max-height)})} - :row-header-selection-rect + :row-header-selection-rect {} :row-header-content {:class ["rc-v-table-row-header-content" "rc-v-table-content"] :style (fn [{:keys [scroll-y]}] {:margin-top (px scroll-y :negative)})} @@ -511,7 +511,7 @@ :column-headers {:class ["rc-v-table-column-headers" "rc-v-table-viewport"] :style {:overflow "hidden" :position "relative"}} - :column-header-selection-rect + :column-header-selection-rect {} :column-header-content {:class ["rc-v-table-column-header-content" "rc-v-table-content"] :style (fn [{:keys [scroll-x]}] {:margin-left (px scroll-x :negative)})} @@ -521,7 +521,7 @@ {:overflow "hidden" :position "relative" :max-height (px max-height)})} - :row-selection-rect + :row-selection-rect {} :row-content {:class ["rc-v-table-row-content" "rc-v-table-content"] :style (fn [{:keys [scroll-x scroll-y]}] {:margin-left (px scroll-x :negative) @@ -922,8 +922,6 @@ [event] (let [target (-> event .-target) bounding-rect (if (nil? target) {} (.getBoundingClientRect target))] - (println "in on-viewport-resize") - (println (.-height bounding-rect)) (reset! rl-row-viewport-width (or row-viewport-width (.-width bounding-rect))) (reset! rl-row-viewport-height (or row-viewport-height (.-height bounding-rect))) (reset! scroll-x (max 0 (min @max-scroll-x @scroll-x))) @@ -1405,7 +1403,7 @@ ;; ========== Debug section - [:pre + #_ [:pre {:style {:font-size "11px" :min-width "220px"}} (str From a0e530b9ed346c9bb8e6bdd01ae84903b6515c32 Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Mon, 24 Oct 2022 08:14:07 -0700 Subject: [PATCH 08/65] Add :use-toplevel option - Sometimes a non :main component should use the toplevel :class and :style fields as well as the fields supplied in the parts structure. Adding `:use-toplevel true` to the -css-desc for a particular component will allow this. :use-toplevel can also be set to `false` for :main. --- src/re_com/util.cljs | 49 ++++++++++++++++++----------------------- src/re_com/v_table.cljs | 8 +++---- 2 files changed, 26 insertions(+), 31 deletions(-) diff --git a/src/re_com/util.cljs b/src/re_com/util.cljs index 48a505e9..cafce36d 100644 --- a/src/re_com/util.cljs +++ b/src/re_com/util.cljs @@ -224,39 +224,34 @@ (for [[k v] css-desc :when (not (and (keyword? k) (map? v)))] (throw (js/Error. "CSS description must contain only keywords and maps"))) + + (defn combine-css [a b] + (let [a (or a {}) + b (or b {}) + acl (:class a) + bcl (:class b) + class (reduce into [] [(if (string? acl) [acl] acl) (if (string? bcl) [bcl] bcl)]) + style (reduce into {} [(:style a) (:style b)]) + attr (reduce into {} [(:attr a) (:attr b)])] + (into {} + [(when-not (empty? class) [:class class]) + (when-not (empty? style) [:style style]) + (when-not (empty? attr) [:attr attr])]))) + (defn fetch-merged-css ([tag] (fetch-merged-css tag {})) ([tag options] (let [xoptions (reduce (partial dissoc options) [:class :style :attr]) defaults (get css-desc (or tag :main)) - user (if (and tag (not (= tag :main))) - (get parts tag) - {:class class :style style})] - (into {} - [(let [d (:class defaults) - d (if (fn? d) (d xoptions) d) - u (:class user) - u (if (string? u) [u] u) - o (:class options) - o (if (string? o) [o] o) - res (reduce into [] [d u o])] - (when-not (empty? res) - [:class res])) - (let [d (:style defaults) - d (if (fn? d) (d xoptions) d) - u (:style user) - o (:style options) - res (reduce into {} [d u o])] - (when-not (empty? res) - [:style res])) - (let [d (:attr defaults) - d (if (fn? d) (d xoptions) d) - u (:attr user) - o (:attr options) - res (reduce into {} [d u o])] - (when-not (empty? res) - [:attr res]))])))) + use-toplevel (get :use-toplevel defaults (if (= tag :main) true false)) + user (combine-css (get parts tag) + (and use-toplevel {:class class :style style})) + defaults (into {} (for [k [:class :style :attr] + :when (contains? defaults k) + :let [v (get defaults k)]] + [k (if (fn? v) (v xoptions) v)]))] + (reduce combine-css [defaults options user])))) fetch-merged-css) (defn add-map-to-hiccup-call [map hiccup] diff --git a/src/re_com/v_table.cljs b/src/re_com/v_table.cljs index 6f52df2a..6169fa57 100644 --- a/src/re_com/v_table.cljs +++ b/src/re_com/v_table.cljs @@ -485,10 +485,10 @@ {:name :v-scroll :level 3 :class "rc-v-table-v-scroll" :impl "[box]" :notes "The vertical scrollbar"}])) (def v-table-css-desc - {:main {} - :wrapper {:class ["rc-v-table"] + {:wrapper {:class ["rc-v-table"] :style (fn [{:keys [max-width max-height]}] - {:max-width max-width :max-height max-height})} + {:max-width max-width :max-height max-height}) + :use-toplevel true} :left-section {:class ["rc-v-table-left-section"]} :top-left {:class ["rc-v-table-top-left" "rc-v-table-content"] @@ -1186,7 +1186,7 @@ (let [cmerger (merge-css v-table-css-desc args)] (add-map-to-hiccup-call - (cmerger :main {:max-width max-width ;; Can't do equivalent of :max-height because we don't know column-header-width or column-footer-width + (cmerger :wrapper {:max-width max-width ;; Can't do equivalent of :max-height because we don't know column-header-width or column-footer-width :max-height (when remove-empty-row-space? (+ From 4587072993777b112f6f1fbae6d657ae59e77c6c Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Wed, 26 Oct 2022 06:35:36 -0700 Subject: [PATCH 09/65] Bugfixes for md-circle-icon --- src/re_com/buttons.cljs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/re_com/buttons.cljs b/src/re_com/buttons.cljs index fb6e6036..16a3af40 100644 --- a/src/re_com/buttons.cljs +++ b/src/re_com/buttons.cljs @@ -108,7 +108,8 @@ ["noselect" "rc-md-circle-icon-button" (case size :smaller "rc-circle-smaller" - :larger "rc-circle-larger") + :larger "rc-circle-larger" + nil) (when emphasise? "rc-circle-emphasis") (when disabled? "rc-circle-disabled")]) :style @@ -166,7 +167,7 @@ {:on-mouse-over (handler-fn (reset! showing? true)) :on-mouse-out (handler-fn (reset! showing? false))}) attr) - [:i (cmerger :main {:md-icon-name md-icon-name})]]] + [:i (cmerger :icon {:md-icon-name md-icon-name})]]] (add-map-to-hiccup-call (cmerger :wrapper) [box From 5c08e113bc4698af8715504fd001344483d00e6c Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Wed, 26 Oct 2022 07:42:43 -0700 Subject: [PATCH 10/65] Merge-css: spread :attr - Contents of the :attr field should generally be spread in the target element, not confined to the :attr keyword of the component. There may be exceptions, but we'll start with this. --- .gitignore | 2 +- src/re_com/util.cljs | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 71fa104b..12c18977 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,4 @@ misc/ /.shadow-cljs/ /shadow-cljs.edn /.clj-kondo/ -/.lsp/ \ No newline at end of file +/.lsp/ diff --git a/src/re_com/util.cljs b/src/re_com/util.cljs index cafce36d..f3fba315 100644 --- a/src/re_com/util.cljs +++ b/src/re_com/util.cljs @@ -250,8 +250,11 @@ defaults (into {} (for [k [:class :style :attr] :when (contains? defaults k) :let [v (get defaults k)]] - [k (if (fn? v) (v xoptions) v)]))] - (reduce combine-css [defaults options user])))) + [k (if (fn? v) (v xoptions) v)])) + res (reduce combine-css [defaults options user]) + attr (:attr res) + res (dissoc res :attr)] + (merge res attr)))) fetch-merged-css) (defn add-map-to-hiccup-call [map hiccup] From adc835d59924d56dcb92e564263bc60ba3a36d85 Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Wed, 26 Oct 2022 08:12:14 -0700 Subject: [PATCH 11/65] Add :spread-attr as an option --- src/re_com/util.cljs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/re_com/util.cljs b/src/re_com/util.cljs index f3fba315..4ba798f5 100644 --- a/src/re_com/util.cljs +++ b/src/re_com/util.cljs @@ -241,7 +241,7 @@ (defn fetch-merged-css ([tag] (fetch-merged-css tag {})) - ([tag options] + ([tag options & [{:keys [flatten-attr] :or {flatten-attr true}}]] (let [xoptions (reduce (partial dissoc options) [:class :style :attr]) defaults (get css-desc (or tag :main)) use-toplevel (get :use-toplevel defaults (if (= tag :main) true false)) @@ -251,10 +251,10 @@ :when (contains? defaults k) :let [v (get defaults k)]] [k (if (fn? v) (v xoptions) v)])) - res (reduce combine-css [defaults options user]) - attr (:attr res) - res (dissoc res :attr)] - (merge res attr)))) + res (reduce combine-css [defaults options user])] + (if flatten-attr + (merge (dissoc res :attr) (:attr res)) + res)))) fetch-merged-css) (defn add-map-to-hiccup-call [map hiccup] From 67e8a61b3c0a2cacab5eec1a3511c5836f3fe442 Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Thu, 27 Oct 2022 10:32:26 -0700 Subject: [PATCH 12/65] Bugfixes - V-table demo working. Add `:flatten-attr false` to re-com component calls - Correct parameters to merge-css --- src/re_com/util.cljs | 6 ++++-- src/re_com/v_table.cljs | 44 +++++++++++++++++++++++------------------ 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/src/re_com/util.cljs b/src/re_com/util.cljs index 4ba798f5..6eecfa10 100644 --- a/src/re_com/util.cljs +++ b/src/re_com/util.cljs @@ -240,8 +240,10 @@ (defn fetch-merged-css ([tag] - (fetch-merged-css tag {})) - ([tag options & [{:keys [flatten-attr] :or {flatten-attr true}}]] + (fetch-merged-css tag {} {})) + ([tag options] + (fetch-merged-css tag options {})) + ([tag options {:keys [flatten-attr] :or {flatten-attr true}}] (let [xoptions (reduce (partial dissoc options) [:class :style :attr]) defaults (get css-desc (or tag :main)) use-toplevel (get :use-toplevel defaults (if (= tag :main) true false)) diff --git a/src/re_com/v_table.cljs b/src/re_com/v_table.cljs index 6169fa57..2f968999 100644 --- a/src/re_com/v_table.cljs +++ b/src/re_com/v_table.cljs @@ -146,7 +146,8 @@ :attr {:on-mouse-enter on-mouse-enter :on-mouse-leave on-mouse-leave ;; TODO: Best way to move this fn to outer fn? (closes over show?) - :on-mouse-down (handler-fn (when show? (scrollbar-mouse-down event)))}}) + :on-mouse-down (handler-fn (when show? (scrollbar-mouse-down event)))}} + {:flatten-attr false}) [box/box :src src :width (if horizontal? @@ -162,7 +163,8 @@ ;; TODO: Best way to move this fn to outer fn? (closes over internal-scroll-pos) :attr {:on-mouse-down (handler-fn - (thumb-mouse-down event internal-scroll-pos))}}) + (thumb-mouse-down event internal-scroll-pos))}} + {:flatten-attr false}) [box/box :src (at) :width (if horizontal? @@ -227,7 +229,8 @@ :attr (when row-header-selection-fn {:on-mouse-down (handler-fn (on-mouse-down :row-header row-header-selection-fn content-rows-height 0 event)) ;; TODO: width set to 0 because we don't have it - could probably measure it :on-mouse-enter (handler-fn (on-mouse-enter :row-header)) - :on-mouse-leave (handler-fn (on-mouse-leave :row-header))})}) + :on-mouse-leave (handler-fn (on-mouse-leave :row-header))})} + {:flatten-attr false}) [box/v-box ;; viewport component :src (at) :size (if row-viewport-height "none" "auto") @@ -283,7 +286,8 @@ (when column-header-selection-fn {:on-mouse-down (handler-fn (on-mouse-down :column-header column-header-selection-fn column-header-height content-rows-width event)) :on-mouse-enter (handler-fn (on-mouse-enter :column-header)) - :on-mouse-leave (handler-fn (on-mouse-leave :column-header))})}) + :on-mouse-leave (handler-fn (on-mouse-leave :column-header))})} + {:flatten-attr false}) [box/v-box ;; viewport component :src (at) :width (when row-viewport-width (px row-viewport-width)) @@ -337,7 +341,8 @@ {:on-mouse-down (handler-fn (on-mouse-down :row row-selection-fn content-rows-height content-rows-width event)) :on-mouse-enter (handler-fn (on-mouse-enter :row)) :on-mouse-leave (handler-fn (on-mouse-leave :row))}) - {:id row-viewport-id})}) ;; Can't be overriding the internally generated id + {:id row-viewport-id})};; Can't be overriding the internally generated id + {:flatten-attr false}) [box/v-box ;; viewport component :src (at) :size (if row-viewport-height "none" "auto") @@ -1187,25 +1192,26 @@ (let [cmerger (merge-css v-table-css-desc args)] (add-map-to-hiccup-call (cmerger :wrapper {:max-width max-width ;; Can't do equivalent of :max-height because we don't know column-header-width or column-footer-width - :max-height - (when remove-empty-row-space? - (+ - (or column-header-height 0) - (or max-row-viewport-height (inc @content-rows-height)) ;; TODO: The inc prevents content scrollbar. Need to inc more if more than 1px borders specified - (or column-footer-height 0) - scrollbar-tot-thick)) - - ;; TODO: Currently, scrolling a v-table with the mouse wheel also scrolls parent scrollbars (usually the one on the ) - ;; The solution seems to be to use CSS overscroll-behavior - ;; https://developers.google.com/web/updates/2017/11/overscroll-behavior - ;; The following should be in the right place but it makes no difference (also tried the block version) - ;; More research required to solve this + :max-height + (when remove-empty-row-space? + (+ + (or column-header-height 0) + (or max-row-viewport-height (inc @content-rows-height)) ;; TODO: The inc prevents content scrollbar. Need to inc more if more than 1px borders specified + (or column-footer-height 0) + scrollbar-tot-thick)) + + ;; TODO: Currently, scrolling a v-table with the mouse wheel also scrolls parent scrollbars (usually the one on the ) + ;; The solution seems to be to use CSS overscroll-behavior + ;; https://developers.google.com/web/updates/2017/11/overscroll-behavior + ;; The following should be in the right place but it makes no difference (also tried the block version) + ;; More research required to solve this ;:overscroll-behavior "contain" ;:overscroll-behavior-block "none" - :attr {:on-wheel (handler-fn (on-wheel event))}}) + :attr {:on-wheel (handler-fn (on-wheel event))}} + {:flatten-attr false}) [box/h-box :src src :debug-as (or debug-as (reflect-current-component)) From 3ad149607fb501e680d945b4a3b4150280b95ac1 Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Fri, 28 Oct 2022 06:00:53 -0700 Subject: [PATCH 13/65] Correct the flatten-attr status of remaining components --- src/re_com/v_table.cljs | 42 +++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/src/re_com/v_table.cljs b/src/re_com/v_table.cljs index 2f968999..d7ff521c 100644 --- a/src/re_com/v_table.cljs +++ b/src/re_com/v_table.cljs @@ -182,7 +182,9 @@ "Render section 1 - the content component" [top-left-renderer column-header-height cmerger] (add-map-to-hiccup-call - (cmerger :top-left) + (cmerger :top-left + {} + {:flatten-attr false}) [box/box ;; content component :src (at) :height (px (or column-header-height 0)) @@ -206,7 +208,7 @@ " [row-header-renderer key-fn top-row-index rows scroll-y cmerger] (add-map-to-hiccup-call - (cmerger :row-header-content {:scroll-y scroll-y}) + (cmerger :row-header-content {:scroll-y scroll-y} {:flatten-attr false}) [box/v-box :src (at) :children (map @@ -236,7 +238,8 @@ :size (if row-viewport-height "none" "auto") :height (when row-viewport-height (px row-viewport-height)) :children [(when selection-allowed? - (let [{:keys [class style attr]} (cmerger :row-header-selection-rect)] + (let [{:keys [class style attr]} + (cmerger :row-header-selection-rect {} {:flatten-attr false})] [selection-renderer class style attr])) ;; selection rectangle component (if row-header-renderer [row-header-content row-header-renderer key-fn top-row-index rows scroll-y cmerger] ;; content component @@ -249,7 +252,7 @@ "Render section 3 - the content component" [bottom-left-renderer column-footer-height cmerger] (add-map-to-hiccup-call - (cmerger :bottom-left) + (cmerger :bottom-left {} {:flatten-attr false}) [box/box ;; content component :src (at) :height (px (or column-footer-height 0)) @@ -269,7 +272,7 @@ " [column-header-renderer scroll-x cmerger] (add-map-to-hiccup-call - (cmerger :column-header-content {:scroll-x scroll-x}) + (cmerger :column-header-content {:scroll-x scroll-x} {:flatten-attr false}) [box/box :src (at) :child [column-header-renderer]])) @@ -293,7 +296,8 @@ :width (when row-viewport-width (px row-viewport-width)) :height (px (or column-header-height 0)) :children [(when selection-allowed? - (let [{:keys [class style attr]} (cmerger :column-header-selection-rect)] + (let [{:keys [class style attr]} + (cmerger :column-header-selection-rect {} {:flatten-attr false})] [selection-renderer class style attr])) ;; selection rectangle component (if column-header-renderer [column-header-content column-header-renderer scroll-x cmerger] ;; content component @@ -317,7 +321,7 @@ " [row-renderer key-fn top-row-index rows scroll-x scroll-y cmerger] (add-map-to-hiccup-call - (cmerger :row-content {:scroll-x scroll-x :scroll-y scroll-y}) + (cmerger :row-content {:scroll-x scroll-x :scroll-y scroll-y} {:flatten-attr false}) [box/v-box :src (at) :children (map @@ -349,7 +353,8 @@ :width (when row-viewport-width (px row-viewport-width)) :height (when row-viewport-height (px row-viewport-height)) :children [(when selection-allowed? - (let [{:keys [class style attr]} (cmerger :row-selection-rect)] + (let [{:keys [class style attr]} + (cmerger :row-selection-rect {} {:flatten-attr false})] [selection-renderer class style attr])) ;; selection rectangle component [row-content row-renderer key-fn top-row-index rows scroll-x scroll-y cmerger]]])) ;; content component @@ -367,7 +372,7 @@ " [column-footer-renderer scroll-x cmerger] (add-map-to-hiccup-call - (cmerger :column-footer-content {:scroll-x scroll-x}) + (cmerger :column-footer-content {:scroll-x scroll-x} {:flatten-attr false}) [box/box :src (at) :child [column-footer-renderer]])) @@ -380,7 +385,7 @@ class style attr content-class content-style content-attr] (add-map-to-hiccup-call - (cmerger :column-footers) + (cmerger :column-footers {} {:flatten-attr false}) [box/box ;; viewport component :src (at) :width (when row-viewport-width (px row-viewport-width)) @@ -396,7 +401,7 @@ "Render section 7 - the content component" [top-right-renderer column-header-height cmerger] (add-map-to-hiccup-call - (cmerger :top-right) + (cmerger :top-right {} {:flatten-attr false}) [box/box ;; content component :src (at) :height (px (or column-header-height 0)) @@ -420,7 +425,7 @@ " [row-footer-renderer key-fn top-row-index rows scroll-y cmerger] (add-map-to-hiccup-call - (cmerger :row-footer-content {:scroll-y scroll-y}) + (cmerger :row-footer-content {:scroll-y scroll-y} {:flatten-attr false}) [box/v-box :src (at) :children (map @@ -436,7 +441,7 @@ row-viewport-height content-rows-height cmerger] (add-map-to-hiccup-call - (cmerger :row-footers {:max-height content-rows-height}) + (cmerger :row-footers {:max-height content-rows-height} {:flatten-attr false}) [box/box ;; viewport component :src (at) :size (if row-viewport-height "none" "auto") @@ -452,7 +457,7 @@ "Render section 9 - the content component" [bottom-right-renderer column-footer-height cmerger] (add-map-to-hiccup-call - (cmerger :bottom-right) + (cmerger :bottom-right {} {:flatten-attr false}) [box/box ;; content component :src (at) :height (px (or column-footer-height 0)) @@ -1220,7 +1225,7 @@ ;; ========== LEFT SECTION (1, 2, 3) - row header area (add-map-to-hiccup-call - (cmerger :left-section) + (cmerger :left-section {} {:flatten-attr false}) [box/v-box :src (at) :children [ @@ -1267,7 +1272,8 @@ ;; ========== MIDDLE SECTION (4, 5, 6) - column header/footer and content area (add-map-to-hiccup-call - (cmerger :middle-section {:max-width @content-rows-width}) + (cmerger :middle-section {:max-width @content-rows-width} + {:flatten-attr false}) [box/v-box :src (at) :size (if row-viewport-width "none" "auto") @@ -1337,7 +1343,7 @@ ;; ========== Right section (7, 8, 9) - row footer area (add-map-to-hiccup-call - (cmerger :right-section) + (cmerger :right-section {} {:flatten-attr false}) [box/v-box :src (at) :children [ @@ -1380,7 +1386,7 @@ ;; ========== Vertical scrollbar section (add-map-to-hiccup-call - (cmerger :v-scroll-section) + (cmerger :v-scroll-section {} {:flatten-attr false}) [box/v-box :src (at) :children [[box/gap From cf863f0d7af3616413270962ab98d9e963d2501c Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Fri, 28 Oct 2022 07:45:56 -0700 Subject: [PATCH 14/65] Refactor merge-css - :flatten-attr parameter was awkward, switch to a separate function to do the job. --- src/re_com/buttons.cljs | 40 ++++++++++++----------- src/re_com/util.cljs | 14 ++++----- src/re_com/v_table.cljs | 70 ++++++++++++++++++----------------------- 3 files changed, 59 insertions(+), 65 deletions(-) diff --git a/src/re_com/buttons.cljs b/src/re_com/buttons.cljs index 16a3af40..b343900b 100644 --- a/src/re_com/buttons.cljs +++ b/src/re_com/buttons.cljs @@ -2,7 +2,7 @@ (:require-macros [re-com.core :refer [handler-fn at reflect-current-component]]) (:require - [re-com.util :refer [deref-or-value px merge-css add-map-to-hiccup-call]] + [re-com.util :refer [deref-or-value px merge-css add-map-to-hiccup-call flatten-attr]] [re-com.config :refer [include-args-desc?]] [re-com.debug :refer [->attr]] [re-com.validate :refer [position? position-options-list button-size? button-sizes-list @@ -61,7 +61,7 @@ cmerger (merge-css button-css-desc args) the-button [:button (merge - (cmerger :main) + (flatten-attr (cmerger :main)) {:disabled disabled? :on-click (handler-fn (when (and on-click (not disabled?)) @@ -159,7 +159,8 @@ (let [cmerger (merge-css md-circle-icon-button-css-desc args) the-button [:div (merge - (cmerger :main {:emphasise? emphasise? :disabled? disabled? :size size}) + (flatten-attr + (cmerger :main {:emphasise? emphasise? :disabled? disabled? :size size})) {:on-click (handler-fn (when (and on-click (not disabled?)) (on-click event)))} @@ -167,7 +168,7 @@ {:on-mouse-over (handler-fn (reset! showing? true)) :on-mouse-out (handler-fn (reset! showing? false))}) attr) - [:i (cmerger :icon {:md-icon-name md-icon-name})]]] + [:i (flatten-attr (cmerger :icon {:md-icon-name md-icon-name}))]]] (add-map-to-hiccup-call (cmerger :wrapper) [box @@ -254,14 +255,15 @@ (let [cmerger (merge-css md-circle-icon-button-css-desc args) the-button [:div (merge - (cmerger :main {:size size :emphasise? emphasise? :disabled? disabled?}) - {:on-click (handler-fn - (when (and on-click (not disabled?)) - (on-click event)))} - (when tooltip - {:on-mouse-over (handler-fn (reset! showing? true)) - :on-mouse-out (handler-fn (reset! showing? false))}) - attr) + (flatten-attr + (cmerger :main {:size size :emphasise? emphasise? :disabled? disabled?})) + {:on-click (handler-fn + (when (and on-click (not disabled?)) + (on-click event)))} + (when tooltip + {:on-mouse-over (handler-fn (reset! showing? true)) + :on-mouse-out (handler-fn (reset! showing? false))}) + attr) [:i (cmerger :icon {:md-icon-name md-icon-name})]]] (add-map-to-hiccup-call (cmerger :wrapper) @@ -342,14 +344,15 @@ :on-cancel #(swap! showing? not) :anchor [:div (merge - (cmerger :main {:disabled? disabled?}) + (flatten-attr + (cmerger :main {:disabled? disabled?})) {:on-click (handler-fn (when (not disabled?) (swap! showing? not)))} attr) [:svg (merge - (cmerger :icon) + (flatten-attr (cmerger :icon)) {:width "11" :height "11"}) [:circle {:cx "5.5" :cy "5.5" :r "5.5"}] @@ -412,7 +415,8 @@ (let [cmerger (merge-css row-button-css-desc args) the-button [:div (merge - (cmerger :main {:mouse-over-row? mouse-over-row? :disabled? disabled?}) + (flatten-attr + (cmerger :main {:mouse-over-row? mouse-over-row? :disabled? disabled?})) {:on-click (handler-fn (when (and on-click (not disabled?)) (on-click event)))} @@ -420,7 +424,7 @@ {:on-mouse-over (handler-fn (reset! showing? true)) :on-mouse-out (handler-fn (reset! showing? false))}) ;; Need to return true to ALLOW default events to be performed attr) - [:i (cmerger :icon {:md-icon-name md-icon-name})]]] + [:i (flatten-attr (cmerger :icon {:md-icon-name md-icon-name}))]]] (add-map-to-hiccup-call (cmerger :wrapper) [box @@ -504,7 +508,7 @@ :align :start :child [:a (merge - (cmerger :main {:disabled? disabled?}) + (flatten-attr (cmerger :main {:disabled? disabled?})) {:on-click (handler-fn (when (and on-click (not disabled?)) (on-click event)))} @@ -591,7 +595,7 @@ disabled? (deref-or-value disabled?) cmerger (merge-css hyperlink-href-css-desc args) the-button [:a - (merge (cmerger :main {:disabled? disabled?}) + (merge (flatten-attr (cmerger :main {:disabled? disabled?})) {:target target} ;; As of HTML5 the href attribute on a elements is not required; when those elements do ;; not have href attributes they do not create hyperlinks. These are also known as a diff --git a/src/re_com/util.cljs b/src/re_com/util.cljs index 6eecfa10..9d5a67ae 100644 --- a/src/re_com/util.cljs +++ b/src/re_com/util.cljs @@ -240,10 +240,8 @@ (defn fetch-merged-css ([tag] - (fetch-merged-css tag {} {})) + (fetch-merged-css tag {})) ([tag options] - (fetch-merged-css tag options {})) - ([tag options {:keys [flatten-attr] :or {flatten-attr true}}] (let [xoptions (reduce (partial dissoc options) [:class :style :attr]) defaults (get css-desc (or tag :main)) use-toplevel (get :use-toplevel defaults (if (= tag :main) true false)) @@ -252,13 +250,13 @@ defaults (into {} (for [k [:class :style :attr] :when (contains? defaults k) :let [v (get defaults k)]] - [k (if (fn? v) (v xoptions) v)])) - res (reduce combine-css [defaults options user])] - (if flatten-attr - (merge (dissoc res :attr) (:attr res)) - res)))) + [k (if (fn? v) (v xoptions) v)]))] + (reduce combine-css [defaults options user])))) fetch-merged-css) +(defn flatten-attr [stuff] + (merge (dissoc stuff :attr) (:attr stuff))) + (defn add-map-to-hiccup-call [map hiccup] (reduce into [[(first hiccup)] (for [[k v] map diff --git a/src/re_com/v_table.cljs b/src/re_com/v_table.cljs index d7ff521c..0a72e4a7 100644 --- a/src/re_com/v_table.cljs +++ b/src/re_com/v_table.cljs @@ -8,7 +8,7 @@ [re-com.config :refer [debug? include-args-desc?]] [re-com.debug :refer [->attr]] [re-com.box :as box] - [re-com.util :as util :refer [deref-or-value px-n add-map-to-hiccup-call merge-css]] + [re-com.util :as util :refer [deref-or-value px-n add-map-to-hiccup-call merge-css flatten-attr]] [re-com.validate :refer [vector-atom? ifn-or-nil? map-atom? parts?]] [re-com.dmm-tracker :refer [make-dmm-tracker captureMouseMoves]])) @@ -146,8 +146,7 @@ :attr {:on-mouse-enter on-mouse-enter :on-mouse-leave on-mouse-leave ;; TODO: Best way to move this fn to outer fn? (closes over show?) - :on-mouse-down (handler-fn (when show? (scrollbar-mouse-down event)))}} - {:flatten-attr false}) + :on-mouse-down (handler-fn (when show? (scrollbar-mouse-down event)))}}) [box/box :src src :width (if horizontal? @@ -163,8 +162,7 @@ ;; TODO: Best way to move this fn to outer fn? (closes over internal-scroll-pos) :attr {:on-mouse-down (handler-fn - (thumb-mouse-down event internal-scroll-pos))}} - {:flatten-attr false}) + (thumb-mouse-down event internal-scroll-pos))}}) [box/box :src (at) :width (if horizontal? @@ -182,9 +180,7 @@ "Render section 1 - the content component" [top-left-renderer column-header-height cmerger] (add-map-to-hiccup-call - (cmerger :top-left - {} - {:flatten-attr false}) + (cmerger :top-left) [box/box ;; content component :src (at) :height (px (or column-header-height 0)) @@ -208,7 +204,7 @@ " [row-header-renderer key-fn top-row-index rows scroll-y cmerger] (add-map-to-hiccup-call - (cmerger :row-header-content {:scroll-y scroll-y} {:flatten-attr false}) + (cmerger :row-header-content {:scroll-y scroll-y}) [box/v-box :src (at) :children (map @@ -231,15 +227,14 @@ :attr (when row-header-selection-fn {:on-mouse-down (handler-fn (on-mouse-down :row-header row-header-selection-fn content-rows-height 0 event)) ;; TODO: width set to 0 because we don't have it - could probably measure it :on-mouse-enter (handler-fn (on-mouse-enter :row-header)) - :on-mouse-leave (handler-fn (on-mouse-leave :row-header))})} - {:flatten-attr false}) + :on-mouse-leave (handler-fn (on-mouse-leave :row-header))})}) [box/v-box ;; viewport component :src (at) :size (if row-viewport-height "none" "auto") :height (when row-viewport-height (px row-viewport-height)) :children [(when selection-allowed? (let [{:keys [class style attr]} - (cmerger :row-header-selection-rect {} {:flatten-attr false})] + (cmerger :row-header-selection-rect)] [selection-renderer class style attr])) ;; selection rectangle component (if row-header-renderer [row-header-content row-header-renderer key-fn top-row-index rows scroll-y cmerger] ;; content component @@ -252,7 +247,7 @@ "Render section 3 - the content component" [bottom-left-renderer column-footer-height cmerger] (add-map-to-hiccup-call - (cmerger :bottom-left {} {:flatten-attr false}) + (cmerger :bottom-left) [box/box ;; content component :src (at) :height (px (or column-footer-height 0)) @@ -272,7 +267,7 @@ " [column-header-renderer scroll-x cmerger] (add-map-to-hiccup-call - (cmerger :column-header-content {:scroll-x scroll-x} {:flatten-attr false}) + (cmerger :column-header-content {:scroll-x scroll-x}) [box/box :src (at) :child [column-header-renderer]])) @@ -289,15 +284,14 @@ (when column-header-selection-fn {:on-mouse-down (handler-fn (on-mouse-down :column-header column-header-selection-fn column-header-height content-rows-width event)) :on-mouse-enter (handler-fn (on-mouse-enter :column-header)) - :on-mouse-leave (handler-fn (on-mouse-leave :column-header))})} - {:flatten-attr false}) + :on-mouse-leave (handler-fn (on-mouse-leave :column-header))})}) [box/v-box ;; viewport component :src (at) :width (when row-viewport-width (px row-viewport-width)) :height (px (or column-header-height 0)) :children [(when selection-allowed? (let [{:keys [class style attr]} - (cmerger :column-header-selection-rect {} {:flatten-attr false})] + (cmerger :column-header-selection-rect)] [selection-renderer class style attr])) ;; selection rectangle component (if column-header-renderer [column-header-content column-header-renderer scroll-x cmerger] ;; content component @@ -321,7 +315,7 @@ " [row-renderer key-fn top-row-index rows scroll-x scroll-y cmerger] (add-map-to-hiccup-call - (cmerger :row-content {:scroll-x scroll-x :scroll-y scroll-y} {:flatten-attr false}) + (cmerger :row-content {:scroll-x scroll-x :scroll-y scroll-y}) [box/v-box :src (at) :children (map @@ -345,8 +339,7 @@ {:on-mouse-down (handler-fn (on-mouse-down :row row-selection-fn content-rows-height content-rows-width event)) :on-mouse-enter (handler-fn (on-mouse-enter :row)) :on-mouse-leave (handler-fn (on-mouse-leave :row))}) - {:id row-viewport-id})};; Can't be overriding the internally generated id - {:flatten-attr false}) + {:id row-viewport-id})});; Can't be overriding the internally generated id [box/v-box ;; viewport component :src (at) :size (if row-viewport-height "none" "auto") @@ -354,7 +347,7 @@ :height (when row-viewport-height (px row-viewport-height)) :children [(when selection-allowed? (let [{:keys [class style attr]} - (cmerger :row-selection-rect {} {:flatten-attr false})] + (cmerger :row-selection-rect)] [selection-renderer class style attr])) ;; selection rectangle component [row-content row-renderer key-fn top-row-index rows scroll-x scroll-y cmerger]]])) ;; content component @@ -372,7 +365,7 @@ " [column-footer-renderer scroll-x cmerger] (add-map-to-hiccup-call - (cmerger :column-footer-content {:scroll-x scroll-x} {:flatten-attr false}) + (cmerger :column-footer-content {:scroll-x scroll-x}) [box/box :src (at) :child [column-footer-renderer]])) @@ -385,7 +378,7 @@ class style attr content-class content-style content-attr] (add-map-to-hiccup-call - (cmerger :column-footers {} {:flatten-attr false}) + (cmerger :column-footers) [box/box ;; viewport component :src (at) :width (when row-viewport-width (px row-viewport-width)) @@ -401,7 +394,7 @@ "Render section 7 - the content component" [top-right-renderer column-header-height cmerger] (add-map-to-hiccup-call - (cmerger :top-right {} {:flatten-attr false}) + (cmerger :top-right) [box/box ;; content component :src (at) :height (px (or column-header-height 0)) @@ -425,7 +418,7 @@ " [row-footer-renderer key-fn top-row-index rows scroll-y cmerger] (add-map-to-hiccup-call - (cmerger :row-footer-content {:scroll-y scroll-y} {:flatten-attr false}) + (cmerger :row-footer-content {:scroll-y scroll-y}) [box/v-box :src (at) :children (map @@ -441,7 +434,7 @@ row-viewport-height content-rows-height cmerger] (add-map-to-hiccup-call - (cmerger :row-footers {:max-height content-rows-height} {:flatten-attr false}) + (cmerger :row-footers {:max-height content-rows-height}) [box/box ;; viewport component :src (at) :size (if row-viewport-height "none" "auto") @@ -457,7 +450,7 @@ "Render section 9 - the content component" [bottom-right-renderer column-footer-height cmerger] (add-map-to-hiccup-call - (cmerger :bottom-right {} {:flatten-attr false}) + (cmerger :bottom-right) [box/box ;; content component :src (at) :height (px (or column-footer-height 0)) @@ -985,8 +978,8 @@ v-table-selection-css-desc {:class class :style style :attr attr})] [:div - (cmerger :main {:width width :height height - :top top :left left}) + (flatten-attr (cmerger :main {:width width :height height + :top top :left left})) ""])) coords-debug (reagent/atom nil) ;; Handy when debugging - used to show selection coords on the left-hand debug section @@ -1215,8 +1208,7 @@ ;:overscroll-behavior-block "none" - :attr {:on-wheel (handler-fn (on-wheel event))}} - {:flatten-attr false}) + :attr {:on-wheel (handler-fn (on-wheel event))}}) [box/h-box :src src :debug-as (or debug-as (reflect-current-component)) @@ -1225,7 +1217,7 @@ ;; ========== LEFT SECTION (1, 2, 3) - row header area (add-map-to-hiccup-call - (cmerger :left-section {} {:flatten-attr false}) + (cmerger :left-section) [box/v-box :src (at) :children [ @@ -1272,8 +1264,7 @@ ;; ========== MIDDLE SECTION (4, 5, 6) - column header/footer and content area (add-map-to-hiccup-call - (cmerger :middle-section {:max-width @content-rows-width} - {:flatten-attr false}) + (cmerger :middle-section {:max-width @content-rows-width}) [box/v-box :src (at) :size (if row-viewport-width "none" "auto") @@ -1330,7 +1321,7 @@ ;; ========== Horizontal scrollbar section (add-map-to-hiccup-call - (cmerger :h-scroll {:scrollbar-margin scrollbar-margin}) + (flatten-attr (cmerger :h-scroll {:scrollbar-margin scrollbar-margin})) [scrollbar :src (at) :type :horizontal @@ -1343,7 +1334,7 @@ ;; ========== Right section (7, 8, 9) - row footer area (add-map-to-hiccup-call - (cmerger :right-section {} {:flatten-attr false}) + (cmerger :right-section) [box/v-box :src (at) :children [ @@ -1386,7 +1377,7 @@ ;; ========== Vertical scrollbar section (add-map-to-hiccup-call - (cmerger :v-scroll-section {} {:flatten-attr false}) + (cmerger :v-scroll-section) [box/v-box :src (at) :children [[box/gap @@ -1396,8 +1387,9 @@ :src (at) :size "auto" :child (add-map-to-hiccup-call - (cmerger - :v-scroll {:scrollbar-margin scrollbar-margin}) + (flatten-attr + (cmerger + :v-scroll {:scrollbar-margin scrollbar-margin})) [scrollbar :src (at) :type :vertical From 53bd3af5c2a45dffa0c751600deb79a5221e9979 Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Mon, 31 Oct 2022 11:03:14 -0700 Subject: [PATCH 15/65] Css split out - Everything appears to be working --- src/re_com/popover.cljs | 500 ++++++++++++++++++++++------------------ 1 file changed, 272 insertions(+), 228 deletions(-) diff --git a/src/re_com/popover.cljs b/src/re_com/popover.cljs index 9fd3cb46..ace7cdac 100644 --- a/src/re_com/popover.cljs +++ b/src/re_com/popover.cljs @@ -5,7 +5,7 @@ (:require [re-com.config :refer [include-args-desc?]] [re-com.debug :refer [->attr]] - [re-com.util :refer [get-element-by-id px deref-or-value sum-scroll-offsets]] + [re-com.util :refer [get-element-by-id px deref-or-value sum-scroll-offsets merge-css add-map-to-hiccup-call flatten-attr]] [re-com.box :refer [box h-box v-box flex-child-style flex-flow-style align-style]] [re-com.close-button :refer [close-button]] [re-com.validate :refer [position? position-options-list popover-status-type? popover-status-types-list number-or-string? @@ -86,6 +86,38 @@ [(/ (+ (.-right bounding-rect) (.-left bounding-rect)) 2) ;; x (/ (+ (.-bottom bounding-rect) (.-top bounding-rect)) 2)])) ;; y +(def popover-arrow-css-desc + {:arrow {:class ["popover-arrow" "rc-popover-arrow"] + :style (fn [{:keys [orientation pop-offset arrow-length arrow-width]}] + {:position "absolute" + (case orientation ;; Connect arrow to edge of popover + :left :right + :right :left + :above :bottom + :below :top) (px arrow-length :negative) + + (case orientation ;; Position the arrow at the top/left, center or bottom/right of the popover + (:left :right) :top + (:above :below) :left) (if (nil? pop-offset) "50%" (px pop-offset)) + + (case orientation ;; Adjust the arrow position so it's center is attached to the desired position set above + (:left :right) :margin-top + (:above :below) :margin-left) (px (/ arrow-width 2) :negative) + + :width (px (case orientation ;; Arrow is rendered in a rectangle so choose the correct edge length + (:left :right) arrow-length + (:above :below) arrow-width)) + + :height (px (case orientation ;; Same as :width comment above + (:left :right) arrow-width + (:above :below) arrow-length))})} + :arrow-drawing {:style + (fn [{:keys [popover-color grey-arrow? popover-border-color no-border?]}] + {:fill (if popover-color + popover-color + (if grey-arrow? "#f7f7f7" "white")) + :stroke (or popover-border-color (when-not no-border? "rgba(0, 0, 0, .2)")) + :stroke-width "1"})}}) (defn- popover-arrow "Render the triangle which connects the popover to the anchor (using SVG)" @@ -94,40 +126,15 @@ arrow-shape {:left (str (point 0 0) (point arrow-length half-arrow-width) (point 0 arrow-width)) :right (str (point arrow-length 0) (point 0 half-arrow-width) (point arrow-length arrow-width)) :above (str (point 0 0) (point half-arrow-width arrow-length) (point arrow-width 0)) - :below (str (point 0 arrow-length) (point half-arrow-width 0) (point arrow-width arrow-length))}] + :below (str (point 0 arrow-length) (point half-arrow-width 0) (point arrow-width arrow-length))} + cmerger (merge-css popover-arrow-css-desc {:parts parts})] [:svg - (merge - {:class (str "popover-arrow rc-popover-arrow " (get-in parts [:arrow :class])) - :style (merge {:position "absolute" - (case orientation ;; Connect arrow to edge of popover - :left :right - :right :left - :above :bottom - :below :top) (px arrow-length :negative) - - (case orientation ;; Position the arrow at the top/left, center or bottom/right of the popover - (:left :right) :top - (:above :below) :left) (if (nil? pop-offset) "50%" (px pop-offset)) - - (case orientation ;; Adjust the arrow position so it's center is attached to the desired position set above - (:left :right) :margin-top - (:above :below) :margin-left) (px half-arrow-width :negative) - - :width (px (case orientation ;; Arrow is rendered in a rectangle so choose the correct edge length - (:left :right) arrow-length - (:above :below) arrow-width)) - - :height (px (case orientation ;; Same as :width comment above - (:left :right) arrow-width - (:above :below) arrow-length))} - (get-in parts [:arrow :style]))} - (get-in parts [:arrow :attr])) - [:polyline {:points (arrow-shape orientation) - :style {:fill (if popover-color - popover-color - (if grey-arrow? "#f7f7f7" "white")) - :stroke (or popover-border-color (when-not no-border? "rgba(0, 0, 0, .2)")) - :stroke-width "1"}}]])) + (cmerger :arrow {:orientation orientation :pop-offset pop-offset :arrow-length arrow-length :arrow-width arrow-width}) + [:polyline (merge {:points (arrow-shape orientation)} + (cmerger :arrow-drawing {:popover-color popover-color + :grey-arrow? grey-arrow? + :popover-border-color popover-border-color + :no-border? no-border?}))]])) ;;-------------------------------------------------------------------------------------------------- @@ -144,26 +151,30 @@ {:name :src :required false :type "map" :validate-fn map? :description [:span "Used in dev builds to assist with debugging. Source code coordinates map containing keys" [:code ":file"] "and" [:code ":line"] ". See 'Debugging'."]} {:name :debug-as :required false :type "map" :validate-fn map? :description [:span "Used in dev builds to assist with debugging, when one component is used implement another component, and we want the implementation component to masquerade as the original component in debug output, such as component stacks. A map optionally containing keys" [:code ":component"] "and" [:code ":args"] "."]}])) +(def backdrop-css-desc + {:main {:class ["noselect" "rc-backdrop"] + :style (fn [{:keys [opacity]}] + {:position "fixed" + :left "0px" + :top "0px" + :width "100%" + :height "100%" + :background-color "black" + :opacity (or opacity 0.0)})}}) + (defn- backdrop "Renders a backdrop div which fills the entire page and responds to clicks on it. Can also specify how tranparent it should be" [& {:keys [opacity on-click class style attr] :as args}] (or (validate-args-macro backdrop-args-desc args) - [:div - (merge - {:class (str "noselect rc-backdrop " class) - :style (merge - {:position "fixed" - :left "0px" - :top "0px" - :width "100%" - :height "100%" - :background-color "black" - :opacity (or opacity 0.0)} - style) - :on-click (handler-fn (on-click))} - (->attr args) - attr)])) + (let [cmerger (merge-css backdrop-css-desc args)] + [:div + (merge + (flatten-attr + (cmerger :main + {:attr {:on-click (handler-fn (on-click))}})) + (->attr args) + attr)]))) ;;-------------------------------------------------------------------------------------------------- @@ -176,6 +187,13 @@ {:name :container :level 1 :class "" :impl "[h-box]" :notes [:span "Container for the " [:code ":title"] " and the close button."]} {:name :close-button :level 2 :class "" :impl "[close-button]" :notes "The close button."}])) +(def popover-title-css-desc + {:main {:class ["popover-title" "rc-popover-title"] + :style (merge (flex-child-style "inherit") + {:font-size "18px"})} + :container {} + :close-button {}}) + (def popover-title-parts (when include-args-desc? (-> (map :name popover-title-parts-desc) set))) @@ -200,35 +218,32 @@ (or (validate-args-macro popover-title-args-desc args) #_(assert (or ((complement nil?) showing?) ((complement nil?) close-callback)) "Must specify either showing? OR close-callback") ;; IJ: TODO re-refactor - (let [close-button? (if (nil? close-button?) true close-button?)] + (let [close-button? (if (nil? close-button?) true close-button?) + cmerger (merge-css popover-title-css-desc args)] [:h3 (merge - {:class (str "popover-title rc-popover-title " class) - :style (merge (flex-child-style "inherit") - {:font-size "18px"} - style)} + (flatten-attr (cmerger :main)) (->attr args) attr) - [h-box - :src (at) - :class (get-in parts [:container :class] "") - :style (get-in parts [:container :style]) - :justify :between - :align :center - :children [title - (when close-button? - [close-button - :src (at) - :class (get-in parts [:close-button :class] "") - :style (get-in parts [:close-button :style]) - :attr (get-in parts [:close-button :attr]) - :on-click #(if close-callback - (close-callback) - (reset! showing? false)) - :div-size 0 - :font-size 26 - :top-offset -1 - :left-offset -5])]]]))) + (add-map-to-hiccup-call + (cmerger :container) + [h-box + :src (at) + :justify :between + :align :center + :children [title + (when close-button? + (add-map-to-hiccup-call + (cmerger :close-button) + [close-button + :src (at) + :on-click #(if close-callback + (close-callback) + (reset! showing? false)) + :div-size 0 + :font-size 26 + :top-offset -1 + :left-offset -5]))]])]))) ;;-------------------------------------------------------------------------------------------------- @@ -271,6 +286,40 @@ {:name :arrow :level 1 :class "rc-popover-arrow" :impl "[:svg]" :notes ""} {:name :content :level 2 :class "rc-popover-content" :impl "[:div]" :notes ""}])) +(def popover-border-css-desc + {:main {:class ["popover" "fade" "in" "rc-popover-border"] + :style (fn [{:keys [top left width height background-color border-color tooltip-style? + orientation margin-left margin-top ready-to-show?] :as params}] + (merge + (into {} + (for [k [:top :left :width :height :background-color :border-color + :margin-left :margin-top] + :let [v (get params k)] + :when v] + [k v])) + (when tooltip-style? + {:border-radius "4px" + :box-shadow "none" + :border "none"}) + + ;; The popover point is zero width, therefore its absolute children will consider this width when deciding their + ;; natural size and in particular, how they natually wrap text. The right hand side of the popover is used as a + ;; text wrapping point so it will wrap, depending on where the child is positioned. The margin is also taken into + ;; consideration for this point so below, we set the margins to negative a lot to prevent + ;; this annoying wrapping phenomenon. + (case orientation + :left {:margin-left "-2000px"} + (:right :above :below) {:margin-right "-2000px"}) + + ;; make it visible and turn off Bootstrap max-width and remove Bootstrap padding which adds an internal white border + {:display "block" + :opacity (if ready-to-show? "1" "0") + :max-width "none" + :padding "0px"}))} + :content {:class ["popover-content" "rc-popover-content"] + :style (fn [{:keys [padding]}] + {:padding padding})}}) + (def popover-border-parts (when include-args-desc? (-> (map :name popover-border-parts-desc) set))) @@ -345,51 +394,29 @@ :as args}] (or (validate-args-macro popover-border-args-desc args) - (let [[orientation grey-arrow?] (calc-metrics @position)] - [:div.popover.fade.in + (let [[orientation grey-arrow?] (calc-metrics @position) + cmerger (merge-css popover-border-css-desc args)] + [:div (merge - {:class (str "rc-popover-border " class) - :id pop-id - :style (merge (if @rendered-once - (when pop-id (calc-popover-pos orientation @p-width @p-height @pop-offset arrow-length arrow-gap)) - {:top "-10000px" :left "-10000px"}) - - (when width {:width width}) - (when height {:height height}) - (when popover-color {:background-color popover-color}) - (when popover-border-color {:border-color popover-border-color}) - (when tooltip-style? - {:border-radius "4px" - :box-shadow "none" - :border "none"}) - - ;; The popover point is zero width, therefore its absolute children will consider this width when deciding their - ;; natural size and in particular, how they natually wrap text. The right hand side of the popover is used as a - ;; text wrapping point so it will wrap, depending on where the child is positioned. The margin is also taken into - ;; consideration for this point so below, we set the margins to negative a lot to prevent - ;; this annoying wrapping phenomenon. - (case orientation - :left {:margin-left "-2000px"} - (:right :above :below) {:margin-right "-2000px"}) - ;; optional override offsets - (when margin-left {:margin-left margin-left}) - (when margin-top {:margin-top margin-top}) - - ;; make it visible and turn off Bootstrap max-width and remove Bootstrap padding which adds an internal white border - {:display "block" - :opacity (if @ready-to-show? "1" "0") - :max-width "none" - :padding "0px"} - style)} - (->attr args) - attr) + {:id pop-id} + (flatten-attr + (cmerger + :main + (merge + (if @rendered-once + (when pop-id (calc-popover-pos orientation @p-width @p-height @pop-offset arrow-length arrow-gap)) + {:top "-10000px" :left "-10000px"}) + {:width width :height height :background-color popover-color + :border-color popover-border-color :tooltip-style? tooltip-style? + :orientation orientation :margin-left margin-left :margin-top margin-top + :ready-to-show? @ready-to-show?}))) + (->attr args) + attr) [popover-arrow orientation @pop-offset arrow-length arrow-width grey-arrow? tooltip-style? popover-color popover-border-color parts] (when title title) (into [:div - (merge - {:class (str "popover-content rc-popover-content " (get-in parts [:content :class])) - :style (merge {:padding padding} (get-in parts [:content :style]))} - (get-in parts [:content :attr]))] + (flatten-attr + (cmerger :content {:padding padding}))] children)])))})))) @@ -404,6 +431,18 @@ {:name :border :level 2 :class "" :impl "[popover-border]" :notes ""} {:name :title :level 3 :class "" :impl "[popover-title]" :notes ""}])) +(def popover-content-wrapper-css-desc + {:main {:class ["popover-content-wrapper" "rc-popover-content-wrapper"] + :style (fn [{:keys [no-clip? left-offset top-offset]}] + (merge (flex-child-style "inherit") + (when no-clip? + {:position "fixed" + :left (px left-offset) + :top (px top-offset)})))} + :backdrop {} + :border {} + :title {}}) + (def popover-content-wrapper-parts (when include-args-desc? (-> (map :name popover-content-wrapper-parts-desc) set))) @@ -470,51 +509,46 @@ :as args}] (or (validate-args-macro popover-content-wrapper-args-desc args) - (do + (let [cmerger (merge-css popover-content-wrapper-css-desc args)] @position-injected ;; Dereference this atom. Although nothing here needs its value explicitly, the calculation of left-offset and top-offset are affected by it for :no-clip? true [:div - (merge {:class (str "popover-content-wrapper rc-popover-content-wrapper " class) - :style (merge (flex-child-style "inherit") - (when no-clip? {:position "fixed" - :left (px @left-offset) - :top (px @top-offset)}) - style)} + (merge (flatten-attr + (cmerger :main {:no-clip? no-clip? + :left-offset @left-offset + :top-offset @top-offset})) (->attr args) attr) (when (and (deref-or-value showing-injected?) on-cancel) - [backdrop - :src (at) - :class (get-in parts [:backdrop :class] "") - :style (get-in parts [:backdrop :style]) - :attr (get-in parts [:backdrop :attr]) - :opacity backdrop-opacity - :on-click on-cancel]) - [popover-border - :src (at) - :class (get-in parts [:border :class] "") - :style (get-in parts [:border :style]) - :attr (get-in parts [:border :attr]) - :position position-injected - :position-offset position-offset - :width width - :height height - :tooltip-style? tooltip-style? - :popover-color popover-color - :popover-border-color popover-border-color - :arrow-length arrow-length - :arrow-width arrow-width - :arrow-gap arrow-gap - :padding padding - :title (when title [popover-title - :src (at) - :class (get-in parts [:title :class] "") - :style (get-in parts [:title :style]) - :attr (get-in parts [:title :attr]) - :title title - :showing? showing-injected? - :close-button? close-button? - :close-callback on-cancel]) - :children [body]]])))})))) + (add-map-to-hiccup-call + (cmerger :backdrop) + [backdrop + :src (at) + :opacity backdrop-opacity + :on-click on-cancel])) + (add-map-to-hiccup-call + (cmerger :border) + [popover-border + :src (at) + :position position-injected + :position-offset position-offset + :width width + :height height + :tooltip-style? tooltip-style? + :popover-color popover-color + :popover-border-color popover-border-color + :arrow-length arrow-length + :arrow-width arrow-width + :arrow-gap arrow-gap + :padding padding + :title (when title (add-map-to-hiccup-call + (cmerger :title) + [popover-title + :src (at) + :title title + :showing? showing-injected? + :close-button? close-button? + :close-callback on-cancel])) + :children [body]])])))})))) ;;-------------------------------------------------------------------------------------------------- @@ -527,6 +561,20 @@ {:name :point-wrapper :level 1 :class "rc-point-wrapper" :impl "[:div]" :notes "Wraps the anchor component and the popover-point (which the actual popover points to)."} {:name :point :level 2 :class "rc-popover-point" :impl "[:div]" :notes [:span "The point (width/height 0) which is placed at the center of the relevant side of the anchor, based on " [:code ":position"] "tag"]}])) +(def popover-anchor-wrapper-css-desc + {:main {:class ["rc-popover-anchor-wrapper" "display-inline-flex"] + :style (flex-child-style "inherit")} + :point-wrapper {:class ["display-inline-flex" "rc-point-wrapper"] + :style (fn [{:keys [flex-flow]}] + (merge (flex-child-style "auto") + (flex-flow-style flex-flow) + (align-style :align-items :center)))} + :point {:class ["rc-popover-point" "display-inline-flex"] + :style (merge + (flex-child-style "auto") + {:position "relative" + :z-index 4})}}) + (def popover-anchor-wrapper-parts (when include-args-desc? (-> (map :name popover-anchor-wrapper-parts-desc) set))) @@ -567,25 +615,19 @@ (reset! internal-position @external-position)) (let [[orientation _arrow-pos] (split-keyword @internal-position "-") ;; only need orientation here place-anchor-before? (case orientation (:left :above) false true) - flex-flow (case orientation (:left :right) "row" "column")] + flex-flow (case orientation (:left :right) "row" "column") + cmerger (merge-css popover-anchor-wrapper-css-desc args)] [:div - (merge {:class (str "rc-popover-anchor-wrapper display-inline-flex " class) - :style (merge (flex-child-style "inherit") - style)} + (merge (flatten-attr (cmerger :main)) (->attr args) attr) [:div ;; Wrapper around the anchor and the "point" - {:class (str "display-inline-flex rc-point-wrapper " (get-in parts [:point-wrapper :class])) - :style (merge (flex-child-style "auto") - (flex-flow-style flex-flow) - (align-style :align-items :center))} + (flatten-attr + (cmerger :point-wrapper {:flex-flow flex-flow})) (when place-anchor-before? anchor) (when (deref-or-value showing?) [:div ;; The "point" that connects the anchor to the popover - {:class (str "display-inline-flex rc-popover-point " (get-in parts [:point :class])) - :style (merge (flex-child-style "auto") - {:position "relative" - :z-index 4})} + (flatten-attr (cmerger :point)) (into popover [:showing-injected? showing? :position-injected internal-position])]) ;; NOTE: Inject showing? and position to the popover (when-not place-anchor-before? anchor)]]))))})))) @@ -602,6 +644,21 @@ {:name :close-button-container :level 3 :class "rc-popover-tooltip-close-button-container" :impl "[box]" :notes ""} {:name :close-button :level 4 :class "rc-popover-tooltip-close-button" :impl "[close-button]" :notes ""}])) +(def popover-tooltip-css-desc + {:main {:class ["rc-popover-tooltip"]} + :content-wrapper {} + :v-box {:style (fn [{:keys [status]}] + (if (= status :info) + {:color "white" + :font-size "14px" + :padding "4px"} + {:color "white" + :font-size "12px" + :font-weight "bold" + :text-align "center"}))} + :close-button-container {:class ["rc-popover-tooltip-close-button-container"]} + :close-button {:class ["rc-popover-tooltip-close-button"]}}) + (def popover-tooltip-parts (when include-args-desc? (-> (map :name popover-tooltip-parts-desc) set))) @@ -633,64 +690,51 @@ (or (validate-args-macro popover-tooltip-args-desc args) (let [label (deref-or-value label) - popover-color (case status - :warning "#f57c00" - :error "#d50000" - :info "#333333" - :success "#13C200" - "black")] - [popover-anchor-wrapper - :src src - :debug-as (or debug-as (reflect-current-component)) - :showing? showing? - :position (or position :below-center) - :anchor anchor - :class (str "rc-popover-tooltip " class) - :style style - :attr attr - :popover [popover-content-wrapper - :src (at) - :class (get-in parts [:content-wrapper :class] "") - :style (get-in parts [:content-wrapper :style]) - :attr (get-in parts [:content-wrapper :attr]) - :no-clip? no-clip? - :on-cancel on-cancel - :width width - :tooltip-style? true - :popover-color popover-color - :padding "3px 8px" - :arrow-length 6 - :arrow-width 12 - :arrow-gap 4 - :body [v-box - :src (at) - :class (get-in parts [:v-box :class]) - :style (merge - (if (= status :info) - {:color "white" - :font-size "14px" - :padding "4px"} - {:color "white" - :font-size "12px" - :font-weight "bold" - :text-align "center"}) - (get-in parts [:v-box :style])) - :children [(when close-button? - [box - :src (at) - :class (str "rc-popover-tooltip-close-button-container " (get-in parts [:close-button-container :class])) - :style (get-in parts [:close-button-container :style]) - :attr (get-in parts [:close-button-container :attr]) - :align-self :end - :child [close-button - :src (at) - :class (str "rc-popover-tooltip-close-button " (get-in parts [:close-button :class])) - :style (get-in parts [:close-button :style]) - :attr (get-in parts [:close-button :attr]) - :on-click #(if on-cancel - (on-cancel) - (reset! showing? false)) - :div-size 15 - :font-size 20 - :left-offset 5]]) - label]]]]))) + cmerger (merge-css popover-tooltip-css-desc args)] + (add-map-to-hiccup-call + (cmerger :main) + [popover-anchor-wrapper + :src src + :debug-as (or debug-as (reflect-current-component)) + :showing? showing? + :position (or position :below-center) + :anchor anchor + :popover (add-map-to-hiccup-call + (cmerger :content-wrapper) + [popover-content-wrapper + :src (at) + :no-clip? no-clip? + :on-cancel on-cancel + :width width + :popover-color (case status + :warning "#f57c00" + :error "#d50000" + :info "#333333" + :success "#13C200" + "black") + :padding "3px 8px" + :arrow-length 6 + :arrow-width 12 + :arrow-gap 4 + :tooltip-style? true + :body (add-map-to-hiccup-call + (cmerger :v-box {:status status}) + [v-box + :src (at) + :children [(when close-button? + (add-map-to-hiccup-call + (cmerger :close-button-container) + [box + :src (at) + :align-self :end + :child (add-map-to-hiccup-call + (cmerger :close-button) + [close-button + :src (at) + :on-click #(if on-cancel + (on-cancel) + (reset! showing? false)) + :div-size 15 + :font-size 20 + :left-offset 5])])) + label]])])])))) From 0d54f6f3cee7190d77dbde2a304312c2709f0050 Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Wed, 2 Nov 2022 13:51:08 -0700 Subject: [PATCH 16/65] Css split out - some work remaining on the buttons --- src/re_com/multi_select.cljs | 697 ++++++++++++++++++----------------- 1 file changed, 367 insertions(+), 330 deletions(-) diff --git a/src/re_com/multi_select.cljs b/src/re_com/multi_select.cljs index 9d7e491f..f612fc66 100644 --- a/src/re_com/multi_select.cljs +++ b/src/re_com/multi_select.cljs @@ -13,10 +13,12 @@ [re-com.text :as text] [re-com.buttons :as buttons] [re-com.close-button :as close-button] - [re-com.util :as rc.util :refer [deref-or-value]] + [re-com.util :as rc.util :refer [deref-or-value add-map-to-hiccup-call flatten-attr merge-css]] [re-com.validate :as validate :refer [string-or-hiccup? parts?]] [reagent.core :as reagent])) +(declare multi-select-css-desc) + (defn items-with-group-headings "Split a list of maps by a group key then return both the group" [items group-fn id-fn] @@ -65,87 +67,82 @@ (defn filter-text-box "Base function (before lifecycle metadata) to render a filter text box" [*filter-text placeholder *warning-message disabled? parts] - [box/h-box - :class (str "rc-multi-select-filter-text-box " (get-in parts [:filter-text-box :class])) - :attr (get-in parts [:filter-text-box :attr]) - :width "100%" - :align :center - :style (merge {:position "relative"} - (get-in parts [:filter-text-box :style])) - :children [[input-text - :class (str "rc-multi-select-filter-input-text " (get-in parts [:filter-input-text :class])) - :model *filter-text - :change-on-blur? false - :placeholder placeholder - ;:disabled? disabled? ;; Left here just in case we DO want to prevent searches while disabled - :width "100%" - :height "28px" - :style (merge {:padding "3px 4px"} - (get-in parts [:filter-input-text :style])) - :attr (get-in parts [:filter-input-text :attr]) - :on-change #(do (reset! *filter-text %) - (reset! *warning-message nil))] - [close-button/close-button - :class (get-in parts [:filter-reset-button :class]) - :style (get-in parts [:filter-reset-button :style]) - :attr (get-in parts [:filter-reset-button :attr]) - :on-click #(reset! *filter-text "") - :div-size 0 - :font-size 20 - :left-offset -13]]]) + (let [cmerger (merge-css multi-select-css-desc {:parts parts})] + (add-map-to-hiccup-call + (cmerger :filter-text-box) + [box/h-box + :width "100%" + :align :center + :children [(add-map-to-hiccup-call + (cmerger :filter-input-text) + [input-text + :model *filter-text + :change-on-blur? false + :placeholder placeholder + ;:disabled? disabled? ;; Left here just in case we DO want to prevent searches while disabled + :width "100%" + :height "28px" + :on-change #(do (reset! *filter-text %) + (reset! *warning-message nil))]) + (add-map-to-hiccup-call + (cmerger :filter-reset-button) + [close-button/close-button + :on-click #(reset! *filter-text "") + :div-size 0 + :font-size 20 + :left-offset -13])]]))) (defn group-heading-item "Render a group heading and set up appropriate mouse events" [] - (let [*mouse-over? (reagent/atom false)] + (let [*mouse-over? (reagent/atom false) + cmerger (merge-css multi-select-css-desc {})] (fn group-heading-render [& {:keys [heading disabled? click-callback double-click-callback selected-item-id]}] (let [id (:id heading) - selected? (= selected-item-id id) - class (if selected? - "highlighted" - (when @*mouse-over? "mouseover"))] - [:li.group-result - {:class class - :style (merge {:padding-left "6px" - :cursor (when-not disabled? "pointer") - :color (if selected? "white" "#444")} - (when disabled? - {:pointer-events "none"})) - :on-mouse-over (handler-fn (reset! *mouse-over? true)) - :on-mouse-out (handler-fn (reset! *mouse-over? false)) - :on-click (when-not disabled? (handler-fn (click-callback id true))) ;; true = group-heading item selected - :on-double-click (when-not disabled? (handler-fn (double-click-callback id)))} + selected? (= selected-item-id id)] + [:li + (flatten-attr + (cmerger + :group-heading-item + {:selected? selected? + :disabled? disabled? + :mouse-over? @*mouse-over? + :attr {:on-mouse-over (handler-fn (reset! *mouse-over? true)) + :on-mouse-out (handler-fn (reset! *mouse-over? false)) + :on-click (when-not disabled? (handler-fn (click-callback id true))) ;; true = group-heading item selected + :on-double-click (when-not disabled? (handler-fn (double-click-callback id)))}})) (:group heading)])))) (defn list-item "Render a list item and set up appropriate mouse events" [] - (let [*mouse-over? (reagent/atom false)] + (let [*mouse-over? (reagent/atom false) + cmerger (merge-css multi-select-css-desc {})] (fn list-item-render [& {:keys [item id-fn label-fn disabled? click-callback double-click-callback selected-item-id group-selected?]}] (let [id (id-fn item) - selected? (= id selected-item-id) - class (if (and selected? (not disabled?)) - "highlighted" - (when @*mouse-over? "mouseover"))] + selected? (= id selected-item-id)] [:li - {:class (str "active-result group-option " class) - :style (merge (when group-selected? {:background-color "hsl(208, 56%, 92%)"}) - (when disabled? {:cursor "default" - :pointer-events "none"})) - :on-mouse-over (handler-fn (reset! *mouse-over? true)) - :on-mouse-out (handler-fn (reset! *mouse-over? false)) - :on-click (when-not disabled? (handler-fn (click-callback id false))) ;; false = group-heading item NOT selected - :on-double-click (when-not disabled? (handler-fn (double-click-callback id)))} + (flatten-attr + (cmerger + :list-item + {:group-selected? group-selected? + :selected? selected? + :disabled? disabled? + :mouse-over? @*mouse-over? + :attr {:on-mouse-over (handler-fn (reset! *mouse-over? true)) + :on-mouse-out (handler-fn (reset! *mouse-over? false)) + :on-click (when-not disabled? (handler-fn (click-callback id false))) ;; false = group-heading item NOT selected + :on-double-click (when-not disabled? (handler-fn (double-click-callback id)))}})) (label-fn item)])))) (defn list-box "Render a list box which can be a single list or a grouped list" - [& {:keys [items id-fn label-fn group-fn disabled? *current-item-id group-heading-selected? click-callback double-click-callback filter-choices-text src]}] + [& {:keys [items id-fn label-fn group-fn disabled? *current-item-id group-heading-selected? click-callback double-click-callback filter-choices-text src class style attr parts] :as args}] (let [[group-names group-item-lists] (items-with-group-headings items group-fn id-fn) has-group-names? (not (and (nil? (:group (first group-names))) (= 1 (count group-item-lists)))) ;; if 0 or 1 group names, no headings to display make-list-item (fn [item] @@ -169,23 +166,24 @@ :double-click-callback double-click-callback :selected-item-id @*current-item-id]) make-heading-then-items (fn [heading items] - (cons (make-group-heading-item heading) (make-items items)))] - [box/box - :src src - :size "1" - :class (if disabled? "bm-multi-select-list-disabled" "bm-multi-select-list") - :style {:background-color "#fafafa" - :border "1px solid #ccc" - :border-radius "4px"} - :child [:ul.chosen-results - {:style {:max-height "none"}} ;; Override the 240px in the class - (if (-> items count pos?) - (if has-group-names? - (apply concat (doall (map make-heading-then-items group-names group-item-lists))) - (make-items (first group-item-lists))) - (if (string/blank? filter-choices-text) - "" - [:li.no-results (str "No results match \"" filter-choices-text "\"")]))]])) + (cons (make-group-heading-item heading) (make-items items))) + cmerger (merge-css multi-select-css-desc args)] + (add-map-to-hiccup-call + (cmerger :list-box {:disabled? disabled?}) + [box/box + :src src + :size "1" + :child [:ul + (flatten-attr (cmerger :list-box-results)) + (if (-> items count pos?) + (if has-group-names? + (apply concat (doall (map make-heading-then-items group-names group-item-lists))) + (make-items (first group-item-lists))) + (if (string/blank? filter-choices-text) + "" + [:li + (flatten-attr (cmerger :list-box-no-results)) + (str "No results match \"" filter-choices-text "\"")]))]]))) ;;-------------------------------------------------------------------------------------------------- @@ -230,6 +228,92 @@ {:name :filter-reset-button :level 4 :class "rc-multi-select-filter-reset-button" :impl "[close-button]"} {:name :right-filter-result-count :level 3 :class "rc-multi-select-right-filter-result-count" :impl "[label]"}])) +(def multi-select-css-desc + {:main {:class ["rc-multi-select" "noselect" "chosen-container" "chosen-container-single"] + :style (fn [{:keys [width]}] + (merge (box/flex-child-style (if width "0 0 auto" "auto")) + (box/align-style :align-self :start) + {:overflow "hidden" + :width width}))} + :container {:class ["rc-multi-select-container"]} + :left {:class ["rc-multi-select-left"]} + :left-label-container {:class ["rc-multi-select-left-label-container"]} + :left-label {:class ["rc-multi-select-left-label"] + :style {:font-size "small" + :font-weight "bold"}} + :left-label-item-count {:class ["rc-multi-select-left-label-item-count"] + :style {:font-size "smaller"}} + :left-list-box {:class ["rc-multi-select-left-list-box"]} + :filter-text-box {:class ["rc-multi-select-filter-text-box"] + :style {:position "relative"}} + :filter-input-text {:class ["rc-multi-select-filter-input-text"] + :style {:padding "3px 4px"}} + :filter-reset-button {:class ["rc-multi-select-filter-reset-button"]} + :left-filter-result-count {:class ["rc-multi-select-left-filter-result-count"] + :style {:font-size "smaller"}} + :middle-container {:class ["rc-multi-select-middle-container"]} + :middle-spacer {:class ["rc-multi-select-middle-spacer"]} + ;;TODO: cleanup: middle-[top|bottom]-spacer are not used. Should they go away, or should it be middle-spacer that goes? + :middle-top-spacer {:class ["rc-multi-select-middle-top-spacer"]} + :middle {:class ["rc-multi-select-middle"]} + :include-all-button {:class ["rc-multi-select-include-all-button"]} + :include-selected-button {:class ["rc-multi-select-include-selected-button"]} + :exclude-selected-button {:class ["rc-multi-select-exclude-selected-button"]} + :exclude-all-button {:class ["rc-multi-select-exclude-all-button"]} + :middle-bottom-spacer {:class ["rc-multi-select-middle-bottom-spacer"]} + :right {:class ["rc-multi-select-right"] + :style {:position "relative"}} + :warning-message {:class ["rc-multi-select-warning-message"] + :style (fn [{:keys [warning-message]}] + (when warning-message + {:color "white" + :background-color "green" + :border-radius "0px" + :opacity "0" + :position "absolute" + :right "0px" + :z-index 1 + :height "25px" + :padding "3px 6px" + :animation-name "rc-multi-select-fade-warning-msg" + :animation-duration "5000ms"}))} + :right-label-container {:class ["rc-multi-select-right-label-container"]} + :right-label {:class ["rc-multi-select-right-label"] + :style {:font-size "small" + :font-weight "bold"}} + :right-label-item-count {:class ["rc-multi-select-right-label-item-count"] + :style {:font-size "smaller"}} + :right-list-box {:class ["rc-multi-select-right-list-box"]} + :right-filter-result-count {:class ["rc-multi-select-right-filter-result-count"] + :style {:font-size "smaller"}} + :group-heading-item {:class (fn [{:keys [selected? mouse-over?]}] + ["group-result" (if selected? + "highlighted" + (when mouse-over? "mouseover"))]) + :style (fn [{:keys [disabled? selected?]}] + (merge {:padding-left "6px" + :cursor (when-not disabled? "pointer") + :color (if selected? "white" "#444")} + (when disabled? + {:pointer-events "none"})))} + :list-item {:class (fn [{:keys [selected? mouse-over? disabled?]}] + ["active-result" "group-option" (if (and selected? (not disabled?)) + "highlighted" + (when mouse-over? "mouseover"))]) + :style (fn [{:keys [group-selected? disabled?]}] + (merge (when group-selected? {:background-color "hsl(208, 56%, 92%)"}) + (when disabled? {:cursor "default" + :pointer-events "none"})))} + ;;TODO: These class names look foreign, need review + :list-box {:class (fn [{:keys [disabled?]}] + [(if disabled? "bm-multi-select-list-disabled" "bm-multi-select-list")]) + :style {:background-color "#fafafa" + :border "1px solid #ccc" + :border-radius "4px"}} + :list-box-results {:class ["chosen-results"] + :style {:max-height "none"}} ;; Override the 240px in the class + :list-box-no-results {:class ["no-results"]}}) + (def multi-select-parts (when include-args-desc? (-> (map :name multi-select-parts-desc) set))) @@ -412,261 +496,214 @@ (when (and changeable? (not= @*internal-model @*latest-ext-model)) (reset! *external-model @*internal-model) (on-change @*internal-model)) - (reset! *current-selection-id nil))] + (reset! *current-selection-id nil)) + cmerger (merge-css multi-select-css-desc args)] [:div (merge - {:class (str "rc-multi-select noselect chosen-container chosen-container-single " class) - :style (merge (box/flex-child-style (if width "0 0 auto" "auto")) - (box/align-style :align-self :start) - {:overflow "hidden" - :width width} - style)} - (->attr args) - attr) ;; Prevent user text selection - [box/h-box - :src (at) - :class (str "rc-multi-select-container " (get-in parts [:container :class])) - :style (get-in parts [:container :class]) - :attr (get-in parts [:container :attr]) - :height height - :max-height max-height - :gap "4px" - :children [[box/v-box - :src (at) - :class (str "rc-multi-select-left " (get-in parts [:left :class])) - :style (get-in parts [:left :style]) - :attr (get-in parts [:left :attr]) - :size "50%" - :gap "4px" - :children [(when left-label - (if (string? left-label) - [box/h-box - :src (at) - :class (str "rc-multi-select-left-label-container " (get-in parts [:left-label-container :class])) - :style (get-in parts [:left-label-container :style]) - :attr (get-in parts [:left-label-container :attr]) - :justify :between - :children [[:span - (merge - {:class (str "rc-multi-select-left-label " (get-in parts [:left-label :class])) - :style (merge {:font-size "small" - :font-weight "bold"} - (get-in parts [:left-label :style]))} - (get-in parts [:left-label :attr])) - left-label] - [:span - (merge - {:class (str "rc-multi-select-left-label-item-count " (get-in parts [:left-label-item-count :class])) - :style (merge {:font-size "smaller"} - (get-in parts [:left-label-item-count :style]))} - (get-in parts [:left-label-item-count :attr])) - (if (string/blank? @*filter-choices-text) - (rc.util/pluralize potential-count "item") - (str "showing " (count filtered-choices) " of " potential-count))]]] - left-label)) - [list-box - :src (at) - :class (str "rc-multi-select-left-list-box " (get-in parts [:left-list-box :class])) - :items filtered-choices - :id-fn id-fn - :label-fn label-fn - :group-fn group-fn - :disabled? disabled? - :*current-item-id *current-choice-id - :group-heading-selected? @*choice-group-heading-selected? - :click-callback choice-click - :double-click-callback include-click - :filter-choices-text @*filter-choices-text] - (when filter-box? - [:<> - [box/gap - :src (at) - :size "4px"] - [filter-text-box *filter-choices-text placeholder *warning-message disabled? parts] - [box/gap - :src (at) - :size "4px"] - (if (string/blank? @*filter-choices-text) - [text/label - :src (at) - :label (gstring/unescapeEntities " ") - :style {:font-size "smaller"}] - [text/label - :src (at) - :class (str "rc-multi-select-left-filter-result-count " (get-in parts [:left-filter-result-count :class])) - :style (merge {:font-size "smaller"} - (get-in parts [:left-filter-result-count :style])) - :attr (get-in parts [:left-filter-result-count :attr]) - :label [:span "Found " (rc.util/pluralize (count filtered-choices) "match" "matches") " containing " [:strong @*filter-choices-text]]])])]] + (flatten-attr (cmerger :main {:width width})) + (->attr args) + attr) ;; Prevent user text selection + (add-map-to-hiccup-call + (cmerger :container) + [box/h-box + :src (at) + :height height + :max-height max-height + :gap "4px" + :children [(add-map-to-hiccup-call + (cmerger :left) + [box/v-box + :src (at) + :size "50%" + :gap "4px" + :children [(when left-label + (if (string? left-label) + (add-map-to-hiccup-call + (cmerger :left-label-container) + [box/h-box + :src (at) + :justify :between + :children [[:span + (flatten-attr (cmerger :left-label)) + left-label] + [:span + (flatten-attr + (cmerger :left-label-item-count)) + (if (string/blank? @*filter-choices-text) + (rc.util/pluralize potential-count "item") + (str "showing " (count filtered-choices) " of " potential-count))]]]) + left-label)) + (add-map-to-hiccup-call + (cmerger :left-list-box) + [list-box + :src (at) + :items filtered-choices + :id-fn id-fn + :label-fn label-fn + :group-fn group-fn + :disabled? disabled? + :*current-item-id *current-choice-id + :group-heading-selected? @*choice-group-heading-selected? + :click-callback choice-click + :double-click-callback include-click + :filter-choices-text @*filter-choices-text]) + (when filter-box? + [:<> + [box/gap + :src (at) + :size "4px"] + [filter-text-box *filter-choices-text placeholder *warning-message disabled? parts] + [box/gap + :src (at) + :size "4px"] + (if (string/blank? @*filter-choices-text) + [text/label + :src (at) + :label (gstring/unescapeEntities " ") + :style {:font-size "smaller"}] + (add-map-to-hiccup-call + (cmerger :left-filter-result-count) + [text/label + :src (at) + :label [:span "Found " (rc.util/pluralize (count filtered-choices) "match" "matches") " containing " [:strong @*filter-choices-text]]]))])]]) - [box/v-box - :src (at) - :class (str "rc-multi-select-middle-container " (get-in parts [:middle-container :class])) - :style (get-in parts [:middle-container :style]) - :attr (get-in parts [:middle-container :attr]) - :justify :between - :children [[box/box - :src (at) - :class (str "rc-multi-select-middle-spacer " (get-in parts [:middle-spacer :class])) - :style (get-in parts [:middle-spacer :style]) - :attr (get-in parts [:middle-spacer :attr]) - :size "0 1 22px" ;; 22 = (+ 18 4) - height of the top components - :child ""] - [box/v-box - :src (at) - :class (str "rc-multi-select-middle " (get-in parts [:middle :class])) - :style (get-in parts [:middle :style]) - :attr (get-in parts [:middle :attr]) - :justify :center - :children [[buttons/button - :src (at) - :class (str "rc-multi-select-include-all-button " (get-in parts [:include-all-button :class])) - :label [:span - [:i {:class (str "zmdi zmdi-hc-fw-rc zmdi-fast-forward")}] - [:span - {:style {:position "relative" :top "-1px"}} - (str " include " (if (string/blank? @*filter-choices-text) potential-count (count filtered-choices)))]] - :disabled? (or disabled? (zero? (count filtered-choices))) - :style (merge button-style - (get-in parts [:include-all-button :style])) - :attr (get-in parts [:include-all-button :attr]) - :on-click include-filtered-click] - [buttons/button - :src (at) - :class (str "rc-multi-select-include-selected-button " (get-in parts [:include-selected-button :class])) - :label [:span - [:i {:class (str "zmdi zmdi-hc-fw-rc zmdi-play")}] - [:span - {:style {:position "relative" :top "-1px"}} - (str " include " (when @*choice-group-heading-selected? - (->> filtered-choices ;; TODO: Inefficient - (filter (fn [item] (= (first @*current-choice-id) (group-fn item)))) - count)))]] - :disabled? (or disabled? (not @*current-choice-id)) - :style (merge button-style - (get-in parts [:include-selected-button :style])) - :attr (get-in parts [:include-selected-button :attr]) - :on-click include-click] - [buttons/button - :src (at) - :class (str "rc-multi-select-exclude-selected-button " (get-in parts [:exclude-selected-button :class])) - :label [:span - [:i {:class (str "zmdi zmdi-hc-fw-rc zmdi-play zmdi-hc-rotate-180")}] - [:span - {:style {:position "relative" :top "-1px"}} - (str " exclude " (when @*selection-group-heading-selected? - (->> filtered-selections ;; TODO: Inefficient - (filter (fn [item] (= (first @*current-selection-id) (group-fn item)))) - count)))]] - :disabled? (or disabled? (not excludable?)) - :style (merge button-style - (get-in parts [:exclude-selected-button :style])) - :attr (get-in parts [:exclude-selected-button :attr]) - :on-click exclude-click] - [buttons/button - :src (at) - :class (str "rc-multi-select-exclude-all-button " (get-in parts [:exclude-all-button :class])) - :label [:span - [:i {:class (str "zmdi zmdi-hc-fw-rc zmdi-fast-rewind")}] - [:span - {:style {:position "relative" :top "-1px"}} - (str " exclude " (if (string/blank? @*filter-selections-text) chosen-count (count filtered-selections)))]] - :disabled? (or disabled? (zero? (count filtered-selections)) (not (> (count @*internal-model) (if required? 1 0)))) - :style (merge button-style - (get-in parts [:exclude-all-button :style])) - :attr (get-in parts [:exclude-all-button :attr]) - :on-click exclude-filtered-click]]] - [box/box - :src (at) - :size (str "0 2 " (if filter-box? "55px" "0px")) ;; 55 = (+ 4 4 28 4 15) - height of the bottom components + (add-map-to-hiccup-call + (cmerger :middle-container) + [box/v-box + :src (at) + :justify :between + :children [(add-map-to-hiccup-call + (cmerger :middle-spacer) + [box/box + :src (at) + :size "0 1 22px" ;; 22 = (+ 18 4) - height of the top components + :child ""]) + (add-map-to-hiccup-call + (cmerger :middle) + [box/v-box + :src (at) + :justify :center + :children [[buttons/button + :src (at) + :class (str "rc-multi-select-include-all-button " (get-in parts [:include-all-button :class])) + :label [:span + [:i {:class (str "zmdi zmdi-hc-fw-rc zmdi-fast-forward")}] + [:span + {:style {:position "relative" :top "-1px"}} + (str " include " (if (string/blank? @*filter-choices-text) potential-count (count filtered-choices)))]] + :disabled? (or disabled? (zero? (count filtered-choices))) + :style (merge button-style + (get-in parts [:include-all-button :style])) + :attr (get-in parts [:include-all-button :attr]) + :on-click include-filtered-click] + [buttons/button + :src (at) + :class (str "rc-multi-select-include-selected-button " (get-in parts [:include-selected-button :class])) + :label [:span + [:i {:class (str "zmdi zmdi-hc-fw-rc zmdi-play")}] + [:span + {:style {:position "relative" :top "-1px"}} + (str " include " (when @*choice-group-heading-selected? + (->> filtered-choices ;; TODO: Inefficient + (filter (fn [item] (= (first @*current-choice-id) (group-fn item)))) + count)))]] + :disabled? (or disabled? (not @*current-choice-id)) + :style (merge button-style + (get-in parts [:include-selected-button :style])) + :attr (get-in parts [:include-selected-button :attr]) + :on-click include-click] + [buttons/button + :src (at) + :class (str "rc-multi-select-exclude-selected-button " (get-in parts [:exclude-selected-button :class])) + :label [:span + [:i {:class (str "zmdi zmdi-hc-fw-rc zmdi-play zmdi-hc-rotate-180")}] + [:span + {:style {:position "relative" :top "-1px"}} + (str " exclude " (when @*selection-group-heading-selected? + (->> filtered-selections ;; TODO: Inefficient + (filter (fn [item] (= (first @*current-selection-id) (group-fn item)))) + count)))]] + :disabled? (or disabled? (not excludable?)) + :style (merge button-style + (get-in parts [:exclude-selected-button :style])) + :attr (get-in parts [:exclude-selected-button :attr]) + :on-click exclude-click] + [buttons/button + :src (at) + :class (str "rc-multi-select-exclude-all-button " (get-in parts [:exclude-all-button :class])) + :label [:span + [:i {:class (str "zmdi zmdi-hc-fw-rc zmdi-fast-rewind")}] + [:span + {:style {:position "relative" :top "-1px"}} + (str " exclude " (if (string/blank? @*filter-selections-text) chosen-count (count filtered-selections)))]] + :disabled? (or disabled? (zero? (count filtered-selections)) (not (> (count @*internal-model) (if required? 1 0)))) + :style (merge button-style + (get-in parts [:exclude-all-button :style])) + :attr (get-in parts [:exclude-all-button :attr]) + :on-click exclude-filtered-click]]]) + [box/box + :src (at) + :size (str "0 2 " (if filter-box? "55px" "0px")) ;; 55 = (+ 4 4 28 4 15) - height of the bottom components ;:style {:background-color "lightblue"} - :child ""]]] - [box/v-box - :src (at) - :class (str "rc-multi-select-right " (get-in parts [:right :class])) - :size "50%" - :gap "4px" - :style (merge {:position "relative"} - (get-in parts [:right :style])) - :attr (get-in parts [:right :attr]) - :children [^{:key (gensym)} - [text/label - :src (at) - :label @*warning-message - :class (str "rc-multi-select-warning-message " (get-in parts [:warning-message :class])) - :style (when @*warning-message - (merge - {:color "white" - :background-color "green" - :border-radius "0px" - :opacity "0" - :position "absolute" - :right "0px" - :z-index 1 - :height "25px" - :padding "3px 6px" - :animation-name "rc-multi-select-fade-warning-msg" - :animation-duration "5000ms"} - (get-in parts [:warning-message :style]))) - :attr (get-in parts [:warning-message :attr])] - (when right-label - (if (string? right-label) - [box/h-box - :src (at) - :class (str "rc-multi-select-right-label-container " (get-in parts [:right-label-container :class])) - :style (get-in parts [:right-label-container :style]) - :attr (get-in parts [:right-label-container :attr]) - :justify :between - :children [[:span - (merge - {:class (str "rc-multi-select-right-label " (get-in parts [:right-label :class])) - :style (merge {:font-size "small" - :font-weight "bold"} - (get-in parts [:right-label :style]))} - (get-in parts [:right-label :attr])) - right-label] - [:span - (merge - {:class (str "rc-multi-select-right-label-item-count " (get-in parts [:right-label-item-count :class])) - :style (merge {:font-size "smaller"} - (get-in parts [:right-label-item-count :style]))} - (get-in parts [:right-label-item-count :attr])) - (if (string/blank? @*filter-selections-text) - (rc.util/pluralize chosen-count "item") - (str "showing " (count filtered-selections) " of " chosen-count))]]] - right-label)) - [list-box - :src (at) - :class (str "rc-multi-select-right-list-box " (get-in parts [:right-list-box :class])) - :style (get-in parts [:right-list-box :style]) - :attr (get-in parts [:right-list-box :attr]) - :items filtered-selections - :id-fn id-fn - :label-fn label-fn - :group-fn group-fn - :disabled? disabled? - :*current-item-id *current-selection-id - :group-heading-selected? @*selection-group-heading-selected? - :click-callback selection-click - :double-click-callback exclude-click - :filter-choices-text @*filter-selections-text] - (when filter-box? - [:<> - [box/gap - :src (at) - :size "4px"] - [filter-text-box *filter-selections-text placeholder *warning-message disabled? parts] - [box/gap - :src (at) - :size "4px"] - (if (string/blank? @*filter-selections-text) - [text/label - :src (at) - :label (gstring/unescapeEntities " ") - :style {:font-size "smaller"}] - [text/label - :src (at) - :label [:span "Found " (rc.util/pluralize (count filtered-selections) "match" "matches") " containing " [:strong @*filter-selections-text]] - :class (str "rc-multi-select-right-filter-result-count " (get-in parts [:right-filter-result-count :class])) - :style (merge {:font-size "smaller"} (get-in parts [:right-filter-result-count :style])) - :attr (get-in parts [:right-filter-result-count :attr])])])]]]]])))))) + :child ""]]]) + (add-map-to-hiccup-call + (cmerger :right) + [box/v-box + :src (at) + :size "50%" + :gap "4px" + :children [^{:key (gensym)} + (add-map-to-hiccup-call + (cmerger :warning-message {:warning-message @*warning-message}) + [text/label + :src (at) + :label @*warning-message]) + (when right-label + (if (string? right-label) + (add-map-to-hiccup-call + (cmerger :right-label-container) + [box/h-box + :src (at) + :justify :between + :children [[:span + (flatten-attr (cmerger :right-label)) + right-label] + [:span + (flatten-attr (cmerger :right-label-item-count)) + (if (string/blank? @*filter-selections-text) + (rc.util/pluralize chosen-count "item") + (str "showing " (count filtered-selections) " of " chosen-count))]]]) + right-label)) + (add-map-to-hiccup-call + (cmerger :right-list-box) + [list-box + :src (at) + :items filtered-selections + :id-fn id-fn + :label-fn label-fn + :group-fn group-fn + :disabled? disabled? + :*current-item-id *current-selection-id + :group-heading-selected? @*selection-group-heading-selected? + :click-callback selection-click + :double-click-callback exclude-click + :filter-choices-text @*filter-selections-text]) + (when filter-box? + [:<> + [box/gap + :src (at) + :size "4px"] + [filter-text-box *filter-selections-text placeholder *warning-message disabled? parts] + [box/gap + :src (at) + :size "4px"] + (if (string/blank? @*filter-selections-text) + [text/label + :src (at) + :label (gstring/unescapeEntities " ") + :style {:font-size "smaller"}] + (add-map-to-hiccup-call + (cmerger :right-filter-result-count) + [text/label + :src (at) + :label [:span "Found " (rc.util/pluralize (count filtered-selections) "match" "matches") " containing " [:strong @*filter-selections-text]]]))])]])]])])))))) From 0bd9e0321e994e142a7b6b42b4563158254f7869 Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Thu, 3 Nov 2022 07:23:26 -0700 Subject: [PATCH 17/65] Css splitting completed - css for buttons is split out --- src/re_com/multi_select.cljs | 131 +++++++++++++++++------------------ 1 file changed, 65 insertions(+), 66 deletions(-) diff --git a/src/re_com/multi_select.cljs b/src/re_com/multi_select.cljs index f612fc66..b289e161 100644 --- a/src/re_com/multi_select.cljs +++ b/src/re_com/multi_select.cljs @@ -185,6 +185,20 @@ (flatten-attr (cmerger :list-box-no-results)) (str "No results match \"" filter-choices-text "\"")]))]]))) +(defn multi-button [& {:keys [disabled? label icon on-click class style attr] :as args}] + + (let [cmerger (merge-css multi-select-css-desc {})] + (add-map-to-hiccup-call + (cmerger :button) + [buttons/button + :src (at) + :label [:span + [:i (flatten-attr (cmerger :button-icon {:icon icon}))] + [:span + (flatten-attr (cmerger :button-content)) + (or label "")]] + :disabled? disabled? + :on-click on-click]))) ;;-------------------------------------------------------------------------------------------------- ;; Component: multi-select @@ -256,6 +270,17 @@ ;;TODO: cleanup: middle-[top|bottom]-spacer are not used. Should they go away, or should it be middle-spacer that goes? :middle-top-spacer {:class ["rc-multi-select-middle-top-spacer"]} :middle {:class ["rc-multi-select-middle"]} + :button {:class ["rc-multi-select-button"] + :style {:width "86px" + :height "24px" + :padding "0px 8px 2px 8px" + :margin "8px 6px" + :text-align "left" + :font-variant "small-caps" + :font-size 11}} + :button-icon {:class (fn [{:keys [icon]}] + ["zmdi" "zmdi-hc-fw-rc" (str "zmdi-" icon)])} + :button-content {:style {:position "relative" :top "-1px"}} :include-all-button {:class ["rc-multi-select-include-all-button"]} :include-selected-button {:class ["rc-multi-select-include-selected-button"]} :exclude-selected-button {:class ["rc-multi-select-exclude-selected-button"]} @@ -369,14 +394,7 @@ *selection-group-heading-selected? (reagent/atom false) *warning-message (reagent/atom nil) *filter-choices-text (reagent/atom "") - *filter-selections-text (reagent/atom "") - button-style {:width "86px" - :height "24px" - :padding "0px 8px 2px 8px" - :margin "8px 6px" - :text-align "left" - :font-variant "small-caps" - :font-size 11}] + *filter-selections-text (reagent/atom "")] (fn multi-select-render [& {:keys [choices model required? max-selected-items left-label right-label on-change disabled? filter-box? regex-filter? placeholder width height max-height tab-index id-fn label-fn group-fn sort-fn class style attr parts src] @@ -583,64 +601,45 @@ [box/v-box :src (at) :justify :center - :children [[buttons/button - :src (at) - :class (str "rc-multi-select-include-all-button " (get-in parts [:include-all-button :class])) - :label [:span - [:i {:class (str "zmdi zmdi-hc-fw-rc zmdi-fast-forward")}] - [:span - {:style {:position "relative" :top "-1px"}} - (str " include " (if (string/blank? @*filter-choices-text) potential-count (count filtered-choices)))]] - :disabled? (or disabled? (zero? (count filtered-choices))) - :style (merge button-style - (get-in parts [:include-all-button :style])) - :attr (get-in parts [:include-all-button :attr]) - :on-click include-filtered-click] - [buttons/button - :src (at) - :class (str "rc-multi-select-include-selected-button " (get-in parts [:include-selected-button :class])) - :label [:span - [:i {:class (str "zmdi zmdi-hc-fw-rc zmdi-play")}] - [:span - {:style {:position "relative" :top "-1px"}} - (str " include " (when @*choice-group-heading-selected? - (->> filtered-choices ;; TODO: Inefficient - (filter (fn [item] (= (first @*current-choice-id) (group-fn item)))) - count)))]] - :disabled? (or disabled? (not @*current-choice-id)) - :style (merge button-style - (get-in parts [:include-selected-button :style])) - :attr (get-in parts [:include-selected-button :attr]) - :on-click include-click] - [buttons/button - :src (at) - :class (str "rc-multi-select-exclude-selected-button " (get-in parts [:exclude-selected-button :class])) - :label [:span - [:i {:class (str "zmdi zmdi-hc-fw-rc zmdi-play zmdi-hc-rotate-180")}] - [:span - {:style {:position "relative" :top "-1px"}} - (str " exclude " (when @*selection-group-heading-selected? - (->> filtered-selections ;; TODO: Inefficient - (filter (fn [item] (= (first @*current-selection-id) (group-fn item)))) - count)))]] - :disabled? (or disabled? (not excludable?)) - :style (merge button-style - (get-in parts [:exclude-selected-button :style])) - :attr (get-in parts [:exclude-selected-button :attr]) - :on-click exclude-click] - [buttons/button - :src (at) - :class (str "rc-multi-select-exclude-all-button " (get-in parts [:exclude-all-button :class])) - :label [:span - [:i {:class (str "zmdi zmdi-hc-fw-rc zmdi-fast-rewind")}] - [:span - {:style {:position "relative" :top "-1px"}} - (str " exclude " (if (string/blank? @*filter-selections-text) chosen-count (count filtered-selections)))]] - :disabled? (or disabled? (zero? (count filtered-selections)) (not (> (count @*internal-model) (if required? 1 0)))) - :style (merge button-style - (get-in parts [:exclude-all-button :style])) - :attr (get-in parts [:exclude-all-button :attr]) - :on-click exclude-filtered-click]]]) + :children [(add-map-to-hiccup-call + (cmerger :include-all-button) + [multi-button + :src (at) + :label (str " include " (if (string/blank? @*filter-choices-text) potential-count (count filtered-choices))) + :icon "fast-forward" + :disabled? (or disabled? (zero? (count filtered-choices))) + :on-click include-filtered-click]) + (add-map-to-hiccup-call + (cmerger :include-selected-button) + [multi-button + :src (at) + :label (str " include " (when @*choice-group-heading-selected? + (->> filtered-choices ;; TODO: Inefficient + (filter (fn [item] (= (first @*current-choice-id) (group-fn item)))) + count))) + :icon "play" + :disabled? (or disabled? (not @*current-choice-id)) + :on-click include-click]) + (add-map-to-hiccup-call + (cmerger :exclude-selected-button) + [multi-button + :src (at) + :label (str " exclude " (when @*selection-group-heading-selected? + (->> filtered-selections ;; TODO: Inefficient + (filter (fn [item] (= (first @*current-selection-id) (group-fn item)))) + count))) + ;;TODO: Zmdi reference should be in -css-desc + :icon "play zmdi-hc-rotate-180" + :disabled? (or disabled? (not excludable?)) + :on-click exclude-click]) + (add-map-to-hiccup-call + (cmerger :exclude-all-button) + [multi-button + :src (at) + :label (str " exclude " (if (string/blank? @*filter-selections-text) chosen-count (count filtered-selections))) + :icon "fast-rewind" + :disabled? (or disabled? (zero? (count filtered-selections)) (not (> (count @*internal-model) (if required? 1 0)))) + :on-click exclude-filtered-click])]]) [box/box :src (at) :size (str "0 2 " (if filter-box? "55px" "0px")) ;; 55 = (+ 4 4 28 4 15) - height of the bottom components From c204e56ceb2dc8f5c0b8edf1053352565d474e75 Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Fri, 4 Nov 2022 08:02:44 -0700 Subject: [PATCH 18/65] Css split out --- src/re_com/dropdown.cljs | 313 +++++++++++++++++++++++---------------- 1 file changed, 186 insertions(+), 127 deletions(-) diff --git a/src/re_com/dropdown.cljs b/src/re_com/dropdown.cljs index 6b429905..aadcc502 100644 --- a/src/re_com/dropdown.cljs +++ b/src/re_com/dropdown.cljs @@ -4,7 +4,7 @@ (:require [re-com.config :refer [include-args-desc?]] [re-com.debug :refer [->attr]] - [re-com.util :refer [deref-or-value position-for-id item-for-id]] + [re-com.util :refer [deref-or-value position-for-id item-for-id add-map-to-hiccup-call merge-css flatten-attr]] [re-com.box :refer [align-style flex-child-style]] [re-com.validate :refer [vector-of-maps? css-style? html-attr? parts? number-or-string? log-warning string-or-hiccup? position? position-options-list] :refer-macros [validate-args-macro]] @@ -121,12 +121,15 @@ (< item-offset-top parent-visible-top) item-offset-top)] (when new-scroll-top (set! (.-scrollTop parent) new-scroll-top)))) +(declare single-dropdown-css-desc) (defn- make-group-heading "Render a group heading" [m] - ^{:key (:id m)} [:li.group-result - (:group m)]) + + (let [cmerger (merge-css single-dropdown-css-desc {})] + ^{:key (:id m)} [:li (cmerger :group-heading) + (:group m)])) (defn- choice-item @@ -152,16 +155,20 @@ (fn [id label on-click internal-model] (let [selected (= @internal-model id) + cmerger (merge-css single-dropdown-css-desc {}) class (if selected "highlighted" (when @mouse-over? "mouseover"))] [:li - {:class (str "active-result group-option " class) - :on-mouse-over (handler-fn (reset! mouse-over? true)) - :on-mouse-out (handler-fn (reset! mouse-over? false)) - :on-mouse-down (handler-fn - (on-click id) - (.preventDefault event))} ;; Prevent free-text input as well as the normal dropdown from loosing focus + (flatten-attr + (cmerger :choice-item + {:selected selected + :mouse-over? @mouse-over? + :attr {:on-mouse-over (handler-fn (reset! mouse-over? true)) + :on-mouse-out (handler-fn (reset! mouse-over? false)) + :on-mouse-down (handler-fn + (on-click id) + (.preventDefault event))}})) ;; Prevent free-text input as well as the normal dropdown from loosing focus label]))}))) @@ -175,21 +182,24 @@ (defn- filter-text-box-base "Base function (before lifecycle metadata) to render a filter text box" [filter-box? filter-text key-handler drop-showing? set-filter-text filter-placeholder] - [:div.chosen-search - [:input - {:type "text" - :auto-complete "off" - :style (when-not filter-box? {:position "absolute" ;; When no filter box required, use it but hide it off screen - :width "0px" ;; The rest of these styles make the textbox invisible - :padding "0px" - :border "none"}) - :value @filter-text - :placeholder filter-placeholder - :on-change (handler-fn (set-filter-text (-> event .-target .-value))) - :on-key-down (handler-fn (when-not (key-handler event) - (.stopPropagation event) - (.preventDefault event))) ;; When key-handler returns false, preventDefault - :on-blur (handler-fn (reset! drop-showing? false))}]]) + + (let [cmerger (merge-css single-dropdown-css-desc {})] + [:div + (flatten-attr (cmerger :filter-wrapper)) + [:input + (flatten-attr + (cmerger + :filter-input-box + {:visible? filter-box? + :attr {:type "text" + :auto-complete "off" + :value @filter-text + :placeholder filter-placeholder + :on-change (handler-fn (set-filter-text (-> event .-target .-value))) + :on-key-down (handler-fn (when-not (key-handler event) + (.stopPropagation event) + (.preventDefault event))) ;; When key-handler returns false, preventDefault + :on-blur (handler-fn (reset! drop-showing? false))}}))]])) (def ^:private filter-text-box @@ -203,28 +213,31 @@ (defn- dropdown-top "Render the top part of the dropdown, with the clickable area and the up/down arrow" [] - (let [ignore-click (atom false)] + (let [ignore-click (atom false) + cmerger (merge-css single-dropdown-css-desc {})] (fn [internal-model choices id-fn label-fn tab-index placeholder dropdown-click key-handler filter-box? drop-showing? title? disabled?] (let [_ (reagent/set-state (reagent/current-component) {:filter-box? filter-box?}) text (if (some? @internal-model) (label-fn (item-for-id @internal-model choices :id-fn id-fn)) placeholder)] - [:a.chosen-single.chosen-default - {:style (when disabled? - {:background-color "#EEE"}) - :tab-index (or tab-index 0) - :on-click (handler-fn - (if @ignore-click - (reset! ignore-click false) - (dropdown-click))) - :on-mouse-down (handler-fn - (when @drop-showing? - (reset! ignore-click true))) ;; TODO: Hmmm, have a look at calling preventDefault (and stopProp?) and removing the ignore-click stuff - :on-key-down (handler-fn - (key-handler event) - (when (= (.-which event) 13) ;; Pressing enter on an anchor also triggers click event, which we don't want - (reset! ignore-click true)))} ;; TODO: Hmmm, have a look at calling preventDefault (and stopProp?) and removing the ignore-click stuff + [:a + (flatten-attr + (cmerger :dropdown-top + {:attr + {:tab-index (or tab-index 0) + :on-click (handler-fn + (if @ignore-click + (reset! ignore-click false) + (dropdown-click))) + :on-mouse-down (handler-fn + (when @drop-showing? + (reset! ignore-click true))) ;; TODO: Hmmm, have a look at calling preventDefault (and stopProp?) and removing the ignore-click stuff + :on-key-down (handler-fn + (key-handler event) + (when (= (.-which event) 13) ;; Pressing enter on an anchor also triggers click event, which we don't want + (reset! ignore-click true)))}})) + ;; TODO: Hmmm, have a look at calling preventDefault (and stopProp?) and removing the ignore-click stuff [:span (when title? {:title text}) text] @@ -261,50 +274,59 @@ (defn- free-text-dropdown-top-base "Base function (before lifecycle metadata) to render the top part of the dropdown (free-text), with the editable area and the up/down arrow" [free-text-input select-free-text? free-text-focused? free-text-sel-range internal-model tab-index placeholder dropdown-click key-handler filter-box? drop-showing? cancel width free-text-change auto-complete? choices capitalize? disabled?] - [:ul.chosen-choices - [:li.search-field - [:div.free-text - {:style (when disabled? - {:background-color "#EEE"})} - [:input - {:type "text" - :auto-complete "off" - :class "form-control" - :style {:width width} - :tab-index tab-index - :placeholder placeholder - :value @internal-model - :disabled disabled? - :on-change (handler-fn (let [value (-> event .-target .-value)] - (free-text-change (cond-> value capitalize? capitalize-first-letter)))) - :on-key-down (handler-fn (when-not (key-handler event) - (.stopPropagation event) - (.preventDefault event))) ;; When key-handler returns false, preventDefault - :on-key-press (handler-fn - (let [ins (.-key event)] - (when (= (count ins) 1) ;; Filter out special keys (e.g. enter) - (handle-free-text-insertion event ins auto-complete? capitalize? choices internal-model free-text-sel-range free-text-change)))) - :on-paste (handler-fn - (let [ins (.getData (.-clipboardData event) "Text")] - (handle-free-text-insertion event ins auto-complete? capitalize? choices internal-model free-text-sel-range free-text-change))) - :on-focus (handler-fn - (reset! free-text-focused? true) - (reset! select-free-text? true)) - :on-blur (handler-fn - (when-not filter-box? - (cancel)) - (reset! free-text-focused? false)) ;; Set free-text-focused? after calling cancel to prevent re-focusing - :on-mouse-down (handler-fn (when @drop-showing? - (cancel) - (.preventDefault event))) ;; Prevent text selection flicker (esp. with filter-box) - :ref #(reset! free-text-input %)}] - [:span.b-wrapper - {:on-mouse-down (handler-fn - (dropdown-click) - (when @free-text-focused? - (.preventDefault event)))} ;; Prevent free-text input from loosing focus - (when (not disabled?) - [:b])]]]]) + (let [cmerger (merge-css single-dropdown-css-desc {})] + [:ul + (flatten-attr (cmerger :choices)) + [:li + (flatten-attr (cmerger :choices-search)) + [:div + (flatten-attr (cmerger :free-text-wrapper {:disabled? disabled?})) + [:input + (flatten-attr + (cmerger + :free-text + {:width width + :attr + {:type "text" + :auto-complete "off" + :tab-index tab-index + :placeholder placeholder + :value @internal-model + :disabled disabled? + :on-change (handler-fn (let [value (-> event .-target .-value)] + (free-text-change (cond-> value capitalize? capitalize-first-letter)))) + :on-key-down (handler-fn (when-not (key-handler event) + (.stopPropagation event) + (.preventDefault event))) ;; When key-handler returns false, preventDefault + :on-key-press (handler-fn + (let [ins (.-key event)] + (when (= (count ins) 1) ;; Filter out special keys (e.g. enter) + (handle-free-text-insertion event ins auto-complete? capitalize? choices internal-model free-text-sel-range free-text-change)))) + :on-paste (handler-fn + (let [ins (.getData (.-clipboardData event) "Text")] + (handle-free-text-insertion event ins auto-complete? capitalize? choices internal-model free-text-sel-range free-text-change))) + :on-focus (handler-fn + (reset! free-text-focused? true) + (reset! select-free-text? true)) + :on-blur (handler-fn + (when-not filter-box? + (cancel)) + (reset! free-text-focused? false)) ;; Set free-text-focused? after calling cancel to prevent re-focusing + :on-mouse-down (handler-fn (when @drop-showing? + (cancel) + (.preventDefault event))) ;; Prevent text selection flicker (esp. with filter-box) + :ref #(reset! free-text-input %)}}))] + [:span + (flatten-attr + (cmerger + :free-text-b-wrapper + {:attr {:on-mouse-down (handler-fn + (dropdown-click) + (when @free-text-focused? + (.preventDefault event)))}})) + ;; Prevent free-text input from loosing focus + (when (not disabled?) + [:b])]]]])) (def ^:private free-text-dropdown-top "Render the top part of the dropdown (free-text), with the editable area and the up/down arrow" @@ -369,6 +391,54 @@ {:name :choices-error :level 4 :class "rc-dropdown-choices-error" :impl "[:li]"} {:name :choices-no-results :level 4 :class "rc-dropdown-choices-no-results" :impl "[:li]"}])) +(def single-dropdown-css-desc + {:main {:class (fn [{:keys [free-text? drop-showing? free-text-focused?]}] + ["rc-dropdown" "chosen-container" "noselect" + (if free-text? "chosen-container-multi" "chosen-container-single") + (when (or drop-showing? free-text-focused?) "chosen-container-active") + (when drop-showing? "chosen-with-drop")]) + :style (fn [{:keys [width]}] + (merge (flex-child-style (if width "0 0 auto" "auto")) + (align-style :align-self :start) + {:width width}))} + :tooltip {:class ["rc-dropdown-tooltip"]} + :chosen-drop {:class ["chosen-drop" "rc-dropdown-chosen-drop"] + :style (fn [{:keys [drop-above? top-height drop-height]}] + (when drop-above? + {:transform (gstring/format "translate3d(0px, -%ipx, 0px)" + (+ top-height drop-height -2))}))} + :chosen-results {:class ["chosen-results" "rc-dropdown-chosen-results"] + :style (fn [{:keys [max-height]}] + (when max-height {:max-height max-height}))} + :choices {:class ["chosen-choices"]} + :choices-search {:class ["search-field"]} + :choices-loading {:class ["loading" "rc-dropdown-choices-loading"]} + :choices-error {:class ["error" "rc-dropdown-choices-error"]} + :choices-no-results {:class ["no-results" "rc-dropdown-choices-no-results"]} + :group-heading {:class ["group-result"]} + :choice-item {:class (fn [{:keys [selected mouse-over?]}] + ["active-result" "group-option" (if selected + "highlighted" + (when mouse-over? "mouseover"))])} + :filter-wrapper {:class ["chosen-search"]} + :filter-input-box {:style (fn [{:keys [visible?]}] + (when-not visible? {:position "absolute" ;; When no filter box required, use it but hide it off screen + :width "0px" ;; The rest of these styles make the textbox invisible + :padding "0px" + :border "none"}))} + :dropdown-top {:class ["chosen-single" "chosen-default"] + :style (fn [{:keys [disabled?]}] + (when disabled? + {:background-color "#eee"}))} + :free-text-wrapper {:class ["free-text"] + :style (fn [{:keys [disabled?]}] + (when disabled? + {:background-color "#eee"}))} + :free-text {:class ["form-control"] + :style (fn [{:keys [width]}] + {:width width})} + :free-text-b-wrapper {:class ["b-wrapper"]}}) + (def single-dropdown-parts (when include-args-desc? (-> (map :name single-dropdown-parts-desc) set))) @@ -449,7 +519,8 @@ free-text-sel-range (reagent/atom nil) focus-free-text #(when @free-text-input (.focus @free-text-input)) node (reagent/atom nil) - focus-anchor #(some-> @node (.getElementsByClassName "chosen-single") (.item 0) (.focus))] + focus-anchor #(some-> @node (.getElementsByClassName "chosen-single") (.item 0) (.focus)) + cmerger (merge-css single-dropdown-css-desc args)] (load-choices "" regex-filter? false) (fn single-dropdown-render [& {:keys [choices model on-change id-fn label-fn group-fn render-fn disabled? filter-box? regex-filter? placeholder title? free-text? auto-complete? capitalize? enter-drop? cancelable? set-to-filter filter-placeholder can-drop-above? est-item-height repeat-change? i18n on-drop width max-height tab-index debounce-delay tooltip tooltip-position class style attr parts] @@ -597,12 +668,12 @@ (or filter-box? free-text?))) ;; Use this boolean to allow/prevent the key from being processed by the text box dropdown [:div (merge - {:class (str "rc-dropdown chosen-container " (if free-text? "chosen-container-multi " "chosen-container-single ") "noselect " (when (or @drop-showing? @free-text-focused?) "chosen-container-active ") (when @drop-showing? "chosen-with-drop ") class) ;; Prevent user text selection - :style (merge (flex-child-style (if width "0 0 auto" "auto")) - (align-style :align-self :start) - {:width width} - style) - :ref #(reset! node %)} + (flatten-attr + (cmerger :main {:free-text? free-text? + :drop-showing? @drop-showing? + :free-text-focused? @free-text-focused? + :width width})) + {:ref #(reset! node %)} (when tooltip {:on-mouse-over (handler-fn (reset! over? true)) :on-mouse-out (handler-fn (reset! over? false))}) @@ -614,34 +685,24 @@ :else [dropdown-top internal-model choices id-fn label-fn tab-index placeholder dropdown-click key-handler filter-box? drop-showing? title? disabled?]) (when (and @drop-showing? (not disabled?)) [:div - (merge - {:class (str "chosen-drop rc-dropdown-chosen-drop " (get-in parts [:chosen-drop :class])) - :style (merge (when @drop-above? {:transform (gstring/format "translate3d(0px, -%ipx, 0px)" (+ top-height @drop-height -2))}) - (get-in parts [:chosen-drop :style]))} - (get-in parts [:chosen-drop :attr])) + (flatten-attr + (cmerger :chosen-drop {:drop-above? @drop-above? + :top-height top-height + :drop-height @drop-height})) (when (and (or filter-box? (not free-text?)) (not just-drop?)) [filter-text-box filter-box? filter-text key-handler drop-showing? #(set-filter-text % args true) filter-placeholder]) [:ul - (merge - {:class (str "chosen-results rc-dropdown-chosen-results " (get-in parts [:chosen-results :class])) - :style (merge (when max-height {:max-height max-height}) - (get-in parts [:chosen-results :style]))} - (get-in parts [:chosen-results :attr])) + (flatten-attr + (cmerger :chosen-results {:max-height max-height})) (cond (and choices-fn? (:loading? @choices-state)) [:li - (merge - {:class (str "loading rc-dropdown-choices-loading " (get-in parts [:choices-loading :class])) - :style (get-in parts [:choices-loading :style] {})} - (get-in parts [:choices-loading :attr])) + (flatten-attr (cmerger :choices-loading)) (get i18n :loading "Loading...")] (and choices-fn? (:error @choices-state)) [:li - (merge - {:class (str "error rc-dropdown-choices-error " (get-in parts [:choices-error :class])) - :style (get-in parts [:choices-error :style] {})} - (get-in parts [:choices-error :attr])) + (flatten-attr (cmerger :choices-error)) (:error @choices-state)] (-> filtered-choices count pos?) (let [[group-names group-opt-lists] (choices-with-group-headings filtered-choices group-fn) @@ -656,15 +717,14 @@ (apply concat (map make-h-then-choices group-names group-opt-lists)))) :else [:li - (merge - {:class (str "no-results rc-dropdown-choices-no-results " (get-in parts [:choices-no-results :class])) - :style (get-in parts [:choices-no-results :style] {}) - :on-mouse-down (handler-fn - (when (and (:on-no-results-match-click set-to-filter) - (seq @filter-text) - free-text?) - (callback @filter-text)))} - (get-in parts [:choices-no-results :attr])) + (flatten-attr + (cmerger :choices-no-results + {:attr + {:on-mouse-down (handler-fn + (when (and (:on-no-results-match-click set-to-filter) + (seq @filter-text) + free-text?) + (callback @filter-text)))}})) (gstring/format (or (and (seq @filter-text) (:no-results-match i18n)) (and (empty? @filter-text) (:no-results i18n)) (:no-results-match i18n) @@ -673,13 +733,12 @@ _ (when tooltip (add-watch drop-showing? :tooltip #(reset! over? false))) _ (when on-drop (add-watch drop-showing? :on-drop #(when (and (not %3) %4) (on-drop))))] (if tooltip - [popover-tooltip - :src (at) - :label tooltip - :position (or tooltip-position :below-center) - :showing? showing? - :anchor dropdown - :class (str "rc-dropdown-tooltip " (get-in parts [:tooltip :class])) - :style (get-in parts [:tooltip :class]) - :attr (get-in parts [:tooltip :attr])] + (add-map-to-hiccup-call + (cmerger :tooltip) + [popover-tooltip + :src (at) + :label tooltip + :position (or tooltip-position :below-center) + :showing? showing? + :anchor dropdown]) dropdown))))))) From ee37bf8301f188e1c7838f41a27facefeb99a660 Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Mon, 7 Nov 2022 06:37:01 -0800 Subject: [PATCH 19/65] Css split out --- src/re_com/box.cljs | 144 ++++++++++++++++++++++++++------------------ 1 file changed, 84 insertions(+), 60 deletions(-) diff --git a/src/re_com/box.cljs b/src/re_com/box.cljs index 32a5ab5b..83e69fd9 100644 --- a/src/re_com/box.cljs +++ b/src/re_com/box.cljs @@ -5,6 +5,7 @@ (:require [clojure.string :as string] [re-com.config :refer [include-args-desc?]] + [re-com.util :refer [add-map-to-hiccup-call flatten-attr merge-css]] [re-com.debug :refer [->attr]] [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?]])) @@ -45,7 +46,8 @@ 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 + (let [size (or size "0 1 auto") + 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) @@ -80,7 +82,8 @@ :end "flex-end" :center "center" :between "space-between" - :around "space-around")] + :around "space-around" + "flex-start")] {:-webkit-justify-content js :justify-content js})) @@ -97,7 +100,8 @@ :end "flex-end" :center "center" :baseline "baseline" - :stretch "stretch")] + :stretch "stretch" + "stretch")] {attribute-wk as attribute as})) @@ -118,12 +122,11 @@ ;; Private Component: box-base (visualise-flow? 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] - :as args}] - (let [s (merge +(def box-base-css-desc + {:main {:class ["display-flex"] + :style (fn [{: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 style attr]}] + (merge (flex-flow-style "inherit") (flex-child-style size) (when scroll (scroll-style :overflow scroll)) @@ -148,12 +151,18 @@ (when radius {:border-radius radius}) (if bk-color {:background-color bk-color} - (if visualise-flow? {:background-color "lightblue"} {})) - style)] + (if visualise-flow? {:background-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] + :as args}] + (let [cmerger (merge-css box-base-css-desc args)] [:div (merge (->attr args) - {:class (str class-name "display-flex " class) :style s} + (flatten-attr (cmerger :main args)) attr) child])) @@ -173,22 +182,26 @@ {:name :src :required false :type "map" :validate-fn map? :description [:span "Used in dev builds to assist with debugging. Source code coordinates map containing keys" [:code ":file"] "and" [:code ":line"] ". See 'Debugging'."]} {:name :debug-as :required false :type "map" :validate-fn map? :description [:span "Used in dev builds to assist with debugging, when one component is used implement another component, and we want the implementation component to masquerade as the original component in debug output, such as component stacks. A map optionally containing keys" [:code ":component"] "and" [:code ":args"] "."]}])) +(def gap-css-desc + {:main {:class ["rc-gap"] + :style (fn [{:keys [size width height]}] + (merge + (when size (flex-child-style size)) + (when width {:width width}) + (when height {:height height}) + (when visualise-flow? {:background-color "chocolate"})))}}) + (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}] (or (validate-args-macro gap-args-desc args) - (let [s (merge - (when size (flex-child-style size)) - (when width {:width width}) - (when height {:height height}) - (when visualise-flow? {:background-color "chocolate"}) - style)] + (let [cmerger (merge-css gap-css-desc args)] [:div (merge (->attr args) - {:class (str "rc-gap " class) :style s} + (flatten-attr (cmerger :main {:size size :width width :height height})) attr)]))) @@ -206,6 +219,13 @@ {:name :src :required false :type "map" :validate-fn map? :description [:span "Used in dev builds to assist with debugging. Source code coordinates map containing keys" [:code ":file"] "and" [:code ":line"] ". See 'Debugging'."]} {:name :debug-as :required false :type "map" :validate-fn map? :description [:span "Used in dev builds to assist with debugging, when one component is used implement another component, and we want the implementation component to masquerade as the original component in debug output, such as component stacks. A map optionally containing keys" [:code ":component"] "and" [:code ":args"] "."]}])) +(def line-css-desc + {:main {:class ["rc-line"] + :style (fn [{:keys [size color]}] + (merge + (flex-child-style (str "0 0 " size)) + {:background-color color}))}}) + (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" @@ -214,14 +234,11 @@ :as args}] (or (validate-args-macro line-args-desc args) - (let [s (merge - (flex-child-style (str "0 0 " size)) - {:background-color color} - style)] + (let [cmerger (merge-css line-css-desc args)] [:div (merge (->attr args) - {:class (str "rc-line " class) :style s} + (cmerger :main {:size size :color color}) attr)]))) @@ -251,6 +268,25 @@ {:name :src :required false :type "map" :validate-fn map? :description [:span "Used in dev builds to assist with debugging. Source code coordinates map containing keys" [:code ":file"] "and" [:code ":line"] ". See 'Debugging'."]} {:name :debug-as :required false :type "map" :validate-fn map? :description [:span "Used in dev builds to assist with debugging, when one component is used implement another component, and we want the implementation component to masquerade as the original component in debug output, such as component stacks. A map optionally containing keys" [:code ":component"] "and" [:code ":args"] "."]}])) +(def h-box-css-desc + {:main {:class ["rc-h-box" "display-flex"] + :style (fn [{:keys [size width height min-width min-height max-width max-height justify align align-self margin padding]}] + (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 visualise-flow? {:background-color "gold"})))}}) + (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. @@ -260,22 +296,7 @@ :as args}] (or (validate-args-macro h-box-args-desc args) - (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 visualise-flow? {:background-color "gold"}) - style) + (let [cmerger (merge-css h-box-css-desc args) gap-form (when gap [re-com.box/gap :src (at) :size gap @@ -285,8 +306,8 @@ children)] (into [:div (merge - (->attr args) - {:class (str "rc-h-box display-flex " class) :style s} + (->attr args) + (flatten-attr (cmerger :main args)) attr)] children)))) @@ -316,6 +337,24 @@ {:name :src :required false :type "map" :validate-fn map? :description [:span "Used in dev builds to assist with debugging. Source code coordinates map containing keys" [:code ":file"] "and" [:code ":line"] ". See 'Debugging'."]} {:name :debug-as :required false :type "map" :validate-fn map? :description [:span "Used in dev builds to assist with debugging, when one component is used implement another component, and we want the implementation component to masquerade as the original component in debug output, such as component stacks. A map optionally containing keys" [:code ":component"] "and" [:code ":args"] "."]}])) +(def v-box-css-desc + {:main {:class ["rc-v-box" "display-flex"] + :style (fn [{:keys [size width height min-width min-height max-width max-height justify align align-self margin padding]}] + (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 visualise-flow? {:background-color "antiquewhite"})))}}) (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. @@ -325,22 +364,7 @@ :as args}] (or (validate-args-macro v-box-args-desc args) - (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 visualise-flow? {:background-color "antiquewhite"}) - style) + (let [cmerger (merge-css v-box-css-desc args) gap-form (when gap [re-com.box/gap :src (at) :size gap @@ -351,7 +375,7 @@ (into [:div (merge (->attr args) - {:class (str "rc-v-box display-flex " class) :style s} + (flatten-attr (cmerger :main args)) attr)] children)))) @@ -549,4 +573,4 @@ :style style :attr attr :src src - :debug-as debug-as)))) \ No newline at end of file + :debug-as debug-as)))) From 38360b5897930fbe1fc9d6b3932c6086da9ca48a Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Thu, 10 Nov 2022 08:55:07 -0800 Subject: [PATCH 20/65] Css split out for base datepicker component --- src/re_com/datepicker.cljs | 382 +++++++++++++++++++------------------ 1 file changed, 196 insertions(+), 186 deletions(-) diff --git a/src/re_com/datepicker.cljs b/src/re_com/datepicker.cljs index ace312b3..c5f7df66 100644 --- a/src/re_com/datepicker.cljs +++ b/src/re_com/datepicker.cljs @@ -9,7 +9,7 @@ [cljs-time.predicates :refer [sunday?]] [cljs-time.format :refer [parse unparse formatters formatter]] [re-com.box :refer [border gap box line h-box flex-child-style]] - [re-com.util :refer [deref-or-value now->utc]] + [re-com.util :refer [deref-or-value now->utc add-map-to-hiccup-call merge-css flatten-attr]] [re-com.popover :refer [popover-anchor-wrapper popover-content-wrapper]] [clojure.string :as string]) (:import @@ -27,6 +27,8 @@ (def ^:const date-format (formatter date-format-str)) +(declare datepicker-css-desc) + (defn iso8601->date [iso8601] (when (seq iso8601) (parse (formatters :basic-date) iso8601))) @@ -161,209 +163,179 @@ ;; ---------------------------------------------------------------------------- - (defn- main-div-with - [table-div hide-border? class style attr parts src debug-as] + [table-div hide-border? cmerger attr src debug-as] ;;extra h-box is currently necessary so that calendar & border do not stretch to width of any containing v-box - [h-box - :src src - :debug-as debug-as - :class "rc-datepicker-wrapper" - :children [[border - :src (at) - :class (str "rc-datepicker-border " (get-in parts [:border :class])) - :style (get-in parts [:border :style] {}) - :attr (get-in parts [:border :attr] {}) - :radius "4px" - :size "none" - :border (when hide-border? "none") - :child [:div - (merge - {:class (str "datepicker noselect rc-datepicker " class) - ;; override inherited body larger 14px font-size - ;; override position from css because we are inline - :style (merge {:font-size "13px" - :position "static"} - style)} - attr) - table-div]]]]) + (add-map-to-hiccup-call + (cmerger :wrapper) + [h-box + :src src + :debug-as debug-as + :children [(add-map-to-hiccup-call + (cmerger :border) + [border + :src (at) + :radius "4px" + :size "none" + :border (when hide-border? "none") + :child [:div + (merge + (flatten-attr (cmerger :main)) + attr) + table-div]])]])) (defn- prev-year-icon [& {:keys [parts]}] - [:svg - (merge {:class (str "rc-datepicker-prev-year-icon " (get-in parts [:prev-year-icon :class])) - :style (get-in parts [:prev-year-icon :style]) - :height "24" - :viewBox "0 0 24 24" - :width "24"} - (get-in parts [:prev-year-icon :attr])) - [:g - {:transform "translate(1.5)"} - [:path {:d "m 16.793529,7.4382353 -1.41,-1.41 -5.9999996,5.9999997 5.9999996,6 1.41,-1.41 -4.58,-4.59 z"}] - [:path {:d "m 10.862647,7.4429412 -1.4100003,-1.41 -6,5.9999998 6,6 1.4100003,-1.41 -4.5800003,-4.59 z"}]]]) + (let [cmerger (merge-css datepicker-css-desc {:parts parts})] + [:svg + (flatten-attr (cmerger :prev-year-icon)) + [:g + {:transform "translate(1.5)"} + [:path {:d "m 16.793529,7.4382353 -1.41,-1.41 -5.9999996,5.9999997 5.9999996,6 1.41,-1.41 -4.58,-4.59 z"}] + [:path {:d "m 10.862647,7.4429412 -1.4100003,-1.41 -6,5.9999998 6,6 1.4100003,-1.41 -4.5800003,-4.59 z"}]]])) (defn- prev-month-icon [& {:keys [parts]}] - [:svg - (merge {:class (str "rc-datepicker-prev-month-icon " (get-in parts [:prev-month-icon :class])) - :style (get-in parts [:prev-month-icon :style]) - :height "24" - :viewBox "0 0 24 24" - :width "24"} - (get-in parts [:prev-month-icon :attr])) - [:path {:d "M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12l4.58-4.59z"}]]) + (let [cmerger (merge-css datepicker-css-desc {:parts parts})] + [:svg + (flatten-attr (cmerger :prev-month-icon)) + [:path {:d "M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12l4.58-4.59z"}]])) (defn- next-month-icon [& {:keys [parts]}] - [:svg - (merge {:class (str "rc-datepicker-next-month-icon " (get-in parts [:next-month-icon :class])) - :style (get-in parts [:next-month-icon :style]) - :height "24" - :viewBox "0 0 24 24" - :width "24"} - (get-in parts [:next-month-icon :attr])) - [:path {:d "M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6-6-6z"}]]) + (let [cmerger (merge-css datepicker-css-desc {:parts parts})] + [:svg + (flatten-attr (cmerger :next-month-icon)) + [:path {:d "M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6-6-6z"}]])) (defn- next-year-icon [& {:keys [parts]}] - [:svg - (merge {:class (str "rc-datepicker-next-year-icon " (get-in parts [:next-year-icon :class])) - :style (get-in parts [:next-year-icon :style]) - :height "24" - :viewBox "0 0 24 24" - :width "24"} - (get-in parts [:next-year-icon :attr])) - [:g - {:transform "translate(-1.5)"} - [:path {:d "m 8.5882353,6 -1.41,1.41 4.5799997,4.59 -4.5799997,4.59 1.41,1.41 5.9999997,-6 z"}] - [:path {:d "m 14.547353,5.9623529 -1.41,1.41 4.58,4.5900001 -4.58,4.59 1.41,1.41 6,-6 z"}]]]) + (let [cmerger (merge-css datepicker-css-desc {:parts parts})] + [:svg + (flatten-attr (cmerger :next-year-icon)) + [:g + {:transform "translate(-1.5)"} + [:path {:d "m 8.5882353,6 -1.41,1.41 4.5799997,4.59 -4.5799997,4.59 1.41,1.41 5.9999997,-6 z"}] + [:path {:d "m 14.547353,5.9623529 -1.41,1.41 4.58,4.5900001 -4.58,4.59 1.41,1.41 6,-6 z"}]]])) (defn- prev-year-nav [& {:keys [display-month minimum disabled? parts]}] (let [prev-year-date-time (dec-year @display-month) - prev-year-enabled? (if minimum (cljs-time/after? prev-year-date-time (dec-month minimum)) true)] + prev-year-enabled? (if minimum (cljs-time/after? prev-year-date-time (dec-month minimum)) true) + cmerger (merge-css datepicker-css-desc {:parts parts})] (when (not disabled?) [:<> - [box - :src (at) - :class (str (if prev-year-enabled? "rc-datepicker-selectable " "rc-datepicker-disabled ") "rc-datepicker-prev-year " (get-in parts [:prev-year :class])) - :style (get-in parts [:prev-year :style]) - :attr (merge - {:on-click (handler-fn (when prev-year-enabled? (reset! display-month prev-year-date-time)))} - (get-in parts [:prev-year :attr])) - :width "20px" - :align :center - :justify :center - :child [prev-year-icon - :parts parts]] + (add-map-to-hiccup-call + (cmerger :prev-year {:enabled? prev-year-enabled? + :attr {:on-click (handler-fn (when prev-year-enabled? (reset! display-month prev-year-date-time)))}}) + [box + :src (at) + :width "20px" + :align :center + :justify :center + :child [prev-year-icon + :parts parts]]) [line :src (at)]]))) (defn- prev-month-nav [& {:keys [display-month minimum disabled? parts]}] (let [prev-month-date-time (dec-month @display-month) - prev-month-enabled? (if minimum (cljs-time/after? prev-month-date-time (dec-month minimum)) true)] + prev-month-enabled? (if minimum (cljs-time/after? prev-month-date-time (dec-month minimum)) true) + cmerger (merge-css datepicker-css-desc {:parts parts})] (when (not disabled?) [:<> - [box - :src (at) - :class (str (if prev-month-enabled? "rc-datepicker-selectable " "rc-datepicker-disabled ") "rc-datepicker-prev-month " (get-in parts [:prev-month :class])) - :style (get-in parts [:prev-month :style]) - :attr (merge - {:on-click (handler-fn (when prev-month-enabled? (reset! display-month prev-month-date-time)))} - (get-in parts [:prev-month :attr])) - :width "20px" - :align :center - :justify :center - :child [prev-month-icon - :parts parts]] + (add-map-to-hiccup-call + (cmerger :prev-month {:enabled? prev-month-enabled? + :attr {:on-click (handler-fn (when prev-month-enabled? (reset! display-month prev-month-date-time)))}}) + [box + :src (at) + :width "20px" + :align :center + :justify :center + :child [prev-month-icon + :parts parts]]) [line :src (at)]]))) (defn- next-month-nav [& {:keys [display-month maximum disabled? parts]}] (let [next-month-date-time (inc-month @display-month) - next-month-enabled? (if maximum (cljs-time/before? next-month-date-time maximum) true)] + next-month-enabled? (if maximum (cljs-time/before? next-month-date-time maximum) true) + cmerger (merge-css datepicker-css-desc {:parts parts})] (when (not disabled?) [:<> [line :src (at)] - [box - :src (at) - :class (str (if next-month-enabled? "rc-datepicker-selectable " "rc-datepicker-disabled ") "rc-datepicker-next-month " (get-in parts [:next-month :class])) - :style (get-in parts [:next-month :style]) - :attr (merge - {:on-click (handler-fn (when next-month-enabled? (reset! display-month next-month-date-time)))} - (get-in parts [:next-month :attr])) - :align :center - :justify :center - :width "20px" - :child [next-month-icon - :parts parts]]]))) + (add-map-to-hiccup-call + (cmerger :next-month {:enabled? next-month-enabled? + :attr {:on-click (handler-fn (when next-month-enabled? (reset! display-month next-month-date-time)))}}) + [box + :src (at) + :align :center + :justify :center + :width "20px" + :child [next-month-icon + :parts parts]])]))) (defn- next-year-nav [& {:keys [display-month maximum disabled? parts]}] (let [next-year-date-time (inc-year @display-month) - next-year-enabled? (if maximum (cljs-time/before? next-year-date-time maximum) true)] + next-year-enabled? (if maximum (cljs-time/before? next-year-date-time maximum) true) + cmerger (merge-css datepicker-css-desc {:parts parts})] (when (not disabled?) [:<> [line :src (at)] - [box - :src (at) - :class (str (if next-year-enabled? "rc-datepicker-selectable " "rc-datepicker-disabled ") "rc-datepicker-next-year " (get-in parts [:next-year :class])) - :style (get-in parts [:next-year :style]) - :attr (merge - {:on-click (handler-fn (when next-year-enabled? (reset! display-month next-year-date-time)))} - (get-in parts [:next-year :attr])) - :align :center - :justify :center - :width "20px" - :child [next-year-icon - :parts parts]]]))) + (add-map-to-hiccup-call + (cmerger :next-year {:enabled? next-year-enabled? + :attr {:on-click (handler-fn (when next-year-enabled? (reset! display-month next-year-date-time)))}}) + [box + :src (at) + :align :center + :justify :center + :width "20px" + :child [next-year-icon + :parts parts]])]))) (defn- nav [& {:keys [display-month minimum maximum disabled? i18n parts]}] (let [minimum (deref-or-value minimum) - maximum (deref-or-value maximum)] - [:th - (merge - {:col-span "7" - :class (str "rc-datepicker-nav " (get-in parts [:nav :class])) - :style (merge {:padding "0px"} (get-in parts [:nav :style]))} - (get-in parts [:nav :attr])) - [h-box - :src (at) - :height "100%" - :children [[prev-year-nav - :display-month display-month - :minimum minimum - :disabled? disabled? - :parts parts] - [prev-month-nav - :display-month display-month - :minimum minimum - :disabled? disabled? - :parts parts] - [box - :src (at) - :class (str "rc-datepicker-month " (get-in parts [:month :class])) - :style (get-in parts [:month :style]) - :attr (get-in parts [:month :attr]) - :size "1" - :align :center - :justify :center - :child (month-label @display-month i18n)] - [next-month-nav - :display-month display-month - :maximum maximum - :disabled? disabled? - :parts parts] - [next-year-nav - :display-month display-month - :maximum maximum - :disabled? disabled? - :parts parts]]]])) + maximum (deref-or-value maximum) + cmerger (merge-css datepicker-css-desc {:parts parts})] + [:th + (flatten-attr (cmerger :nav)) + [h-box + :src (at) + :height "100%" + :children [[prev-year-nav + :display-month display-month + :minimum minimum + :disabled? disabled? + :parts parts] + [prev-month-nav + :display-month display-month + :minimum minimum + :disabled? disabled? + :parts parts] + (add-map-to-hiccup-call + (cmerger :month) + [box + :src (at) + :size "1" + :align :center + :justify :center + :child (month-label @display-month i18n)]) + [next-month-nav + :display-month display-month + :maximum maximum + :disabled? disabled? + :parts parts] + [next-year-nav + :display-month display-month + :maximum maximum + :disabled? disabled? + :parts parts]]]])) (defn- week-days [& {:keys [start-of-week i18n parts]}] @@ -381,12 +353,10 @@ (defn- table-thead "Answer 2 x rows showing month with nav buttons and days" [display-month {:keys [show-weeks? minimum maximum start-of-week i18n]} disabled? parts] - (let [template-row (if show-weeks? [:tr [:th]] [:tr])] + (let [template-row (if show-weeks? [:tr [:th]] [:tr]) + cmerger (merge-css datepicker-css-desc {:parts parts})] [:thead - (merge - {:class (str "rc-datepicker-header " (get-in parts [:header :class])) - :style (get-in parts [:header :style] {})} - (get-in parts [:header :attr])) + (flatten-attr (cmerger :header)) (conj template-row [nav :display-month display-month @@ -399,7 +369,7 @@ [week-days :start-of-week start-of-week :i18n i18n - :parts parts])])) + :cmerger cmerger])])) (defn- selection-changed [selection change-callback] @@ -407,7 +377,7 @@ (defn- table-td - [date focus-month selected today {minimum :minimum maximum :maximum selectable-fn :selectable-fn :as attributes} disabled? on-change parts] + [date focus-month selected today {minimum :minimum maximum :maximum selectable-fn :selectable-fn :as attributes} disabled? on-change cmerger] ;;following can be simplified and terse (let [minimum (deref-or-value minimum) maximum (deref-or-value maximum) @@ -426,26 +396,25 @@ :else (str classes " ")) on-click #(when-not (or disabled? unselectable-day?) (selection-changed date on-change))] [:td - (merge - {:class (str classes "rc-datepicker-date " (get-in parts [:date :class])) - :style (get-in parts [:date :style]) - :on-click (handler-fn (on-click))} - (get-in parts [:date :attr])) + (flatten-attr + (cmerger :date {:attr {:on-click (handler-fn (on-click))}})) (cljs-time/day date)])) -(defn- week-td [start-of-week date] - [:td {:class "week"} (week-of-year start-of-week date)]) +(defn- week-td [start-of-week date cmerger] + [:td + (flatten-attr (cmerger :week)) + (week-of-year start-of-week date)]) (defn- table-tr "Return 7 columns of date cells from date inclusive" - [date start-of-week focus-month selected attributes disabled? on-change parts] + [date start-of-week focus-month selected attributes disabled? on-change cmerger] ; {:pre [(sunday? date)]} - (let [table-row (if (:show-weeks? attributes) [:tr (week-td start-of-week date)] [:tr]) + (let [table-row (if (:show-weeks? attributes) [:tr (week-td start-of-week date cmerger)] [:tr]) row-dates (map #(inc-date date %) (range 7)) today (when (:show-today? attributes) (now->utc))] - (into table-row (map #(table-td % focus-month selected today attributes disabled? on-change parts) row-dates)))) + (into table-row (map #(table-td % focus-month selected today attributes disabled? on-change cmerger) row-dates)))) (defn- table-tbody @@ -454,13 +423,11 @@ (let [start-of-week (:start-of-week attributes) current-start (previous (is-day-pred start-of-week) display-month) focus-month (cljs-time/month display-month) - row-start-dates (map #(inc-date current-start (* 7 %)) (range 6))] + row-start-dates (map #(inc-date current-start (* 7 %)) (range 6)) + cmerger (merge-css datepicker-css-desc {:parts parts})] (into [:tbody - (merge - {:class (str "rc-datepicker-dates " (get-in parts [:dates :class])) - :style (get-in parts [:dates :style])} - (get-in parts [:dates :attr]))] - (map #(table-tr % start-of-week focus-month selected attributes disabled? on-change parts) row-start-dates)))) + (flatten-attr (cmerger :dates))] + (map #(table-tr % start-of-week focus-month selected attributes disabled? on-change cmerger) row-start-dates)))) (defn- configure @@ -501,12 +468,59 @@ {:name :dates :level 4 :class "rc-datepicker-dates" :impl "[:tbody]" :notes "The table body containing the dates."} {:type :legacy :level 5 :impl "[:tr]" :notes "A date row. Repeats 6 times." :name-label "-"} {:name :date :level 6 :class "rc-datepicker-date" :impl "[:td]" :notes "A date cell. Repeats 7 times per date row."}])) - (def datepicker-parts (when include-args-desc? (-> (map :name datepicker-parts-desc) set))) + +(def datepicker-css-desc + {:main {:class ["datepicker" "noselect" "rc-datepicker"] + :style {:font-size "13px" + :position "static"}} + :border {:class ["rc-datepicker-border"]} + :wrapper {:class ["rc-datepicker-wrapper"]} + :table {:class ["rc-datepicker-table" "table-condensed"]} + :header {:class ["rc-datepicker-header"]} + :nav {:class ["rc-datepicker-nav"] + :style {:padding "0px"} + :attr {:col-span "7"}} + :prev-year {:class (fn [{:keys [enabled?]}] + ["rc-datepicker-prev-year" + (if enabled? "rc-datepicker-selectable" "rc-datepicker-disabled")])} + :prev-year-icon {:class ["rc-datepicker-prev-year-icon"] + :attr {:height "24" + :viewBox "0 0 24 24" + :width "24"}} + :prev-month {:class (fn [{:keys [enabled?]}] + ["rc-datepicker-prev-month" + (if enabled? "rc-datepicker-selectable" "rc-datepicker-disabled")])} + :prev-month-icon {:class ["rc-datepicker-prev-month-icon"] + :attr {:height "24" + :viewBox "0 0 24 24" + :width "24"}} + :month {:class ["rc-datepicker-month"]} + :next-month {:class (fn [{:keys [enabled?]}] + ["rc-datepicker-next-month" + (if enabled? "rc-datepicker-selectable" "rc-datepicker-disabled")])} + :next-month-icon {:class ["rc-datepicker-next-month-icon"] + :attr {:height "24" + :viewBox "0 0 24 24" + :width "24"}} + :next-year {:class (fn [{:keys [enabled?]}] + ["rc-datepicker-next-year" + (if enabled? "rc-datepicker-selectable" "rc-datepicker-disabled")])} + :next-year-icon {:class ["rc-datepicker-next-year-icon"] + :attr {:height "24" + :viewBox "0 0 24 24" + :width "24"}} + :day {:class (fn [{:keys [day]}] + ["rc-datepicker-day" (str "rc-datepicker-day-" (string/lower-case (:name day)))])} + :week {:class ["rc-datepicker-week"]} + :dates {:class ["rc-datepicker-dates"]} + :date {:class ["rc-datepicker-date"]}}) + + (def datepicker-args-desc (when include-args-desc? [{:name :model :required false :type "satisfies DateTimeProtocol | r/atom" :validate-fn date-like? :description [:span "the selected date. If provided, should pass pred " [:code ":selectable-fn"] ". If not provided, (now->utc) will be used and the returned date will be a " [:code "goog.date.UtcDateTime"]]} @@ -533,7 +547,8 @@ (validate-args-macro datepicker-args-desc args) (let [external-model (reagent/atom (deref-or-value model)) ;; Set model type in stone on creation of this datepicker instance internal-model (reagent/atom @external-model) ;; Holds the last known external value of model, to detect external model changes - display-month (reagent/atom (cljs-time/first-day-of-the-month (or @internal-model (now->utc))))] + display-month (reagent/atom (cljs-time/first-day-of-the-month (or @internal-model (now->utc)))) + cmerger (merge-css datepicker-css-desc args)] (fn datepicker-render [& {:keys [model on-change disabled? start-of-week hide-border? class style attr parts src debug-as] :or {start-of-week 6} ;; Default to Sunday @@ -550,17 +565,12 @@ (reset! display-month (cljs-time/first-day-of-the-month (or @internal-model (now->utc))))) [main-div-with [:table - (merge - {:class (str "table-condensed rc-datepicker-table " (get-in parts [:table :class])) - :style (get-in parts [:table :style])} - (get-in parts [:table :attr])) + (flatten-attr (cmerger :table)) [table-thead display-month configuration disabled? parts] [table-tbody @display-month @internal-model configuration disabled? on-change parts]] hide-border? - class - style + cmerger attr - parts src (or debug-as (reflect-current-component))])))))) From 65ff5f664b03912be8f6f2098834fb2644b10bd8 Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Fri, 11 Nov 2022 09:57:25 -0800 Subject: [PATCH 21/65] Bugfixes - Can't reliably pass a closure as a parameter to a hiccup component. Not sure why not, but best to avoid for now. --- src/re_com/v_table.cljs | 308 +++++++++++++++++++++------------------- 1 file changed, 161 insertions(+), 147 deletions(-) diff --git a/src/re_com/v_table.cljs b/src/re_com/v_table.cljs index 0a72e4a7..2d590db4 100644 --- a/src/re_com/v_table.cljs +++ b/src/re_com/v_table.cljs @@ -20,6 +20,7 @@ (def px (memoize util/px)) +(declare v-table-css-desc) (defn show-row-data-on-alt-click "Make a call to this function in the click event of your row renderer, then every time they Alt+Click on a row, @@ -178,13 +179,14 @@ (defn top-left-content "Render section 1 - the content component" - [top-left-renderer column-header-height cmerger] - (add-map-to-hiccup-call - (cmerger :top-left) - [box/box ;; content component - :src (at) - :height (px (or column-header-height 0)) - :child (if top-left-renderer [top-left-renderer] "")])) + [top-left-renderer column-header-height parts] + (let [cmerger (merge-css v-table-css-desc {:parts parts})] + (add-map-to-hiccup-call + (cmerger :top-left) + [box/box ;; content component + :src (at) + :height (px (or column-header-height 0)) + :child (if top-left-renderer [top-left-renderer] "")]))) ;; ================================================================================== SECTION 2 - row-headers @@ -202,16 +204,17 @@ - rows a vector of row maps (or objects) to render the row-headers from - scroll-y current horizontal scrollbar position in px " - [row-header-renderer key-fn top-row-index rows scroll-y cmerger] - (add-map-to-hiccup-call - (cmerger :row-header-content {:scroll-y scroll-y}) - [box/v-box - :src (at) - :children (map - (fn [index row] - ^{:key (if key-fn (key-fn row) index)} [row-header-renderer index row]) - (iterate inc top-row-index) - rows)])) + [row-header-renderer key-fn top-row-index rows scroll-y parts] + (let [cmerger (merge-css v-table-css-desc {:parts parts})] + (add-map-to-hiccup-call + (cmerger :row-header-content {:scroll-y scroll-y}) + [box/v-box + :src (at) + :children (map + (fn [index row] + ^{:key (if key-fn (key-fn row) index)} [row-header-renderer index row]) + (iterate inc top-row-index) + rows)]))) (defn row-header-viewport @@ -219,39 +222,41 @@ [row-header-renderer key-fn top-row-index rows scroll-y row-header-selection-fn [selection-renderer on-mouse-down on-mouse-enter on-mouse-leave] selection-allowed? row-viewport-height content-rows-height - cmerger] - (add-map-to-hiccup-call - (cmerger - :row-headers - {:max-height content-rows-height - :attr (when row-header-selection-fn - {:on-mouse-down (handler-fn (on-mouse-down :row-header row-header-selection-fn content-rows-height 0 event)) ;; TODO: width set to 0 because we don't have it - could probably measure it - :on-mouse-enter (handler-fn (on-mouse-enter :row-header)) - :on-mouse-leave (handler-fn (on-mouse-leave :row-header))})}) - [box/v-box ;; viewport component - :src (at) - :size (if row-viewport-height "none" "auto") - :height (when row-viewport-height (px row-viewport-height)) - :children [(when selection-allowed? - (let [{:keys [class style attr]} - (cmerger :row-header-selection-rect)] - [selection-renderer class style attr])) ;; selection rectangle component - (if row-header-renderer - [row-header-content row-header-renderer key-fn top-row-index rows scroll-y cmerger] ;; content component - "")]])) + parts] + (let [cmerger (merge-css v-table-css-desc {:parts parts})] + (add-map-to-hiccup-call + (cmerger + :row-headers + {:max-height content-rows-height + :attr (when row-header-selection-fn + {:on-mouse-down (handler-fn (on-mouse-down :row-header row-header-selection-fn content-rows-height 0 event)) ;; TODO: width set to 0 because we don't have it - could probably measure it + :on-mouse-enter (handler-fn (on-mouse-enter :row-header)) + :on-mouse-leave (handler-fn (on-mouse-leave :row-header))})}) + [box/v-box ;; viewport component + :src (at) + :size (if row-viewport-height "none" "auto") + :height (when row-viewport-height (px row-viewport-height)) + :children [(when selection-allowed? + (let [{:keys [class style attr]} + (cmerger :row-header-selection-rect)] + [selection-renderer class style attr])) ;; selection rectangle component + (if row-header-renderer + [row-header-content row-header-renderer key-fn top-row-index rows scroll-y parts] ;; content component + "")]]))) ;; ================================================================================== SECTION 3 - bottom-left (defn bottom-left-content "Render section 3 - the content component" - [bottom-left-renderer column-footer-height cmerger] - (add-map-to-hiccup-call - (cmerger :bottom-left) - [box/box ;; content component - :src (at) - :height (px (or column-footer-height 0)) - :child (if bottom-left-renderer [bottom-left-renderer] "")])) + [bottom-left-renderer column-footer-height parts] + (let [cmerger (merge-css v-table-css-desc {:parts parts})] + (add-map-to-hiccup-call + (cmerger :bottom-left) + [box/box ;; content component + :src (at) + :height (px (or column-footer-height 0)) + :child (if bottom-left-renderer [bottom-left-renderer] "")]))) ;; ================================================================================== SECTION 4 - column-headers @@ -265,12 +270,13 @@ - column-header-renderer function that knows how to render column-headers - scroll-x current horizontal scrollbar position in px " - [column-header-renderer scroll-x cmerger] - (add-map-to-hiccup-call - (cmerger :column-header-content {:scroll-x scroll-x}) - [box/box - :src (at) - :child [column-header-renderer]])) + [column-header-renderer scroll-x parts] + (let [cmerger (merge-css v-table-css-desc {:parts parts})] + (add-map-to-hiccup-call + (cmerger :column-header-content {:scroll-x scroll-x}) + [box/box + :src (at) + :child [column-header-renderer]]))) (defn column-header-viewport @@ -278,24 +284,25 @@ [column-header-renderer scroll-x column-header-selection-fn [selection-renderer on-mouse-down on-mouse-enter on-mouse-leave] selection-allowed? row-viewport-width column-header-height content-rows-width - cmerger] - (add-map-to-hiccup-call - (cmerger :column-headers {:attr - (when column-header-selection-fn - {:on-mouse-down (handler-fn (on-mouse-down :column-header column-header-selection-fn column-header-height content-rows-width event)) - :on-mouse-enter (handler-fn (on-mouse-enter :column-header)) - :on-mouse-leave (handler-fn (on-mouse-leave :column-header))})}) - [box/v-box ;; viewport component - :src (at) - :width (when row-viewport-width (px row-viewport-width)) - :height (px (or column-header-height 0)) - :children [(when selection-allowed? - (let [{:keys [class style attr]} - (cmerger :column-header-selection-rect)] - [selection-renderer class style attr])) ;; selection rectangle component - (if column-header-renderer - [column-header-content column-header-renderer scroll-x cmerger] ;; content component - "")]])) + parts] + (let [cmerger (merge-css v-table-css-desc {:parts parts})] + (add-map-to-hiccup-call + (cmerger :column-headers {:attr + (when column-header-selection-fn + {:on-mouse-down (handler-fn (on-mouse-down :column-header column-header-selection-fn column-header-height content-rows-width event)) + :on-mouse-enter (handler-fn (on-mouse-enter :column-header)) + :on-mouse-leave (handler-fn (on-mouse-leave :column-header))})}) + [box/v-box ;; viewport component + :src (at) + :width (when row-viewport-width (px row-viewport-width)) + :height (px (or column-header-height 0)) + :children [(when selection-allowed? + (let [{:keys [class style attr]} + (cmerger :column-header-selection-rect)] + [selection-renderer class style attr])) ;; selection rectangle component + (if column-header-renderer + [column-header-content column-header-renderer scroll-x parts] ;; content component + "")]]))) ;; ================================================================================== SECTION 5 - rows @@ -313,16 +320,17 @@ - scroll-x current horizontal scrollbar position in px - scroll-y current horizontal scrollbar position in px " - [row-renderer key-fn top-row-index rows scroll-x scroll-y cmerger] - (add-map-to-hiccup-call - (cmerger :row-content {:scroll-x scroll-x :scroll-y scroll-y}) - [box/v-box - :src (at) - :children (map - (fn [index row] - ^{:key (if key-fn (key-fn row) index)} [row-renderer index row]) - (iterate inc top-row-index) - rows)])) + [row-renderer key-fn top-row-index rows scroll-x scroll-y parts] + (let [cmerger (merge-css v-table-css-desc {:parts parts})] + (add-map-to-hiccup-call + (cmerger :row-content {:scroll-x scroll-x :scroll-y scroll-y}) + [box/v-box + :src (at) + :children (map + (fn [index row] + ^{:key (if key-fn (key-fn row) index)} [row-renderer index row]) + (iterate inc top-row-index) + rows)]))) (defn row-viewport @@ -330,26 +338,27 @@ [row-renderer key-fn top-row-index rows scroll-x scroll-y row-selection-fn [selection-renderer on-mouse-down on-mouse-enter on-mouse-leave] selection-allowed? row-viewport-height row-viewport-width row-viewport-id content-rows-height content-rows-width - cmerger] - (add-map-to-hiccup-call - (cmerger :rows {:max-height content-rows-height - :attr - (merge - (when row-selection-fn - {:on-mouse-down (handler-fn (on-mouse-down :row row-selection-fn content-rows-height content-rows-width event)) - :on-mouse-enter (handler-fn (on-mouse-enter :row)) - :on-mouse-leave (handler-fn (on-mouse-leave :row))}) - {:id row-viewport-id})});; Can't be overriding the internally generated id - [box/v-box ;; viewport component - :src (at) - :size (if row-viewport-height "none" "auto") - :width (when row-viewport-width (px row-viewport-width)) - :height (when row-viewport-height (px row-viewport-height)) - :children [(when selection-allowed? - (let [{:keys [class style attr]} - (cmerger :row-selection-rect)] - [selection-renderer class style attr])) ;; selection rectangle component - [row-content row-renderer key-fn top-row-index rows scroll-x scroll-y cmerger]]])) ;; content component + parts] + (let [cmerger (merge-css v-table-css-desc {:parts parts})] + (add-map-to-hiccup-call + (cmerger :rows {:max-height content-rows-height + :attr + (merge + (when row-selection-fn + {:on-mouse-down (handler-fn (on-mouse-down :row row-selection-fn content-rows-height content-rows-width event)) + :on-mouse-enter (handler-fn (on-mouse-enter :row)) + :on-mouse-leave (handler-fn (on-mouse-leave :row))}) + {:id row-viewport-id})});; Can't be overriding the internally generated id + [box/v-box ;; viewport component + :src (at) + :size (if row-viewport-height "none" "auto") + :width (when row-viewport-width (px row-viewport-width)) + :height (when row-viewport-height (px row-viewport-height)) + :children [(when selection-allowed? + (let [{:keys [class style attr]} + (cmerger :row-selection-rect)] + [selection-renderer class style attr])) ;; selection rectangle component + [row-content row-renderer key-fn top-row-index rows scroll-x scroll-y parts]]]))) ;; content component ;; ================================================================================== SECTION 6 - column-footers @@ -363,42 +372,45 @@ - column-footer-renderer function that knows how to render column-footers - scroll-x current horizontal scrollbar position in px " - [column-footer-renderer scroll-x cmerger] - (add-map-to-hiccup-call - (cmerger :column-footer-content {:scroll-x scroll-x}) - [box/box - :src (at) - :child [column-footer-renderer]])) + [column-footer-renderer scroll-x parts] + (let [cmerger (merge-css v-table-css-desc {:parts parts})] + (add-map-to-hiccup-call + (cmerger :column-footer-content {:scroll-x scroll-x}) + [box/box + :src (at) + :child [column-footer-renderer]]))) (defn column-footer-viewport "Render section 6 - the viewport component (which renders the content component as its child)" [column-footer-renderer scroll-x row-viewport-width column-footer-height - cmerger + parts class style attr content-class content-style content-attr] - (add-map-to-hiccup-call - (cmerger :column-footers) - [box/box ;; viewport component - :src (at) - :width (when row-viewport-width (px row-viewport-width)) - :height (px (or column-footer-height 0)) - :child (if column-footer-renderer - [column-footer-content column-footer-renderer scroll-x cmerger] ;; content component - "")])) + (let [cmerger (merge-css v-table-css-desc {:parts parts})] + (add-map-to-hiccup-call + (cmerger :column-footers) + [box/box ;; viewport component + :src (at) + :width (when row-viewport-width (px row-viewport-width)) + :height (px (or column-footer-height 0)) + :child (if column-footer-renderer + [column-footer-content column-footer-renderer scroll-x parts] ;; content component + "")]))) ;; ================================================================================== SECTION 7 - top-right (defn top-right-content "Render section 7 - the content component" - [top-right-renderer column-header-height cmerger] - (add-map-to-hiccup-call - (cmerger :top-right) - [box/box ;; content component - :src (at) - :height (px (or column-header-height 0)) - :child (if top-right-renderer [top-right-renderer] "")])) + [top-right-renderer column-header-height parts] + (let [cmerger (merge-css v-table-css-desc {:parts parts})] + (add-map-to-hiccup-call + (cmerger :top-right) + [box/box ;; content component + :src (at) + :height (px (or column-header-height 0)) + :child (if top-right-renderer [top-right-renderer] "")]))) ;; ================================================================================== SECTION 8 - row-footers @@ -432,29 +444,31 @@ "Render section 8 - the viewport component (which renders the content component as its child)" [row-footer-renderer key-fn top-row-index rows scroll-y row-viewport-height content-rows-height - cmerger] - (add-map-to-hiccup-call - (cmerger :row-footers {:max-height content-rows-height}) - [box/box ;; viewport component - :src (at) - :size (if row-viewport-height "none" "auto") - :height (when row-viewport-height (px row-viewport-height)) - :child (if row-footer-renderer - [row-footer-content row-footer-renderer key-fn top-row-index rows scroll-y cmerger] ;; content component - "")])) + parts] + (let [cmerger (merge-css v-table-css-desc {:parts parts})] + (add-map-to-hiccup-call + (cmerger :row-footers {:max-height content-rows-height}) + [box/box ;; viewport component + :src (at) + :size (if row-viewport-height "none" "auto") + :height (when row-viewport-height (px row-viewport-height)) + :child (if row-footer-renderer + [row-footer-content row-footer-renderer key-fn top-row-index rows scroll-y cmerger] ;; content component + "")]))) ;; ================================================================================== SECTION 9 - bottom-left (defn bottom-right-content "Render section 9 - the content component" - [bottom-right-renderer column-footer-height cmerger] - (add-map-to-hiccup-call - (cmerger :bottom-right) - [box/box ;; content component - :src (at) - :height (px (or column-footer-height 0)) - :child (if bottom-right-renderer [bottom-right-renderer] "")])) + [bottom-right-renderer column-footer-height parts] + (let [cmerger (merge-css v-table-css-desc {:parts parts})] + (add-map-to-hiccup-call + (cmerger :bottom-right) + [box/box ;; content component + :src (at) + :height (px (or column-footer-height 0)) + :child (if bottom-right-renderer [bottom-right-renderer] "")]))) ;;============================ PUBLIC API =================================== @@ -1228,7 +1242,7 @@ ;----------------- column-header-height ;----------------- - cmerger] + parts] ;; ========== SECTION 2 - row-headers @@ -1246,7 +1260,7 @@ row-viewport-height @content-rows-height ;----------------- - cmerger] + parts] ;; ========== SECTION 3 - bottom-left @@ -1255,7 +1269,7 @@ ;----------------- column-footer-height ;----------------- - cmerger] + parts] [box/gap :src (at) @@ -1283,7 +1297,7 @@ column-header-height @content-rows-width ;----------------- - cmerger] + parts] ;; ========== SECTION 5 - rows (main content area) @@ -1305,7 +1319,7 @@ @content-rows-height @content-rows-width ;----------------- - cmerger] + parts] ;; ========== SECTION 6 - column-footers @@ -1316,7 +1330,7 @@ row-viewport-width column-footer-height ;----------------- - cmerger] + parts] ;; ========== Horizontal scrollbar section @@ -1345,7 +1359,7 @@ ;----------------- column-header-height ;----------------- - cmerger] + parts] ;; ========== SECTION 8 - row-footers @@ -1359,7 +1373,7 @@ row-viewport-height @content-rows-height ;----------------- - cmerger] + parts] ;; ========== SECTION 9 - bottom-right @@ -1368,7 +1382,7 @@ ;----------------- column-footer-height ;----------------- - cmerger] + parts] [box/gap :src (at) From a45213ab45bcad4fe86ab6222416c62dafacb397 Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Fri, 18 Nov 2022 10:02:47 -0800 Subject: [PATCH 22/65] Bugfixes --- src/re_com/box.cljs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/re_com/box.cljs b/src/re_com/box.cljs index 83e69fd9..6ad7b9ca 100644 --- a/src/re_com/box.cljs +++ b/src/re_com/box.cljs @@ -161,9 +161,9 @@ (let [cmerger (merge-css box-base-css-desc args)] [:div (merge - (->attr args) - (flatten-attr (cmerger :main args)) - attr) + (flatten-attr (cmerger :main args)) + (->attr args) + attr) child])) @@ -221,16 +221,16 @@ (def line-css-desc {:main {:class ["rc-line"] - :style (fn [{:keys [size color]}] + :style (fn [{:keys [size color] + :or {color "lightgray"}}] (merge - (flex-child-style (str "0 0 " size)) + (flex-child-style (str "0 0 " (or size "1px"))) {:background-color color}))}}) (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}] (or (validate-args-macro line-args-desc args) @@ -270,7 +270,8 @@ (def h-box-css-desc {:main {:class ["rc-h-box" "display-flex"] - :style (fn [{:keys [size width height min-width min-height max-width max-height justify align align-self margin padding]}] + :style (fn [{:keys [size width height min-width min-height max-width max-height justify align align-self margin padding] + :or {size "none" justify :start align :stretch}}] (merge (flex-flow-style "row nowrap") (flex-child-style size) @@ -292,7 +293,6 @@ 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}] (or (validate-args-macro h-box-args-desc args) @@ -339,7 +339,8 @@ (def v-box-css-desc {:main {:class ["rc-v-box" "display-flex"] - :style (fn [{:keys [size width height min-width min-height max-width max-height justify align align-self margin padding]}] + :style (fn [{:keys [size width height min-width min-height max-width max-height justify align align-self margin padding] + :or {size "none" justify :start align :stretch}}] (merge (flex-flow-style "column nowrap") (flex-child-style size) @@ -360,7 +361,6 @@ 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}] (or (validate-args-macro v-box-args-desc args) From f061d933bfd332466189559614003512c22c756e Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Fri, 18 Nov 2022 10:43:58 -0800 Subject: [PATCH 23/65] Bugfix and tools --- src/re_com/util.cljs | 16 +++++++++++++++- src/re_com/v_table.cljs | 6 ++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/re_com/util.cljs b/src/re_com/util.cljs index 9d5a67ae..fdb362cf 100644 --- a/src/re_com/util.cljs +++ b/src/re_com/util.cljs @@ -1,6 +1,7 @@ (ns re-com.util (:require - [reagent.ratom :refer [RAtom Reaction RCursor Track Wrapper]] + [reagent.ratom :refer [RAtom Reaction RCursor Track Wrapper]] + [reagent.dom.server :refer [render-to-string]] [goog.date.DateTime] [goog.date.UtcDateTime] [clojure.string :as string])) @@ -220,6 +221,10 @@ 0 0 0 0))) +(defn say-when [cond itm] + (do (when cond (println itm)) + itm)) + (defn merge-css [css-desc {:as params :keys [class style parts]}] (for [[k v] css-desc :when (not (and (keyword? k) (map? v)))] @@ -251,6 +256,8 @@ :when (contains? defaults k) :let [v (get defaults k)]] [k (if (fn? v) (v xoptions) v)]))] + (when (and tag (not (contains? css-desc tag))) + (println "Missing!!!: " tag)) (reduce combine-css [defaults options user])))) fetch-merged-css) @@ -264,3 +271,10 @@ itm [k v]] itm) (rest hiccup)])) + +(defn say-hiccup [itm] + (println "SOURCE:") + (println itm) + (println "HTML:") + (println (render-to-string itm)) + itm) diff --git a/src/re_com/v_table.cljs b/src/re_com/v_table.cljs index 2d590db4..c2572352 100644 --- a/src/re_com/v_table.cljs +++ b/src/re_com/v_table.cljs @@ -439,7 +439,6 @@ (iterate inc top-row-index) rows)])) - (defn row-footer-viewport "Render section 8 - the viewport component (which renders the content component as its child)" [row-footer-renderer key-fn top-row-index rows scroll-y @@ -558,7 +557,8 @@ :style {:overflow "hidden"}} :row-footers {:class ["rc-v-table-row-footers" "rc-v-table-viewport"] :style (fn [{:keys [max-height]}] - {:max-height (px max-height)})} + {:max-height (px max-height) + :overflow "hidden"})} :row-footer-content {:class ["rc-v-table-row-footer-content" "rc-v-table-content"] :style (fn [{:keys [scroll-y]}] {:margin-top (px scroll-y :negative)})} @@ -1461,3 +1461,5 @@ "content-rows-wh: " (str "(" @content-rows-width "," @content-rows-height ")") "\n")]]])))))}))))) ;"call-count: " @call-count "\n" + + From 88d037bc3b13cacb07c30945d4abbce42122ecad Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Sat, 19 Nov 2022 09:35:19 -0800 Subject: [PATCH 24/65] Complete splitting out of class spec --- src/re_com/box.cljs | 58 +++++++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 23 deletions(-) diff --git a/src/re_com/box.cljs b/src/re_com/box.cljs index 6ad7b9ca..c94fb2ab 100644 --- a/src/re_com/box.cljs +++ b/src/re_com/box.cljs @@ -405,6 +405,9 @@ {:name :src :required false :type "map" :validate-fn map? :description [:span "Used in dev builds to assist with debugging. Source code coordinates map containing keys" [:code ":file"] "and" [:code ":line"] ". See 'Debugging'."]} {:name :debug-as :required false :type "map" :validate-fn map? :description [:span "Used in dev builds to assist with debugging, when one component is used implement another component, and we want the implementation component to masquerade as the original component in debug output, such as component stacks. A map optionally containing keys" [:code ":component"] "and" [:code ":args"] "."]}])) +(def box-css-desc + {:main {:class ["rc-box"]}}) + (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" @@ -413,25 +416,26 @@ :as args}] (or (validate-args-macro box-args-desc args) - (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 - :src src - :debug-as debug-as))) + (let [cmerger (merge-css box-css-desc args) + {:keys [class style attr]} (cmerger :main {:attr attr})] + (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 class + :style style + :attr attr + :src src + :debug-as debug-as)))) ;; ------------------------------------------------------------------------------------ @@ -466,6 +470,9 @@ {:name :src :required false :type "map" :validate-fn map? :description [:span "Used in dev builds to assist with debugging. Source code coordinates map containing keys" [:code ":file"] "and" [:code ":line"] ". See 'Debugging'."]} {:name :debug-as :required false :type "map" :validate-fn map? :description [:span "Used in dev builds to assist with debugging, when one component is used implement another component, and we want the implementation component to masquerade as the original component in debug output, such as component stacks. A map optionally containing keys" [:code ":component"] "and" [:code ":args"] "."]}])) +(def scroller-css-desc + {:main {:class ["rc-scroller"]}}) + (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. @@ -485,7 +492,9 @@ (or (validate-args-macro scroller-args-desc args) (let [not-v-or-h (and (nil? v-scroll) (nil? h-scroll)) - scroll (if (and (nil? scroll) not-v-or-h) :auto scroll)] + scroll (if (and (nil? scroll) not-v-or-h) :auto scroll) + cmerger (merge-css scroller-css-desc args) + {:keys [class style attr]} (cmerger :main {:attr attr})] (box-base :size size :scroll scroll :h-scroll h-scroll @@ -502,7 +511,6 @@ :margin margin :padding padding :child child - :class-name "rc-scroller " :class class :style style :attr attr @@ -538,6 +546,9 @@ {:name :src :required false :type "map" :validate-fn map? :description [:span "Used in dev builds to assist with debugging. Source code coordinates map containing keys" [:code ":file"] "and" [:code ":line"] ". See 'Debugging'."]} {:name :debug-as :required false :type "map" :validate-fn map? :description [:span "Used in dev builds to assist with debugging, when one component is used implement another component, and we want the implementation component to masquerade as the original component in debug output, such as component stacks. A map optionally containing keys" [:code ":component"] "and" [:code ":args"] "."]}])) +(def border-css-desc + {:main {:class ["rc-border"]}}) + (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. @@ -551,7 +562,9 @@ (or (validate-args-macro border-args-desc args) (let [no-border (every? nil? [border l-border r-border t-border b-border]) - default-border "1px solid lightgrey"] + default-border "1px solid lightgrey" + cmerger (merge-css border-css-desc args) + {:keys [class style attr]} (cmerger :main {:attr attr})] (box-base :size size :width width :height height @@ -568,7 +581,6 @@ :b-border b-border :radius radius :child child - :class-name "rc-border " :class class :style style :attr attr From 3d2c757fe44cf77491e0da20d62dd9af3fbbe9e5 Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Sat, 19 Nov 2022 09:36:24 -0800 Subject: [PATCH 25/65] Bugfixes - Fixed date class specifier - Cmerger closure in wrong place --- src/re_com/datepicker.cljs | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/src/re_com/datepicker.cljs b/src/re_com/datepicker.cljs index c5f7df66..83019a10 100644 --- a/src/re_com/datepicker.cljs +++ b/src/re_com/datepicker.cljs @@ -387,17 +387,16 @@ unselectable-day? (if enabled-day (not (selectable-fn date)) true) - classes (cond disabled? "rc-datepicker-disabled" - unselectable-day? "rc-datepicker-unselectable" - (= focus-month (cljs-time/month date)) "rc-datepicker-selectable" - :else "rc-datepicker-selectable rc-datepicker-out-of-focus") - classes (cond (and selected (=date selected date)) (str classes " rc-datepicker-selected start-date end-date ") - (and today (=date date today) (not disabled?)) (str classes " rc-datepicker-today ") - :else (str classes " ")) on-click #(when-not (or disabled? unselectable-day?) (selection-changed date on-change))] [:td (flatten-attr - (cmerger :date {:attr {:on-click (handler-fn (on-click))}})) + (cmerger :date {:disabled? disabled? + :unselectable-day? unselectable-day? + :selected selected + :today today + :focus-month focus-month + :date date + :attr {:on-click (handler-fn (on-click))}})) (cljs-time/day date)])) @@ -518,7 +517,20 @@ ["rc-datepicker-day" (str "rc-datepicker-day-" (string/lower-case (:name day)))])} :week {:class ["rc-datepicker-week"]} :dates {:class ["rc-datepicker-dates"]} - :date {:class ["rc-datepicker-date"]}}) + :date {:class (fn [{:keys [disabled? unselectable-day? selected today focus-month date]}] + (reduce + into + (list + ["rc-datepicker-date"] + (cond disabled? ["rc-datepicker-disabled"] + unselectable-day? ["rc-datepicker-unselectable"] + (= focus-month (cljs-time/month date)) ["rc-datepicker-selectable"] + :else ["rc-datepicker-selectable" "rc-datepicker-out-of-focus"]) + (cond (and selected (=date selected date)) + ["rc-datepicker-selected" "start-date" "end-date"] + (and today (=date date today) (not disabled?)) + ["rc-datepicker-today"] + :else []))))}}) (def datepicker-args-desc @@ -547,8 +559,7 @@ (validate-args-macro datepicker-args-desc args) (let [external-model (reagent/atom (deref-or-value model)) ;; Set model type in stone on creation of this datepicker instance internal-model (reagent/atom @external-model) ;; Holds the last known external value of model, to detect external model changes - display-month (reagent/atom (cljs-time/first-day-of-the-month (or @internal-model (now->utc)))) - cmerger (merge-css datepicker-css-desc args)] + display-month (reagent/atom (cljs-time/first-day-of-the-month (or @internal-model (now->utc))))] (fn datepicker-render [& {:keys [model on-change disabled? start-of-week hide-border? class style attr parts src debug-as] :or {start-of-week 6} ;; Default to Sunday @@ -558,7 +569,8 @@ (let [latest-ext-model (deref-or-value model) disabled? (deref-or-value disabled?) props-with-defaults (merge args {:start-of-week start-of-week}) - configuration (configure props-with-defaults)] + configuration (configure props-with-defaults) + cmerger (merge-css datepicker-css-desc args)] (when (not= @external-model latest-ext-model) ;; Has model changed externally? (reset! external-model latest-ext-model) (reset! internal-model latest-ext-model) From 86d94a0cb6130cdbb3d5b634d61985f96a6c23cf Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Tue, 22 Nov 2022 10:57:47 -0800 Subject: [PATCH 26/65] Css split out --- src/re_com/splits.cljs | 292 +++++++++++++++++++---------------------- 1 file changed, 134 insertions(+), 158 deletions(-) diff --git a/src/re_com/splits.cljs b/src/re_com/splits.cljs index ee875303..b77788fd 100644 --- a/src/re_com/splits.cljs +++ b/src/re_com/splits.cljs @@ -4,53 +4,34 @@ (:require [re-com.config :refer [include-args-desc?]] [re-com.debug :refer [->attr]] - [re-com.util :refer [get-element-by-id sum-scroll-offsets]] + [re-com.util :refer [get-element-by-id sum-scroll-offsets merge-css flatten-attr]] [re-com.box :refer [flex-child-style flex-flow-style]] [re-com.validate :refer [string-or-hiccup? number-or-string? html-attr? css-style? parts?] :refer-macros [validate-args-macro]] [reagent.core :as reagent])) +(declare hv-split-css-desc) + (defn drag-handle "Return a drag handle to go into a vertical or horizontal splitter bar: orientation: Can be :horizontal or :vertical over?: When true, the mouse is assumed to be over the splitter so show a bolder color" [orientation over? parts] (let [vertical? (= orientation :vertical) - length "20px" - width "8px" - pos1 "3px" - pos2 "3px" - color (if over? "#999" "#ccc") - border (str "solid 1px " color) - flex-flow (str (if vertical? "row" "column") " nowrap")] + cmerger (merge-css hv-split-css-desc {:parts parts})] [:div - (merge - {:class (str "rc-" (if vertical? "v" "h") "-split-handle display-flex " (get-in parts [:handle :class])) - :style (merge (flex-flow-style flex-flow) - {:width (if vertical? width length) - :height (if vertical? length width) - :margin "auto"} - (get-in parts [:handle :style]))} - (get-in parts [:handle :attr])) + (flatten-attr (cmerger :handle {:vertical? vertical?})) [:div - (merge - {:class (str "rc-" (if vertical? "v" "h") "-split-handle-bar-1 " (get-in parts [:handle-bar-1 :class])) - :style (merge - (if vertical? - {:width pos1 :height length :border-right border} - {:width length :height pos1 :border-bottom border}) - (get-in parts [:handle-bar-1 :style]))} - (get-in parts [:handle-bar-1 :attr]))] + (flatten-attr (cmerger :handle-bar-1 {:vertical? vertical? :over? over?}))] [:div - (merge - {:class (str "rc-" (if vertical? "v" "h") "-split-handle-bar-2 " (get-in parts [:handle-bar-2 :class])) - :style (merge - (if vertical? - {:width pos2 :height length :border-right border} - {:width length :height pos2 :border-bottom border}) - (get-in parts [:handle-bar-2 :style]))} - (get-in parts [:handle-bar-2 :attr]))]])) + (flatten-attr (cmerger :handle-bar-2 {:vertical? vertical? :over? over?}))]])) +(defn calculate-split-flex-style [value is-px?] + (if is-px? + (if (pos? value) + (str "0 0 " value "px") ;; flex for panel-1 + (str "1 1 0px")) ;; flex for panel-2 + (str value " 1 0px"))) ;; ------------------------------------------------------------------------------------ ;; Component: h-split @@ -73,10 +54,68 @@ {:name :handle-bar-2 :level 3 :class "rc-v-split-handle-bar-2" :impl "[:div]" :notes "The splitter handle's second bar."} {:name :bottom :level 1 :class "rc-v-split-bottom" :impl "[:div]" :notes "Second (i.e. bottom) panel of the split."}])) +(def hv-split-css-desc + {:main {:class (fn [{:keys [vertical?]}] + [(if vertical? "rc-v-split" "rc-h-split") "display-flex"]) + :style (fn [{:keys [size margin width height vertical?]}] + (merge (flex-child-style size) + (flex-flow-style (str (if vertical? "column" "row") " nowrap")) + {:margin margin + :width width + :height height}))} + :splitter {:class (fn [{:keys [vertical?]}] + ["display-flex" (if vertical? "rc-v-split-splitter" "rc-h-split-splitter")]) + :style (fn [{:keys [vertical? size over?]}] + (merge (flex-child-style (str "0 0 " size)) + {:cursor (if vertical? "row-resize" "col-resize")} + (when over? {:background-color "#f8f8f8"})))} + :handle {:class (fn [{:keys [vertical?]}] + [(if vertical? "rc-v-split-handle" "rc-h-split-handle") "display-flex"]) + :style (fn [{:keys [vertical?]}] + (let [[width height] (if vertical? ["8px" "20px"] ["20px" "8px"])] + (merge + (flex-flow-style (str (if vertical? "row" "column") " nowrap")) + {:width width :height height :margin "auto"})))} + :handle-bar-1 {:class (fn [{:keys [vertical?]}] + [(if vertical? "rc-v-split-handle-bar-1" "rc-h-split-handle-bar-1")]) + :style (fn [{:keys [vertical? over?]}] + (let [border (str "solid 1px " (if over? "#999" "#ccc"))] + (if vertical? + {:width "3px" :height "20px" :border-right border} + {:width "20px" :height "3px" :border-bottom border})))} + :handle-bar-2 {:class (fn [{:keys [vertical?]}] + [(if vertical? "rc-v-split-handle-bar-2" "rc-h-split-handle-bar-2")]) + :style (fn [{:keys [vertical? over?]}] + (let [border (str "solid 1px " (if over? "#999" "#ccc"))] + (if vertical? + {:width "3px" :height "20px" :border-right border} + {:width "20px" :height "3px" :border-bottom border})))} + ;; Leaving rc-h-split-top class (below) for backwards compatibility only. + :left {:class ["rc-h-split-left" "rc-h-split-top" "display-flex"] + :style (fn [{:keys [flex dragging?]}] + (merge (flex-child-style flex) + (when dragging? {:pointer-events "none"})))} + ;; Leaving rc-h-split-bottom class (below) for backwards compatibility only. + :right {:class ["rc-h-split-right" "rc-h-split-bottom" "display-flex"] + :style (fn [{:keys [flex dragging?]}] + (merge (flex-child-style flex) + (when dragging? {:pointer-events "none"})))} + :top {:class ["rc-v-split-top" "display-flex"] + :style (fn [{:keys [flex dragging?]}] + (merge (flex-child-style flex) + (when dragging? {:pointer-events "none"})))} + :bottom {:class ["rc-v-split-bottom" "display-flex"] + :style (fn [{:keys [flex dragging?]}] + (merge (flex-child-style flex) + (when dragging? {:pointer-events "none"})))}}) + + (def hv-split-parts (when include-args-desc? (-> (map :name hv-split-parts-desc) set))) + + (def hv-split-args-desc (when include-args-desc? [{:name :panel-1 :required true :type "hiccup" :validate-fn string-or-hiccup? :description "markup to go in the left (or top) panel"} @@ -136,72 +175,40 @@ (reset! dragging? true)) mouseover-split #(reset! over? true) ;; true CANCELs mouse-over (false cancels all others) - mouseout-split #(reset! over? false) - - make-container-attrs (fn [class style attr in-drag?] - (merge {:class (str "rc-h-split display-flex " class) - :id container-id - :style (merge (flex-child-style size) - (flex-flow-style "row nowrap") - {:margin margin - :width width - :height height} - style)} - (when in-drag? ;; only listen when we are dragging - {:on-mouse-up (handler-fn (stop-drag)) - :on-mouse-move (handler-fn (mousemove event)) - :on-mouse-out (handler-fn (mouseout event))}) - (->attr args) - attr)) - - make-panel-attrs (fn [class style attr in-drag? percentage] - (merge - {:class (str "display-flex " class) - :style (merge (flex-child-style (if split-is-px? - (if (pos? percentage) - (str "0 0 " percentage "px") ;; flex for panel-1 - (str "1 1 0px")) ;; flex for panel-2 - (str percentage " 1 0px"))) - (when in-drag? {:pointer-events "none"}) - style)} - attr)) - - make-splitter-attrs (fn [class style attr] - (merge - {:class (str "display-flex " class) - :on-mouse-down (handler-fn (mousedown event)) - :on-mouse-over (handler-fn (mouseover-split)) - :on-mouse-out (handler-fn (mouseout-split)) - :style (merge (flex-child-style (str "0 0 " splitter-size)) - {:cursor "col-resize"} - (when @over? {:background-color "#f8f8f8"}) - style)} - attr))] + mouseout-split #(reset! over? false)] (fn h-split-render [& {:keys [panel-1 panel-2 _size _width _height _on-split-change _initial-split _splitter-size _margin class style attr parts src]}] - [:div (make-container-attrs class style attr @dragging?) - [:div (make-panel-attrs - ;; Leaving rc-h-split-top class (below) for backwards compatibility only. - (str "rc-h-split-top rc-h-split-left " (get-in parts [:left :class])) - (get-in parts [:top :style]) - (get-in parts [:top :attr]) - @dragging? @split-perc) - panel-1] - [:div (make-splitter-attrs - (str "rc-h-split-splitter " (get-in parts [:splitter :class])) - (get-in parts [:splitter :style]) - (get-in parts [:splitter :attr])) - [drag-handle :vertical @over? parts]] - [:div (make-panel-attrs - ;; Leaving rc-h-split-bottom class (below) for backwards compatibility only. - (str "rc-h-split-bottom rc-h-split-right " (get-in parts [:right :class])) - (get-in parts [:bottom :style]) - (get-in parts [:bottom :attr]) - @dragging? (if split-is-px? - (- @split-perc) ;; Negative value indicates this is for panel-2 - (- 100 @split-perc))) - panel-2]])))) + (let [cmerger (merge-css hv-split-css-desc args)] + [:div + (flatten-attr + (cmerger :main {:vertical? false :size size :margin margin :width width :height height + :attr (merge + {:id container-id} + (when @dragging? ;; only listen when we are dragging + {:on-mouse-up (handler-fn (stop-drag)) + :on-mouse-move (handler-fn (mousemove event)) + :on-mouse-out (handler-fn (mouseout event))}))})) + [:div + (flatten-attr + (cmerger :left {:dragging? @dragging? + :flex (calculate-split-flex-style @split-perc split-is-px?)})) + panel-1] + [:div + (flatten-attr + (cmerger :splitter {:vertical? false :size splitter-size :over? @over? + :attr {:on-mouse-down (handler-fn (mousedown event)) + :on-mouse-over (handler-fn (mouseover-split)) + :on-mouse-out (handler-fn (mouseout-split))}})) + [drag-handle :vertical @over? parts]] + [:div + (flatten-attr + (cmerger :right {:dragging? @dragging? + :flex (calculate-split-flex-style (if split-is-px? + (- @split-perc) ;; Negative value indicates this is for panel-2 + (- 100 @split-perc)) + split-is-px?)})) + panel-2]]))))) ;; ------------------------------------------------------------------------------------ @@ -248,69 +255,38 @@ (reset! dragging? true)) mouseover-split #(reset! over? true) - mouseout-split #(reset! over? false) - - make-container-attrs (fn [class style attr in-drag?] - (merge {:class (str "rc-v-split display-flex " class) - :id container-id - :style (merge (flex-child-style size) - (flex-flow-style "column nowrap") - {:margin margin - :width width - :height height} - style)} - (when in-drag? ;; only listen when we are dragging - {:on-mouse-up (handler-fn (stop-drag)) - :on-mouse-move (handler-fn (mousemove event)) - :on-mouse-out (handler-fn (mouseout event))}) - (->attr args) - attr)) - - make-panel-attrs (fn [class style attr in-drag? percentage] - (merge - {:class (str "display-flex " class) - :style (merge (flex-child-style (if split-is-px? - (if (pos? percentage) - (str "0 0 " percentage "px") ;; flex for panel-1 - (str "1 1 0px")) ;; flex for panel-2 - (str percentage " 1 0px"))) - (when in-drag? {:pointer-events "none"}) - style)} - attr)) - - make-splitter-attrs (fn [class style attr] - (merge - {:class (str "display-flex " class) - :on-mouse-down (handler-fn (mousedown event)) - :on-mouse-over (handler-fn (mouseover-split)) - :on-mouse-out (handler-fn (mouseout-split)) - :style (merge (flex-child-style (str "0 0 " splitter-size)) - {:cursor "row-resize"} - (when @over? {:background-color "#f8f8f8"}) - style)} - attr))] + mouseout-split #(reset! over? false)] (fn v-split-render [& {:keys [panel-1 panel-2 _size _width _height _on-split-change _initial-split _splitter-size _margin class style attr parts src]}] - [:div (make-container-attrs class style attr @dragging?) - [:div (make-panel-attrs - (str "rc-v-split-top " (get-in parts [:top :class])) - (get-in parts [:top :style]) - (get-in parts [:top :attr]) - @dragging? - @split-perc) - panel-1] - [:div (make-splitter-attrs - (str "rc-v-split-splitter " (get-in parts [:splitter :class])) - (get-in parts [:splitter :style]) - (get-in parts [:splitter :attr])) - [drag-handle :horizontal @over? parts]] - [:div (make-panel-attrs - (str "rc-v-split-bottom " (get-in parts [:bottom :class])) - (get-in parts [:bottom :style]) - (get-in parts [:bottom :attr]) - @dragging? - (if split-is-px? - (- @split-perc) ;; Negative value indicates this is for panel-2 - (- 100 @split-perc))) - panel-2]])))) + + (let [cmerger (merge-css hv-split-css-desc args)] + [:div + (flatten-attr + (cmerger :main {:vertical? true :size size :margin margin :width width :height height + :attr (merge + {:id container-id} + (when @dragging? ;; only listen when we are dragging + {:on-mouse-up (handler-fn (stop-drag)) + :on-mouse-move (handler-fn (mousemove event)) + :on-mouse-out (handler-fn (mouseout event))}))})) + [:div + (flatten-attr + (cmerger :top {:dragging? @dragging? + :flex (calculate-split-flex-style @split-perc split-is-px?)})) + panel-1] + [:div + (flatten-attr + (cmerger :splitter {:vertical? true :size splitter-size :over? @over? + :attr {:on-mouse-down (handler-fn (mousedown event)) + :on-mouse-over (handler-fn (mouseover-split)) + :on-mouse-out (handler-fn (mouseout-split))}})) + [drag-handle :horizontal @over? parts]] + [:div + (flatten-attr + (cmerger :bottom {:dragging? @dragging? + :flex (calculate-split-flex-style (if split-is-px? + (- @split-perc) ;; Negative value indicates this is for panel-2 + (- 100 @split-perc)) + split-is-px?)})) + panel-2]]))))) From d5351c1ec76e27d5ba5da350c1aaca8c7cbc9ff4 Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Thu, 24 Nov 2022 11:03:42 -0800 Subject: [PATCH 27/65] Css split out --- src/re_com/tag_dropdown.cljs | 342 +++++++++++++++++++---------------- 1 file changed, 185 insertions(+), 157 deletions(-) diff --git a/src/re_com/tag_dropdown.cljs b/src/re_com/tag_dropdown.cljs index aaa5cda9..0992d0f9 100644 --- a/src/re_com/tag_dropdown.cljs +++ b/src/re_com/tag_dropdown.cljs @@ -8,7 +8,7 @@ [reagent.core :as reagent] [re-com.config :refer [include-args-desc?]] [re-com.debug :refer [->attr]] - [re-com.util :refer [deref-or-value px-n assoc-in-if-empty]] + [re-com.util :refer [deref-or-value px-n assoc-in-if-empty merge-css add-map-to-hiccup-call flatten-attr]] [re-com.validate :as validate :refer [parts?]] [re-com.box :refer [box h-box v-box gap]] [re-com.checkbox :refer [checkbox]] @@ -33,90 +33,91 @@ base-style)] base-style))) +(declare tag-dropdown-css-desc) + (defn text-tag [] (let [over? (reagent/atom false)] (fn text-tag-render [& {:keys [tag-data on-click on-unselect tooltip id-fn label-fn description-fn hover-style disabled? class style attr parts] :or {id-fn :id - label-fn :label}}] + label-fn :label} + :as args}] (let [tag-id (id-fn tag-data) tag-id-kw (keyword tag-id) clickable? (and (some? on-click) (not disabled?)) unselectable? (and (some? on-unselect) (not disabled?)) placeholder? (= (:id tag-data) :$placeholder$) - border (when placeholder? "1px dashed #828282") tag-label (label-fn tag-data) - tag-description (when description-fn (description-fn tag-data))] - [v-box - :src (at) - :class (str "rc-tag-dropdown-tag " (get-in parts [:tag :class]) " " (get-in parts [tag-id-kw :class])) - :style (merge (get-in parts [:tag :style]) (get-in parts [tag-id-kw :style])) - :attr (merge (get-in parts [:tag :attr]) (get-in parts [tag-id-kw :attr])) - :align :start - :children [[h-box - :src (at) - :align-self :start - :justify (if placeholder? :end :center) + tag-description (when description-fn (description-fn tag-data)) + cmerger (merge-css tag-dropdown-css-desc args)] + (add-map-to-hiccup-call + (cmerger :tag {:class (get-in parts [tag-id-kw :class]) + :style (get-in parts [tag-id-kw :style]) + :attr (get-in parts [tag-id-kw :attr])}) + [v-box + :src (at) + :align :start + :children [(add-map-to-hiccup-call + (cmerger :tag-box + {:background-color (:background-color tag-data) + :disabled? disabled? + :placeholder? placeholder? + :style (when (and @over? (not disabled?)) hover-style) + :attr {:title tooltip + :on-click (handler-fn (when (and placeholder? (not disabled?)) (on-click (:id tag-data)))) + :on-mouse-enter (handler-fn (reset! over? true)) + :on-mouse-leave (handler-fn (reset! over? false))}}) + [h-box + :src (at) + :align-self :start + :justify (if placeholder? :end :center) - ;:width (if placeholder? (:width tag-data) width) + ;:width (if placeholder? (:width tag-data) width) - :min-width (when placeholder? (:width tag-data)) + :min-width (when placeholder? (:width tag-data)) - :padding "0px 4px" - :margin (px-n 2 (if placeholder? 0 6) 2 0) - :class (str "noselect rc-text-tag " class) - :style (merge - {:color "white" - :background-color (:background-color tag-data) - :cursor (if (not disabled?) "pointer" "default") - :font-size "12px" - ;:font-weight "bold" - :border border - :border-radius "3px"} - (when (and @over? (not disabled?)) hover-style) - style) - :attr (merge - {:title tooltip - :on-click (handler-fn (when (and placeholder? (not disabled?)) (on-click (:id tag-data)))) - :on-mouse-enter (handler-fn (reset! over? true)) - :on-mouse-leave (handler-fn (reset! over? false))} - attr) - :children [(if placeholder? - [box - :src (at) - :style {:color "hsl(194, 61%, 85%)"} - :child (gstring/unescapeEntities "▼")] - [box - :src (at) - :style {:cursor (when clickable? "pointer")} - :attr {:on-click (handler-fn - (when clickable? - (on-click (:id tag-data))) - #_(.stopPropagation event))} - :child (or tag-label "???")]) - (when (and unselectable? (not placeholder?)) - [h-box - :src (at) - :align :center - :children [[box - :src (at) - :style {:margin-left "4px" - :margin-right "3px"} - :child "|"] - [close-button - :src (at) - :color "white" - :hover-color "#ccc" - :div-size 13 - :font-size 13 - :top-offset 1 - :on-click #(when unselectable? - (on-unselect (:id tag-data)))]]])]] - (when tag-description - [:span - {:style {:color "#586069"}} - tag-description])]])))) + :padding "0px 4px" + :margin (px-n 2 (if placeholder? 0 6) 2 0) + :children [(if placeholder? + (add-map-to-hiccup-call + (cmerger :tag-label {:placeholder? placeholder?}) + [box + :src (at) + :child (gstring/unescapeEntities "▼")]) + (add-map-to-hiccup-call + (cmerger :tag-label {:placeholder? placeholder? + :clickable? clickable?}) + [box + :src (at) + :attr {:on-click (handler-fn + (when clickable? + (on-click (:id tag-data))) + #_(.stopPropagation event))} + :child (or tag-label "???")])) + (when (and unselectable? (not placeholder?)) + [h-box + :src (at) + :align :center + :children [(add-map-to-hiccup-call + (cmerger :tag-close-spacer) + [box + :src (at) + :child "|"]) + [close-button + :src (at) + :color "white" + :hover-color "#ccc" + :div-size 13 + :font-size 13 + :top-offset 1 + :on-click #(when unselectable? + (on-unselect (:id tag-data)))]]])]]) + (when tag-description + [:span + (flatten-attr + (cmerger :tag-description)) + tag-description])]]))))) (def tag-dropdown-exclusive-parts-desc (when include-args-desc? @@ -135,6 +136,39 @@ (remove #(= :legacy (:type %))) (map #(update % :level (comp inc inc))))))) +(def tag-dropdown-css-desc + {:main {:class ["rc-tag-dropdown"] + :style (fn [{:keys [disabled?]}] + {:background-color (if disabled? "#EEE" "white") + :color "#BBB" + :border "1px solid lightgrey" + :border-radius "2px" + :overflow "hidden" + :cursor (if disabled? "default" "pointer")})} + :tags {:class ["rc-tag-dropdown-tags"] + :style {:overflow "hidden"}} + + :tag {:class ["rc-tag-dropdown-tag"]} + :tag-box {:class ["noselect" "rc-text-tag"] + :style (fn [{:keys [background-color disabled? placeholder?]}] + {:color "white" + :background-color background-color + :cursor (if disabled? "default" "pointer") + :font-size "12px" + :border (when placeholder? "1px dashed #828282") + :border-radius "3px"})} + :tag-label {:style (fn [{:keys [placeholder? clickable?]}] + (if placeholder? + {:color "hsl(194, 61%, 85%)"} + (when clickable? {:cursor "pointer"})))} + :tag-close-spacer {:style {:margin-left "4px" + :margin-right "3px"}} + :tag-description {:style {:color "#586069"}} + + :close-button-wrapper {:style {:margin-left "5px"}} + :selection-list {:class ["rc-tag-dropdown-selection-list"]} + :popover-anchor-wrapper {:class ["rc-tag-dropdown-popover-anchor-wrapper"]}}) + (def tag-dropdown-parts (when include-args-desc? (-> (map :name tag-dropdown-parts-desc) set))) @@ -191,6 +225,7 @@ abbrev? (and (>= choices-num-chars abbrev-threshold) (number? abbrev-threshold) (fn? abbrev-fn)) + cmerger (merge-css tag-dropdown-css-desc args) placeholder-tag [text-tag :tag-data {:id :$placeholder$ @@ -200,94 +235,87 @@ :on-click #(reset! showing? true) :tooltip "Click to select tags" :hover-style {:background-color "#eee"}] - tag-list-body [selection-list/selection-list - :src (at) - :class (get-in parts [:selection-list :class] "") - :style (get-in parts [:selection-list :style]) - :attr (get-in parts [:selection-list :attr]) - :disabled? disabled? - :required? required? - :parts (-> + tag-list-body (add-map-to-hiccup-call + (cmerger :selection-list) + [selection-list/selection-list + :src (at) + :disabled? disabled? + :required? required? + :parts (-> (select-keys parts selection-list/selection-list-parts) (assoc-in-if-empty [:list-group-item :style :border] "1px solid #ddd") (assoc-in-if-empty [:list-group-item :style :height] "auto") (assoc-in-if-empty [:list-group-item :style :padding] "10px 15px")) - :choices choices - :hide-border? true - :label-fn (fn [tag] - [text-tag - :label-fn label-fn - :description-fn description-fn - :tag-data tag - :style style]) - :model model - :on-change #(on-change %) - :multi-select? true] - tag-main [h-box - :src (at) - :min-width min-width - :max-width max-width - :height height - :align :center - :padding "0px 6px" - :class (str "rc-tag-dropdown " (get-in parts [:main :class])) - :style (merge {:background-color (if disabled? "#EEE" "white") - :color "#BBB" - :border "1px solid lightgrey" - :border-radius "2px" - :overflow "hidden" - :cursor (if disabled? "default" "pointer")} - (get-in parts [:main :style])) - :attr (merge {} - (when (not disabled?) {:on-click (handler-fn (reset! showing? true))}) - (get-in parts [:main :attr])) - :children [[h-box - :src (at) - :class (str "rc-tag-dropdown-tags " (get-in parts [:tags :class])) - :size "1" ;; This line will align the tag placeholder to the right - :style {:overflow "hidden"} - :children (conj - (mapv (fn [tag] - (when (contains? model (:id tag)) - [text-tag - :label-fn (if abbrev? abbrev-fn label-fn) - :tag-data tag - :tooltip (:label tag) - :disabled? disabled? - :on-click #(reset! showing? true) ;; Show dropdown - :on-unselect (when (and unselect-buttons? (not (and (= 1 (count model)) required?))) #(on-change (disj model %))) - :hover-style {:opacity "0.8"} - :style style - :parts parts])) - choices) - (when (not disabled?) - placeholder-tag) - [gap - :src (at) - :size "20px"] - (when (zero? (count model)) - [box - :src (at) - :child (if placeholder placeholder "")]))] - (when (and (not-empty model) (not disabled?) - (not required?)) - [close-button - :src (at) - :parts {:wrapper {:style {:margin-left "5px"}}} - :on-click #(on-change #{})])]]] - [popover-anchor-wrapper - :src src - :debug-as (or debug-as (reflect-current-component)) - :class (str "rc-tag-dropdown-popover-anchor-wrapper " (get-in parts [:popover-anchor-wrapper :class])) - :showing? showing? - :position :below-center - :anchor tag-main - :popover [popover-content-wrapper - :src (at) - :arrow-length 0 - :arrow-width 0 - :arrow-gap 1 - :no-clip? true - :on-cancel #(reset! showing? false) - :padding "19px 19px" - :body tag-list-body]])))))) \ No newline at end of file + :choices choices + :hide-border? true + :label-fn (fn [tag] + [text-tag + :label-fn label-fn + :description-fn description-fn + :tag-data tag + :style style]) + :model model + :on-change #(on-change %) + :multi-select? true]) + tag-main (add-map-to-hiccup-call + (cmerger :main {:disabled? disabled? + :attr (when (not disabled?) + {:on-click (handler-fn (reset! showing? true))})}) + [h-box + :src (at) + :min-width min-width + :max-width max-width + :height height + :align :center + :padding "0px 6px" + :children [(add-map-to-hiccup-call + (cmerger :tags) + [h-box + :src (at) + :size "1" ;; This line will align the tag placeholder to the right + :children (conj + (mapv (fn [tag] + (when (contains? model (:id tag)) + [text-tag + :label-fn (if abbrev? abbrev-fn label-fn) + :tag-data tag + :tooltip (:label tag) + :disabled? disabled? + :on-click #(reset! showing? true) ;; Show dropdown + :on-unselect (when (and unselect-buttons? (not (and (= 1 (count model)) required?))) #(on-change (disj model %))) + :hover-style {:opacity "0.8"} + :style style + :parts parts])) + choices) + (when (not disabled?) + placeholder-tag) + [gap + :src (at) + :size "20px"] + (when (zero? (count model)) + [box + :src (at) + :child (if placeholder placeholder "")]))]) + (when (and (not-empty model) (not disabled?) + (not required?)) + [close-button + :src (at) + :parts {:wrapper (cmerger :close-button-wrapper)} + :on-click #(on-change #{})])]])] + (add-map-to-hiccup-call + (cmerger :popover-anchor-wrapper) + [popover-anchor-wrapper + :src src + :debug-as (or debug-as (reflect-current-component)) + :showing? showing? + :position :below-center + :anchor tag-main + :popover [popover-content-wrapper + :src (at) + :arrow-length 0 + :arrow-width 0 + :arrow-gap 1 + :no-clip? true + :on-cancel #(reset! showing? false) + :padding "19px 19px" + :body tag-list-body]]))))))) From 3fd600896eab8d15a44b642028976b73f1e3dea5 Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Fri, 25 Nov 2022 10:34:49 -0800 Subject: [PATCH 28/65] Css split out --- src/re_com/simple_v_table.cljs | 287 ++++++++++++++++++--------------- 1 file changed, 153 insertions(+), 134 deletions(-) diff --git a/src/re_com/simple_v_table.cljs b/src/re_com/simple_v_table.cljs index ff46eada..d7514e2c 100644 --- a/src/re_com/simple_v_table.cljs +++ b/src/re_com/simple_v_table.cljs @@ -6,7 +6,7 @@ [reagent.core :as reagent] [re-com.config :refer [include-args-desc?]] [re-com.box :refer [box h-box gap]] - [re-com.util :refer [px deref-or-value assoc-in-if-empty]] + [re-com.util :refer [px deref-or-value assoc-in-if-empty merge-css add-map-to-hiccup-call flatten-attr]] [re-com.validate :refer [vector-of-maps? vector-atom? parts?]] [re-com.v-table :as v-table])) @@ -63,109 +63,79 @@ :sub :end :text-top :center}) +(declare simple-v-table-css-desc) + (defn column-header-item [{:keys [id row-label-fn width height align vertical-align header-label sort-by] :as column} parts sort-by-column] (let [{:keys [key-fn comp] :or {key-fn row-label-fn comp compare}} sort-by - {current-key-fn :key-fn order :order} @sort-by-column] - (let [on-click #(swap! sort-by-column swap!-sort-by-column key-fn comp) - align (get vertical-align->align (keyword vertical-align) vertical-align)] - (cond-> - [h-box - :class (str "rc-simple-v-table-column-header-item " (get-in parts [:simple-column-header-item :class])) - :width (px width) - :style (merge - {:padding "0px 12px" - :min-height "24px" - :height (px height) - :font-weight "bold" - :text-align align - :white-space "nowrap" - :overflow "hidden" - :text-overflow "ellipsis"} - (when sort-by - {:cursor "pointer"}) - (get-in parts [:simple-column-header-item :style])) - :attr (merge - {} + {current-key-fn :key-fn order :order} @sort-by-column + on-click #(swap! sort-by-column swap!-sort-by-column key-fn comp) + align (get vertical-align->align (keyword vertical-align) vertical-align) + cmerger (merge-css simple-v-table-css-desc {:parts parts})] + (cond-> + (add-map-to-hiccup-call + (cmerger :simple-column-header-item {:height height :align align :sort-by sort-by + :attr (when sort-by {:on-click on-click})}) + [h-box + :width (px width) + :children [header-label (when sort-by - {:on-click on-click}) - (get-in parts [:simple-column-header-item :attr])) - :children [header-label - (when sort-by - [:<> - [gap :size "16px"] - (if (not= current-key-fn key-fn) - [sort-icon] - (if (= order :desc) - [arrow-down-icon] - [arrow-up-icon]))])]] - (when align) - (into [:align align]))))) + [:<> + [gap :size "16px"] + (if (not= current-key-fn key-fn) + [sort-icon] + (if (= order :desc) + [arrow-down-icon] + [arrow-up-icon]))])]]) + (when align) + (into [:align align])))) (defn column-header-renderer ":column-header-renderer AND :top-left-renderer - Render the table header" [columns parts sort-by-column] - [h-box - :class (str "rc-simple-v-table-column-header noselect " (get-in parts [:simple-column-header :class])) - :style (merge {:padding "4px 0px" - :overflow "hidden" - :white-space "nowrap"} - (get-in parts [:simple-column-header :style])) - :attr (merge {:on-click (handler-fn (v-table/show-row-data-on-alt-click columns 0 event))} - (get-in parts [:simple-column-header :attr])) - :children (into [] - (for [column columns] - [column-header-item column parts sort-by-column]))]) + (let [cmerger (merge-css simple-v-table-css-desc {:parts parts})] + (add-map-to-hiccup-call + (cmerger :simple-column-header + {:attr {:on-click (handler-fn (v-table/show-row-data-on-alt-click columns 0 event))}}) + [h-box + :children (into [] + (for [column columns] + [column-header-item column parts sort-by-column]))]))) (defn row-item "Render a single row item (column) of a single row" [row {:keys [width height align vertical-align row-label-fn] :as column} cell-style parts] - [:div - (merge - {:class (str "rc-simple-v-table-row-item " (get-in parts [:simple-row-item :class])) - :style (merge {:display "inline-block" - :padding (str "0px " "12px") - :width (px width) - :height (px height) - :text-align align - :vertical-align vertical-align - :white-space "nowrap" - :overflow "hidden" - :text-overflow "ellipsis"} - (get-in parts [:simple-row-item :style]) - (if (fn? cell-style) - (cell-style row column) - cell-style))} - (get-in parts [:simple-row-item :attr])) - (row-label-fn row)]) + (let [cmerger (merge-css simple-v-table-css-desc {:parts parts})] + [:div + (cmerger :simple-row-item {:width width + :height height + :align align + :vertical-align vertical-align + :cell-style cell-style + :row row + :column column}) + (row-label-fn row)])) (defn row-renderer ":row-renderer AND :row-header-renderer: Render a single row of the table data" [columns on-click-row on-enter-row on-leave-row striped? row-height row-style cell-style parts table-row-line-color row-index row] - (into + (let [cmerger (merge-css simple-v-table-css-desc {:parts parts})] + (into [:div - (merge - {:class (str "rc-simple-v-table-row " (get-in parts [:simple-row :class])) - :style (merge {:padding "4px 0px" - :overflow "hidden" - :white-space "nowrap" - :height (px row-height) - :border-top (str "1px solid " table-row-line-color) - :cursor (when on-click-row "pointer")} - (when (and striped? (odd? row-index)) - {:background-color "#f2f2f2"}) - (get-in parts [:simple-row :style]) - (if (fn? row-style) - (row-style row) - row-style)) - :on-click (handler-fn (do (v-table/show-row-data-on-alt-click row row-index event) - (when on-click-row (on-click-row row-index)))) - :on-mouse-enter (when on-enter-row (handler-fn (on-enter-row row-index))) - :on-mouse-leave (when on-leave-row (handler-fn (on-leave-row row-index)))} - (get-in parts [:simple-row :attr]))] + (cmerger :simple-row + {:row-height row-height + :row-style row-style + :table-row-line-color table-row-line-color + :on-click-row on-click-row + :odd-row? (and striped? (odd? row-index)) + :row row + :attr {:on-click (handler-fn (do (v-table/show-row-data-on-alt-click row row-index event) + (when on-click-row (on-click-row row-index)))) + :on-mouse-enter (when on-enter-row (handler-fn (on-enter-row row-index))) + :on-mouse-leave (when on-leave-row (handler-fn (on-leave-row row-index)))}})] (for [column columns] - [row-item row column cell-style parts]))) + [row-item row column cell-style parts])))) (def simple-v-table-exclusive-parts-desc @@ -188,6 +158,65 @@ simple-v-table-exclusive-parts-desc (map #(update % :level inc) v-table/v-table-parts-desc)))) +(def simple-v-table-css-desc + {:simple-wrapper {:class ["rc-simple-v-table-wrapper"] + :style (fn [{:keys [max-rows padding max-width table-row-line-color]}] + {;; :flex setting + ;; When max-rows is being used: + ;; - "0 1 auto" allows shrinking within parent but not growing (to prevent vertical spill) + ;; Otherwise: + ;; - "100%" used instead of 1 to resolve conflicts when simple-v-table is the anchor of a popover (e.g. the periodic table demo) + :flex (if max-rows "0 1 auto" "100%") + :background-color "white" + :padding padding + :max-width max-width + :border (str "1px solid " (or table-row-line-color "#EAEEF1")) + :border-radius "3px"})} + :simple-column-header {:class ["rc-simple-v-table-column-header" "noselect"] + :style {:padding "4px 0px" + :overflow "hidden" + :white-space "nowrap"}} + :simple-column-header-item {:class ["rc-simple-v-table-column-header-item"] + :style (fn [{:keys [height align sort-by]}] + {:padding "0px 12px" + :min-height "24px" + :height (px height) + :font-weight "bold" + :text-align align + :white-space "nowrap" + :overflow "hidden" + :text-overflow "ellipsis" + :cursor (when sort-by "pointer")})} + :simple-row {:class ["rc-simple-v-table-row"] + :style (fn [{:keys [row-height + table-row-line-color + on-click-row odd-row? + row-style + row]}] + (merge {:padding "4px 0px" + :overflow "hidden" + :white-space "nowrap" + :height (px row-height) + :border-top (str "1px solid " table-row-line-color) + :cursor (when on-click-row "pointer")} + (when odd-row? {:background-color "#f2f2f2"}) + (if (fn? row-style) + (row-style row) + row-style)))} + :simple-row-item {:class ["rc-simple-v-table-row-item"] + :style (fn [{:keys [width height align vertical-align cell-style row column]}] + (merge {:display "inline-block" + :padding "0px 12px" + :width (px width) + :height (px height) + :text-align align + :vertical-align vertical-align + :white-space "nowrap" + :overflow "hidden" + :text-overflow "ellipsis"} + (if (fn? cell-style) + (cell-style row column) + cell-style)))}}) (def simple-v-table-parts (when include-args-desc? @@ -241,7 +270,6 @@ row-height 31 fixed-column-count 0 table-padding 19 - table-row-line-color "#EAEEF1" fixed-column-border-color "#BBBEC0" column-header-renderer column-header-renderer} :as args}] @@ -252,7 +280,6 @@ content-cols (subvec columns fcc-bounded (count columns)) fixed-content-width (->> fixed-cols (map :width) (reduce + 0)) content-width (->> content-cols (map :width) (reduce + 0)) - table-border-style (str "1px solid " table-row-line-color) fixed-col-border-style (str "1px solid " fixed-column-border-color) actual-table-width (+ fixed-content-width (when (pos? fixed-column-count) 1) ;; 1 border width (for fixed-col-border) @@ -268,59 +295,51 @@ (if (= order :desc) (vec (reverse sorted)) (vec sorted)))) - (deref-or-value model))))] - [box - :src src - :debug-as (or debug-as (reflect-current-component)) - :class (str "rc-simple-v-table-wrapper " (get-in parts [:simple-wrapper :class])) - :style (merge {;; :flex setting - ;; When max-rows is being used: - ;; - "0 1 auto" allows shrinking within parent but not growing (to prevent vertical spill) - ;; Otherwise: - ;; - "100%" used instead of 1 to resolve conflicts when simple-v-table is the anchor of a popover (e.g. the periodic table demo) - :flex (if max-rows "0 1 auto" "100%") - :background-color "white" ;; DEBUG "salmon" - :padding (px table-padding) - :max-width (or max-width (px actual-table-width)) ;; Removing actual-table-width would make the table stretch to the end of the page - :border table-border-style - :border-radius "3px"} - (get-in parts [:simple-wrapper :style])) - :attr (get-in parts [:simple-wrapper :attr]) - :child [v-table/v-table - :src (at) - :model internal-model - - ;; ===== Column header (section 4) - :column-header-renderer (partial column-header-renderer content-cols parts sort-by-column) - :column-header-height column-header-height - - ;; ===== Row header (section 2) - :row-header-renderer (partial row-renderer fixed-cols on-click-row on-enter-row on-leave-row striped? row-height row-style cell-style parts table-row-line-color) - - ;; ===== Rows (section 5) - :row-renderer (partial row-renderer content-cols on-click-row on-enter-row on-leave-row striped? row-height row-style cell-style parts table-row-line-color) - :row-content-width content-width - :row-height row-height - :max-row-viewport-height (when max-rows (* max-rows row-height)) - ;:max-width (px (or max-width (+ fixed-content-width content-width v-table/scrollbar-tot-thick))) ; :max-width handled by enclosing parent above - - ;; ===== Corners (section 1) - :top-left-renderer (partial column-header-renderer fixed-cols parts sort-by-column) ;; Used when there are fixed columns - - ;; ===== Styling - :class class - :parts (cond-> (-> + (deref-or-value model)))) + cmerger (merge-css simple-v-table-css-desc args)] + (add-map-to-hiccup-call + (cmerger :simple-wrapper {:max-rows max-rows + :padding (px table-padding) + :max-width (or max-width (px actual-table-width)) + :table-row-line-color table-row-line-color}) + [box + :src src + :debug-as (or debug-as (reflect-current-component)) + :child [v-table/v-table + :src (at) + :model internal-model + + ;; ===== Column header (section 4) + :column-header-renderer (partial column-header-renderer content-cols parts sort-by-column) + :column-header-height column-header-height + + ;; ===== Row header (section 2) + :row-header-renderer (partial row-renderer fixed-cols on-click-row on-enter-row on-leave-row striped? row-height row-style cell-style parts table-row-line-color) + + ;; ===== Rows (section 5) + :row-renderer (partial row-renderer content-cols on-click-row on-enter-row on-leave-row striped? row-height row-style cell-style parts table-row-line-color) + :row-content-width content-width + :row-height row-height + :max-row-viewport-height (when max-rows (* max-rows row-height)) + ;:max-width (px (or max-width (+ fixed-content-width content-width v-table/scrollbar-tot-thick))) ; :max-width handled by enclosing parent above + + ;; ===== Corners (section 1) + :top-left-renderer (partial column-header-renderer fixed-cols parts sort-by-column) ;; Used when there are fixed columns + + ;; ===== Styling + :class class + :parts (cond-> (-> ;; Remove the parts that are exclusive to simple-v-table, or v-table part ;; validation will fail: (apply dissoc (into [parts] simple-v-table-exclusive-parts)) ;; Inject styles, if not set already, into parts. merge is not safe as it is not ;; recursive so e.g. simply setting :attr would delete :style map. - ;(assoc-in-if-empty [:wrapper :style :background-color] "antiquewhite") ;; DEBUG + ;(assoc-in-if-empty [:wrapper :style :background-color] "antiquewhite") ;; DEBUG (assoc-in-if-empty [:wrapper :style :font-size] "13px") (assoc-in-if-empty [:wrapper :style :cursor] "default")) - (pos? fixed-column-count) - (-> - (assoc-in-if-empty [:top-left :style :border-right] fixed-col-border-style) - (assoc-in-if-empty [:row-headers :style :border-right] fixed-col-border-style)))]])))))) + (pos? fixed-column-count) + (-> + (assoc-in-if-empty [:top-left :style :border-right] fixed-col-border-style) + (assoc-in-if-empty [:row-headers :style :border-right] fixed-col-border-style)))]]))))))) From d63918c6e8f93c18aacef14d8eeba39207f0a092 Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Fri, 25 Nov 2022 11:05:39 -0800 Subject: [PATCH 29/65] Renamed -css-desc -> -css-spec - More accurate. It's not a description --- src/re_com/box.cljs | 32 +++++++++++++------------- src/re_com/buttons.cljs | 28 +++++++++++------------ src/re_com/datepicker.cljs | 28 +++++++++++------------ src/re_com/dropdown.cljs | 16 ++++++------- src/re_com/multi_select.cljs | 18 +++++++-------- src/re_com/popover.cljs | 28 +++++++++++------------ src/re_com/simple_v_table.cljs | 14 ++++++------ src/re_com/splits.cljs | 10 ++++---- src/re_com/tag_dropdown.cljs | 8 +++---- src/re_com/v_table.cljs | 42 +++++++++++++++++----------------- 10 files changed, 112 insertions(+), 112 deletions(-) diff --git a/src/re_com/box.cljs b/src/re_com/box.cljs index c94fb2ab..ebae1c7c 100644 --- a/src/re_com/box.cljs +++ b/src/re_com/box.cljs @@ -122,7 +122,7 @@ ;; Private Component: box-base (visualise-flow? color: lightblue) ;; ------------------------------------------------------------------------------------ -(def box-base-css-desc +(def box-base-css-spec {:main {:class ["display-flex"] :style (fn [{: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 style attr]}] @@ -158,7 +158,7 @@ [& {: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] :as args}] - (let [cmerger (merge-css box-base-css-desc args)] + (let [cmerger (merge-css box-base-css-spec args)] [:div (merge (flatten-attr (cmerger :main args)) @@ -182,7 +182,7 @@ {:name :src :required false :type "map" :validate-fn map? :description [:span "Used in dev builds to assist with debugging. Source code coordinates map containing keys" [:code ":file"] "and" [:code ":line"] ". See 'Debugging'."]} {:name :debug-as :required false :type "map" :validate-fn map? :description [:span "Used in dev builds to assist with debugging, when one component is used implement another component, and we want the implementation component to masquerade as the original component in debug output, such as component stacks. A map optionally containing keys" [:code ":component"] "and" [:code ":args"] "."]}])) -(def gap-css-desc +(def gap-css-spec {:main {:class ["rc-gap"] :style (fn [{:keys [size width height]}] (merge @@ -197,7 +197,7 @@ :as args}] (or (validate-args-macro gap-args-desc args) - (let [cmerger (merge-css gap-css-desc args)] + (let [cmerger (merge-css gap-css-spec args)] [:div (merge (->attr args) @@ -219,7 +219,7 @@ {:name :src :required false :type "map" :validate-fn map? :description [:span "Used in dev builds to assist with debugging. Source code coordinates map containing keys" [:code ":file"] "and" [:code ":line"] ". See 'Debugging'."]} {:name :debug-as :required false :type "map" :validate-fn map? :description [:span "Used in dev builds to assist with debugging, when one component is used implement another component, and we want the implementation component to masquerade as the original component in debug output, such as component stacks. A map optionally containing keys" [:code ":component"] "and" [:code ":args"] "."]}])) -(def line-css-desc +(def line-css-spec {:main {:class ["rc-line"] :style (fn [{:keys [size color] :or {color "lightgray"}}] @@ -234,7 +234,7 @@ :as args}] (or (validate-args-macro line-args-desc args) - (let [cmerger (merge-css line-css-desc args)] + (let [cmerger (merge-css line-css-spec args)] [:div (merge (->attr args) @@ -268,7 +268,7 @@ {:name :src :required false :type "map" :validate-fn map? :description [:span "Used in dev builds to assist with debugging. Source code coordinates map containing keys" [:code ":file"] "and" [:code ":line"] ". See 'Debugging'."]} {:name :debug-as :required false :type "map" :validate-fn map? :description [:span "Used in dev builds to assist with debugging, when one component is used implement another component, and we want the implementation component to masquerade as the original component in debug output, such as component stacks. A map optionally containing keys" [:code ":component"] "and" [:code ":args"] "."]}])) -(def h-box-css-desc +(def h-box-css-spec {:main {:class ["rc-h-box" "display-flex"] :style (fn [{:keys [size width height min-width min-height max-width max-height justify align align-self margin padding] :or {size "none" justify :start align :stretch}}] @@ -296,7 +296,7 @@ :as args}] (or (validate-args-macro h-box-args-desc args) - (let [cmerger (merge-css h-box-css-desc args) + (let [cmerger (merge-css h-box-css-spec args) gap-form (when gap [re-com.box/gap :src (at) :size gap @@ -337,7 +337,7 @@ {:name :src :required false :type "map" :validate-fn map? :description [:span "Used in dev builds to assist with debugging. Source code coordinates map containing keys" [:code ":file"] "and" [:code ":line"] ". See 'Debugging'."]} {:name :debug-as :required false :type "map" :validate-fn map? :description [:span "Used in dev builds to assist with debugging, when one component is used implement another component, and we want the implementation component to masquerade as the original component in debug output, such as component stacks. A map optionally containing keys" [:code ":component"] "and" [:code ":args"] "."]}])) -(def v-box-css-desc +(def v-box-css-spec {:main {:class ["rc-v-box" "display-flex"] :style (fn [{:keys [size width height min-width min-height max-width max-height justify align align-self margin padding] :or {size "none" justify :start align :stretch}}] @@ -364,7 +364,7 @@ :as args}] (or (validate-args-macro v-box-args-desc args) - (let [cmerger (merge-css v-box-css-desc args) + (let [cmerger (merge-css v-box-css-spec args) gap-form (when gap [re-com.box/gap :src (at) :size gap @@ -405,7 +405,7 @@ {:name :src :required false :type "map" :validate-fn map? :description [:span "Used in dev builds to assist with debugging. Source code coordinates map containing keys" [:code ":file"] "and" [:code ":line"] ". See 'Debugging'."]} {:name :debug-as :required false :type "map" :validate-fn map? :description [:span "Used in dev builds to assist with debugging, when one component is used implement another component, and we want the implementation component to masquerade as the original component in debug output, such as component stacks. A map optionally containing keys" [:code ":component"] "and" [:code ":args"] "."]}])) -(def box-css-desc +(def box-css-spec {:main {:class ["rc-box"]}}) (defn box @@ -416,7 +416,7 @@ :as args}] (or (validate-args-macro box-args-desc args) - (let [cmerger (merge-css box-css-desc args) + (let [cmerger (merge-css box-css-spec args) {:keys [class style attr]} (cmerger :main {:attr attr})] (box-base :size size :width width @@ -470,7 +470,7 @@ {:name :src :required false :type "map" :validate-fn map? :description [:span "Used in dev builds to assist with debugging. Source code coordinates map containing keys" [:code ":file"] "and" [:code ":line"] ". See 'Debugging'."]} {:name :debug-as :required false :type "map" :validate-fn map? :description [:span "Used in dev builds to assist with debugging, when one component is used implement another component, and we want the implementation component to masquerade as the original component in debug output, such as component stacks. A map optionally containing keys" [:code ":component"] "and" [:code ":args"] "."]}])) -(def scroller-css-desc +(def scroller-css-spec {:main {:class ["rc-scroller"]}}) (defn scroller @@ -493,7 +493,7 @@ (validate-args-macro scroller-args-desc args) (let [not-v-or-h (and (nil? v-scroll) (nil? h-scroll)) scroll (if (and (nil? scroll) not-v-or-h) :auto scroll) - cmerger (merge-css scroller-css-desc args) + cmerger (merge-css scroller-css-spec args) {:keys [class style attr]} (cmerger :main {:attr attr})] (box-base :size size :scroll scroll @@ -546,7 +546,7 @@ {:name :src :required false :type "map" :validate-fn map? :description [:span "Used in dev builds to assist with debugging. Source code coordinates map containing keys" [:code ":file"] "and" [:code ":line"] ". See 'Debugging'."]} {:name :debug-as :required false :type "map" :validate-fn map? :description [:span "Used in dev builds to assist with debugging, when one component is used implement another component, and we want the implementation component to masquerade as the original component in debug output, such as component stacks. A map optionally containing keys" [:code ":component"] "and" [:code ":args"] "."]}])) -(def border-css-desc +(def border-css-spec {:main {:class ["rc-border"]}}) (defn border @@ -563,7 +563,7 @@ (validate-args-macro border-args-desc args) (let [no-border (every? nil? [border l-border r-border t-border b-border]) default-border "1px solid lightgrey" - cmerger (merge-css border-css-desc args) + cmerger (merge-css border-css-spec args) {:keys [class style attr]} (cmerger :main {:attr attr})] (box-base :size size :width width diff --git a/src/re_com/buttons.cljs b/src/re_com/buttons.cljs index b343900b..4d1c3a06 100644 --- a/src/re_com/buttons.cljs +++ b/src/re_com/buttons.cljs @@ -21,7 +21,7 @@ {:name :tooltip :level 1 :class "rc-button-tooltip" :impl "[popover-tooltip]" :notes "Tooltip, if enabled."} {:type :legacy :level 1 :class "rc-button" :impl "[:button]" :notes "The actual button."}])) -(def button-css-desc +(def button-css-spec {:main {:class ["rc-button" "btn"] :style (flex-child-style "none") } :wrapper {:class ["rc-button-wrapper" "display-inline-flex"]} @@ -58,7 +58,7 @@ (do (when-not tooltip (reset! showing? false)) ;; To prevent tooltip from still showing after button drag/drop (let [disabled? (deref-or-value disabled?) - cmerger (merge-css button-css-desc args) + cmerger (merge-css button-css-spec args) the-button [:button (merge (flatten-attr (cmerger :main)) @@ -102,7 +102,7 @@ {:type :legacy :level 1 :class "rc-md-circle-icon-button" :impl "[:div]" :notes "The actual button."} {:name :icon :level 2 :class "rc-md-circle-icon-button-icon" :impl "[:i]" :notes "The button icon."}])) -(def md-circle-icon-button-css-desc +(def md-circle-icon-button-css-spec {:main {:class (fn [{:keys [size emphasise? disabled?]}] ["noselect" "rc-md-circle-icon-button" @@ -156,7 +156,7 @@ (validate-args-macro md-circle-icon-button-args-desc args) (do (when-not tooltip (reset! showing? false)) ;; To prevent tooltip from still showing after button drag/drop - (let [cmerger (merge-css md-circle-icon-button-css-desc args) + (let [cmerger (merge-css md-circle-icon-button-css-spec args) the-button [:div (merge (flatten-attr @@ -198,7 +198,7 @@ {:type :legacy :level 1 :class "rc-md-icon-button" :impl "[:div]" :notes "The actual button."} {:name :icon :level 2 :class "rc-md-icon-button-icon" :impl "[:i]" :notes "The button icon."}])) -(def md-icon-button-css-desc +(def md-icon-button-css-spec {:main {:class (fn [{:keys [size emphasise? disabled?]}] ["noselect" "rc-md-icon-button" @@ -252,7 +252,7 @@ (validate-args-macro md-icon-button-args-desc args) (do (when-not tooltip (reset! showing? false)) ;; To prevent tooltip from still showing after button drag/drop - (let [cmerger (merge-css md-circle-icon-button-css-desc args) + (let [cmerger (merge-css md-circle-icon-button-css-spec args) the-button [:div (merge (flatten-attr @@ -293,7 +293,7 @@ {:type :legacy :level 1 :class "rc-info-button" :impl "[:div]" :notes "The actual button."} {:name :icon :level 2 :class "rc-info-button-icon" :impl "[:svg]" :notes "The button icon."}])) -(def info-button-css-desc +(def info-button-css-spec {:main {:class (fn [{:keys [disabled?]}] ["noselect" "rc-info-button" (when disabled? "rc-icon-disabled")]) :style (fn [{:keys [disabled?]}] @@ -330,7 +330,7 @@ [& {:keys [info position width disabled? class style attr parts src debug-as] :as args}] (or (validate-args-macro info-button-args-desc args) - (let [cmerger (merge-css info-button-css-desc args)] + (let [cmerger (merge-css info-button-css-spec args)] (add-map-to-hiccup-call (cmerger :tooltip) [popover-tooltip @@ -371,7 +371,7 @@ {:type :legacy :level 1 :class "rc-row-button" :impl "[:div]" :notes "The actual button."} {:name :icon :level 2 :class "rc-row-button-icon" :impl "[:i]" :notes "The button icon."}])) -(def row-button-css-desc +(def row-button-css-spec {:main {:class (fn [{:keys [disabled? mouse-over-row?]}] ["noselect" "rc-row-button" (when mouse-over-row? "rc-row-mouse-over-row") @@ -412,7 +412,7 @@ (validate-args-macro row-button-args-desc args) (do (when-not tooltip (reset! showing? false)) ;; To prevent tooltip from still showing after button drag/drop - (let [cmerger (merge-css row-button-css-desc args) + (let [cmerger (merge-css row-button-css-spec args) the-button [:div (merge (flatten-attr @@ -454,7 +454,7 @@ {:name :container :level 1 :class "rc-hyperlink-container" :impl "[box]"} {:type :legacy :level 2 :class "rc-hyperlink" :impl "[:a]" :notes "The anchor."}])) -(def hyperlink-css-desc +(def hyperlink-css-spec {:main {:class ["noselect" "rc-hyperlink"] :style (fn [{:keys [disabled?]}] (merge @@ -500,7 +500,7 @@ (when-not tooltip (reset! showing? false)) ;; To prevent tooltip from still showing after button drag/drop (let [label (deref-or-value label) disabled? (deref-or-value disabled?) - cmerger (merge-css hyperlink-css-desc args) + cmerger (merge-css hyperlink-css-spec args) the-button (add-map-to-hiccup-call (cmerger :container) [box @@ -545,7 +545,7 @@ {:name :tooltip :level 1 :class "rc-hyperlink-href-tooltip" :impl "[popover-tooltip]" :notes "Tooltip, if enabled."} {:type :legacy :level 2 :class "rc-hyperlink-href" :impl "[:a]" :notes "The anchor."}])) -(def hyperlink-href-css-desc +(def hyperlink-href-css-spec {:main {:class ["rc-hyperlink-href" "noselect"] :style (fn [{:keys [disabled?]}] (merge @@ -593,7 +593,7 @@ href (deref-or-value href) target (deref-or-value target) disabled? (deref-or-value disabled?) - cmerger (merge-css hyperlink-href-css-desc args) + cmerger (merge-css hyperlink-href-css-spec args) the-button [:a (merge (flatten-attr (cmerger :main {:disabled? disabled?})) {:target target} diff --git a/src/re_com/datepicker.cljs b/src/re_com/datepicker.cljs index 83019a10..4acfb6ac 100644 --- a/src/re_com/datepicker.cljs +++ b/src/re_com/datepicker.cljs @@ -27,7 +27,7 @@ (def ^:const date-format (formatter date-format-str)) -(declare datepicker-css-desc) +(declare datepicker-css-spec) (defn iso8601->date [iso8601] (when (seq iso8601) @@ -186,7 +186,7 @@ (defn- prev-year-icon [& {:keys [parts]}] - (let [cmerger (merge-css datepicker-css-desc {:parts parts})] + (let [cmerger (merge-css datepicker-css-spec {:parts parts})] [:svg (flatten-attr (cmerger :prev-year-icon)) [:g @@ -196,21 +196,21 @@ (defn- prev-month-icon [& {:keys [parts]}] - (let [cmerger (merge-css datepicker-css-desc {:parts parts})] + (let [cmerger (merge-css datepicker-css-spec {:parts parts})] [:svg (flatten-attr (cmerger :prev-month-icon)) [:path {:d "M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12l4.58-4.59z"}]])) (defn- next-month-icon [& {:keys [parts]}] - (let [cmerger (merge-css datepicker-css-desc {:parts parts})] + (let [cmerger (merge-css datepicker-css-spec {:parts parts})] [:svg (flatten-attr (cmerger :next-month-icon)) [:path {:d "M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6-6-6z"}]])) (defn- next-year-icon [& {:keys [parts]}] - (let [cmerger (merge-css datepicker-css-desc {:parts parts})] + (let [cmerger (merge-css datepicker-css-spec {:parts parts})] [:svg (flatten-attr (cmerger :next-year-icon)) [:g @@ -222,7 +222,7 @@ [& {:keys [display-month minimum disabled? parts]}] (let [prev-year-date-time (dec-year @display-month) prev-year-enabled? (if minimum (cljs-time/after? prev-year-date-time (dec-month minimum)) true) - cmerger (merge-css datepicker-css-desc {:parts parts})] + cmerger (merge-css datepicker-css-spec {:parts parts})] (when (not disabled?) [:<> (add-map-to-hiccup-call @@ -242,7 +242,7 @@ [& {:keys [display-month minimum disabled? parts]}] (let [prev-month-date-time (dec-month @display-month) prev-month-enabled? (if minimum (cljs-time/after? prev-month-date-time (dec-month minimum)) true) - cmerger (merge-css datepicker-css-desc {:parts parts})] + cmerger (merge-css datepicker-css-spec {:parts parts})] (when (not disabled?) [:<> (add-map-to-hiccup-call @@ -262,7 +262,7 @@ [& {:keys [display-month maximum disabled? parts]}] (let [next-month-date-time (inc-month @display-month) next-month-enabled? (if maximum (cljs-time/before? next-month-date-time maximum) true) - cmerger (merge-css datepicker-css-desc {:parts parts})] + cmerger (merge-css datepicker-css-spec {:parts parts})] (when (not disabled?) [:<> [line @@ -282,7 +282,7 @@ [& {:keys [display-month maximum disabled? parts]}] (let [next-year-date-time (inc-year @display-month) next-year-enabled? (if maximum (cljs-time/before? next-year-date-time maximum) true) - cmerger (merge-css datepicker-css-desc {:parts parts})] + cmerger (merge-css datepicker-css-spec {:parts parts})] (when (not disabled?) [:<> [line @@ -302,7 +302,7 @@ [& {:keys [display-month minimum maximum disabled? i18n parts]}] (let [minimum (deref-or-value minimum) maximum (deref-or-value maximum) - cmerger (merge-css datepicker-css-desc {:parts parts})] + cmerger (merge-css datepicker-css-spec {:parts parts})] [:th (flatten-attr (cmerger :nav)) [h-box @@ -354,7 +354,7 @@ "Answer 2 x rows showing month with nav buttons and days" [display-month {:keys [show-weeks? minimum maximum start-of-week i18n]} disabled? parts] (let [template-row (if show-weeks? [:tr [:th]] [:tr]) - cmerger (merge-css datepicker-css-desc {:parts parts})] + cmerger (merge-css datepicker-css-spec {:parts parts})] [:thead (flatten-attr (cmerger :header)) (conj template-row @@ -423,7 +423,7 @@ current-start (previous (is-day-pred start-of-week) display-month) focus-month (cljs-time/month display-month) row-start-dates (map #(inc-date current-start (* 7 %)) (range 6)) - cmerger (merge-css datepicker-css-desc {:parts parts})] + cmerger (merge-css datepicker-css-spec {:parts parts})] (into [:tbody (flatten-attr (cmerger :dates))] (map #(table-tr % start-of-week focus-month selected attributes disabled? on-change cmerger) row-start-dates)))) @@ -473,7 +473,7 @@ (-> (map :name datepicker-parts-desc) set))) -(def datepicker-css-desc +(def datepicker-css-spec {:main {:class ["datepicker" "noselect" "rc-datepicker"] :style {:font-size "13px" :position "static"}} @@ -570,7 +570,7 @@ disabled? (deref-or-value disabled?) props-with-defaults (merge args {:start-of-week start-of-week}) configuration (configure props-with-defaults) - cmerger (merge-css datepicker-css-desc args)] + cmerger (merge-css datepicker-css-spec args)] (when (not= @external-model latest-ext-model) ;; Has model changed externally? (reset! external-model latest-ext-model) (reset! internal-model latest-ext-model) diff --git a/src/re_com/dropdown.cljs b/src/re_com/dropdown.cljs index aadcc502..29f243ba 100644 --- a/src/re_com/dropdown.cljs +++ b/src/re_com/dropdown.cljs @@ -121,13 +121,13 @@ (< item-offset-top parent-visible-top) item-offset-top)] (when new-scroll-top (set! (.-scrollTop parent) new-scroll-top)))) -(declare single-dropdown-css-desc) +(declare single-dropdown-css-spec) (defn- make-group-heading "Render a group heading" [m] - (let [cmerger (merge-css single-dropdown-css-desc {})] + (let [cmerger (merge-css single-dropdown-css-spec {})] ^{:key (:id m)} [:li (cmerger :group-heading) (:group m)])) @@ -155,7 +155,7 @@ (fn [id label on-click internal-model] (let [selected (= @internal-model id) - cmerger (merge-css single-dropdown-css-desc {}) + cmerger (merge-css single-dropdown-css-spec {}) class (if selected "highlighted" (when @mouse-over? "mouseover"))] @@ -183,7 +183,7 @@ "Base function (before lifecycle metadata) to render a filter text box" [filter-box? filter-text key-handler drop-showing? set-filter-text filter-placeholder] - (let [cmerger (merge-css single-dropdown-css-desc {})] + (let [cmerger (merge-css single-dropdown-css-spec {})] [:div (flatten-attr (cmerger :filter-wrapper)) [:input @@ -214,7 +214,7 @@ "Render the top part of the dropdown, with the clickable area and the up/down arrow" [] (let [ignore-click (atom false) - cmerger (merge-css single-dropdown-css-desc {})] + cmerger (merge-css single-dropdown-css-spec {})] (fn [internal-model choices id-fn label-fn tab-index placeholder dropdown-click key-handler filter-box? drop-showing? title? disabled?] (let [_ (reagent/set-state (reagent/current-component) {:filter-box? filter-box?}) @@ -274,7 +274,7 @@ (defn- free-text-dropdown-top-base "Base function (before lifecycle metadata) to render the top part of the dropdown (free-text), with the editable area and the up/down arrow" [free-text-input select-free-text? free-text-focused? free-text-sel-range internal-model tab-index placeholder dropdown-click key-handler filter-box? drop-showing? cancel width free-text-change auto-complete? choices capitalize? disabled?] - (let [cmerger (merge-css single-dropdown-css-desc {})] + (let [cmerger (merge-css single-dropdown-css-spec {})] [:ul (flatten-attr (cmerger :choices)) [:li @@ -391,7 +391,7 @@ {:name :choices-error :level 4 :class "rc-dropdown-choices-error" :impl "[:li]"} {:name :choices-no-results :level 4 :class "rc-dropdown-choices-no-results" :impl "[:li]"}])) -(def single-dropdown-css-desc +(def single-dropdown-css-spec {:main {:class (fn [{:keys [free-text? drop-showing? free-text-focused?]}] ["rc-dropdown" "chosen-container" "noselect" (if free-text? "chosen-container-multi" "chosen-container-single") @@ -520,7 +520,7 @@ focus-free-text #(when @free-text-input (.focus @free-text-input)) node (reagent/atom nil) focus-anchor #(some-> @node (.getElementsByClassName "chosen-single") (.item 0) (.focus)) - cmerger (merge-css single-dropdown-css-desc args)] + cmerger (merge-css single-dropdown-css-spec args)] (load-choices "" regex-filter? false) (fn single-dropdown-render [& {:keys [choices model on-change id-fn label-fn group-fn render-fn disabled? filter-box? regex-filter? placeholder title? free-text? auto-complete? capitalize? enter-drop? cancelable? set-to-filter filter-placeholder can-drop-above? est-item-height repeat-change? i18n on-drop width max-height tab-index debounce-delay tooltip tooltip-position class style attr parts] diff --git a/src/re_com/multi_select.cljs b/src/re_com/multi_select.cljs index b289e161..72ca68e1 100644 --- a/src/re_com/multi_select.cljs +++ b/src/re_com/multi_select.cljs @@ -17,7 +17,7 @@ [re-com.validate :as validate :refer [string-or-hiccup? parts?]] [reagent.core :as reagent])) -(declare multi-select-css-desc) +(declare multi-select-css-spec) (defn items-with-group-headings "Split a list of maps by a group key then return both the group" @@ -67,7 +67,7 @@ (defn filter-text-box "Base function (before lifecycle metadata) to render a filter text box" [*filter-text placeholder *warning-message disabled? parts] - (let [cmerger (merge-css multi-select-css-desc {:parts parts})] + (let [cmerger (merge-css multi-select-css-spec {:parts parts})] (add-map-to-hiccup-call (cmerger :filter-text-box) [box/h-box @@ -97,7 +97,7 @@ "Render a group heading and set up appropriate mouse events" [] (let [*mouse-over? (reagent/atom false) - cmerger (merge-css multi-select-css-desc {})] + cmerger (merge-css multi-select-css-spec {})] (fn group-heading-render [& {:keys [heading disabled? click-callback double-click-callback selected-item-id]}] (let [id (:id heading) @@ -120,7 +120,7 @@ "Render a list item and set up appropriate mouse events" [] (let [*mouse-over? (reagent/atom false) - cmerger (merge-css multi-select-css-desc {})] + cmerger (merge-css multi-select-css-spec {})] (fn list-item-render [& {:keys [item id-fn label-fn disabled? click-callback double-click-callback selected-item-id group-selected?]}] (let [id (id-fn item) @@ -167,7 +167,7 @@ :selected-item-id @*current-item-id]) make-heading-then-items (fn [heading items] (cons (make-group-heading-item heading) (make-items items))) - cmerger (merge-css multi-select-css-desc args)] + cmerger (merge-css multi-select-css-spec args)] (add-map-to-hiccup-call (cmerger :list-box {:disabled? disabled?}) [box/box @@ -187,7 +187,7 @@ (defn multi-button [& {:keys [disabled? label icon on-click class style attr] :as args}] - (let [cmerger (merge-css multi-select-css-desc {})] + (let [cmerger (merge-css multi-select-css-spec {})] (add-map-to-hiccup-call (cmerger :button) [buttons/button @@ -242,7 +242,7 @@ {:name :filter-reset-button :level 4 :class "rc-multi-select-filter-reset-button" :impl "[close-button]"} {:name :right-filter-result-count :level 3 :class "rc-multi-select-right-filter-result-count" :impl "[label]"}])) -(def multi-select-css-desc +(def multi-select-css-spec {:main {:class ["rc-multi-select" "noselect" "chosen-container" "chosen-container-single"] :style (fn [{:keys [width]}] (merge (box/flex-child-style (if width "0 0 auto" "auto")) @@ -515,7 +515,7 @@ (reset! *external-model @*internal-model) (on-change @*internal-model)) (reset! *current-selection-id nil)) - cmerger (merge-css multi-select-css-desc args)] + cmerger (merge-css multi-select-css-spec args)] [:div (merge (flatten-attr (cmerger :main {:width width})) @@ -628,7 +628,7 @@ (->> filtered-selections ;; TODO: Inefficient (filter (fn [item] (= (first @*current-selection-id) (group-fn item)))) count))) - ;;TODO: Zmdi reference should be in -css-desc + ;;TODO: Zmdi reference should be in -css-spec :icon "play zmdi-hc-rotate-180" :disabled? (or disabled? (not excludable?)) :on-click exclude-click]) diff --git a/src/re_com/popover.cljs b/src/re_com/popover.cljs index ace7cdac..2207bb06 100644 --- a/src/re_com/popover.cljs +++ b/src/re_com/popover.cljs @@ -86,7 +86,7 @@ [(/ (+ (.-right bounding-rect) (.-left bounding-rect)) 2) ;; x (/ (+ (.-bottom bounding-rect) (.-top bounding-rect)) 2)])) ;; y -(def popover-arrow-css-desc +(def popover-arrow-css-spec {:arrow {:class ["popover-arrow" "rc-popover-arrow"] :style (fn [{:keys [orientation pop-offset arrow-length arrow-width]}] {:position "absolute" @@ -127,7 +127,7 @@ :right (str (point arrow-length 0) (point 0 half-arrow-width) (point arrow-length arrow-width)) :above (str (point 0 0) (point half-arrow-width arrow-length) (point arrow-width 0)) :below (str (point 0 arrow-length) (point half-arrow-width 0) (point arrow-width arrow-length))} - cmerger (merge-css popover-arrow-css-desc {:parts parts})] + cmerger (merge-css popover-arrow-css-spec {:parts parts})] [:svg (cmerger :arrow {:orientation orientation :pop-offset pop-offset :arrow-length arrow-length :arrow-width arrow-width}) [:polyline (merge {:points (arrow-shape orientation)} @@ -151,7 +151,7 @@ {:name :src :required false :type "map" :validate-fn map? :description [:span "Used in dev builds to assist with debugging. Source code coordinates map containing keys" [:code ":file"] "and" [:code ":line"] ". See 'Debugging'."]} {:name :debug-as :required false :type "map" :validate-fn map? :description [:span "Used in dev builds to assist with debugging, when one component is used implement another component, and we want the implementation component to masquerade as the original component in debug output, such as component stacks. A map optionally containing keys" [:code ":component"] "and" [:code ":args"] "."]}])) -(def backdrop-css-desc +(def backdrop-css-spec {:main {:class ["noselect" "rc-backdrop"] :style (fn [{:keys [opacity]}] {:position "fixed" @@ -167,7 +167,7 @@ [& {:keys [opacity on-click class style attr] :as args}] (or (validate-args-macro backdrop-args-desc args) - (let [cmerger (merge-css backdrop-css-desc args)] + (let [cmerger (merge-css backdrop-css-spec args)] [:div (merge (flatten-attr @@ -187,7 +187,7 @@ {:name :container :level 1 :class "" :impl "[h-box]" :notes [:span "Container for the " [:code ":title"] " and the close button."]} {:name :close-button :level 2 :class "" :impl "[close-button]" :notes "The close button."}])) -(def popover-title-css-desc +(def popover-title-css-spec {:main {:class ["popover-title" "rc-popover-title"] :style (merge (flex-child-style "inherit") {:font-size "18px"})} @@ -219,7 +219,7 @@ (validate-args-macro popover-title-args-desc args) #_(assert (or ((complement nil?) showing?) ((complement nil?) close-callback)) "Must specify either showing? OR close-callback") ;; IJ: TODO re-refactor (let [close-button? (if (nil? close-button?) true close-button?) - cmerger (merge-css popover-title-css-desc args)] + cmerger (merge-css popover-title-css-spec args)] [:h3 (merge (flatten-attr (cmerger :main)) @@ -286,7 +286,7 @@ {:name :arrow :level 1 :class "rc-popover-arrow" :impl "[:svg]" :notes ""} {:name :content :level 2 :class "rc-popover-content" :impl "[:div]" :notes ""}])) -(def popover-border-css-desc +(def popover-border-css-spec {:main {:class ["popover" "fade" "in" "rc-popover-border"] :style (fn [{:keys [top left width height background-color border-color tooltip-style? orientation margin-left margin-top ready-to-show?] :as params}] @@ -395,7 +395,7 @@ (or (validate-args-macro popover-border-args-desc args) (let [[orientation grey-arrow?] (calc-metrics @position) - cmerger (merge-css popover-border-css-desc args)] + cmerger (merge-css popover-border-css-spec args)] [:div (merge {:id pop-id} @@ -431,7 +431,7 @@ {:name :border :level 2 :class "" :impl "[popover-border]" :notes ""} {:name :title :level 3 :class "" :impl "[popover-title]" :notes ""}])) -(def popover-content-wrapper-css-desc +(def popover-content-wrapper-css-spec {:main {:class ["popover-content-wrapper" "rc-popover-content-wrapper"] :style (fn [{:keys [no-clip? left-offset top-offset]}] (merge (flex-child-style "inherit") @@ -509,7 +509,7 @@ :as args}] (or (validate-args-macro popover-content-wrapper-args-desc args) - (let [cmerger (merge-css popover-content-wrapper-css-desc args)] + (let [cmerger (merge-css popover-content-wrapper-css-spec args)] @position-injected ;; Dereference this atom. Although nothing here needs its value explicitly, the calculation of left-offset and top-offset are affected by it for :no-clip? true [:div (merge (flatten-attr @@ -561,7 +561,7 @@ {:name :point-wrapper :level 1 :class "rc-point-wrapper" :impl "[:div]" :notes "Wraps the anchor component and the popover-point (which the actual popover points to)."} {:name :point :level 2 :class "rc-popover-point" :impl "[:div]" :notes [:span "The point (width/height 0) which is placed at the center of the relevant side of the anchor, based on " [:code ":position"] "tag"]}])) -(def popover-anchor-wrapper-css-desc +(def popover-anchor-wrapper-css-spec {:main {:class ["rc-popover-anchor-wrapper" "display-inline-flex"] :style (flex-child-style "inherit")} :point-wrapper {:class ["display-inline-flex" "rc-point-wrapper"] @@ -616,7 +616,7 @@ (let [[orientation _arrow-pos] (split-keyword @internal-position "-") ;; only need orientation here place-anchor-before? (case orientation (:left :above) false true) flex-flow (case orientation (:left :right) "row" "column") - cmerger (merge-css popover-anchor-wrapper-css-desc args)] + cmerger (merge-css popover-anchor-wrapper-css-spec args)] [:div (merge (flatten-attr (cmerger :main)) (->attr args) @@ -644,7 +644,7 @@ {:name :close-button-container :level 3 :class "rc-popover-tooltip-close-button-container" :impl "[box]" :notes ""} {:name :close-button :level 4 :class "rc-popover-tooltip-close-button" :impl "[close-button]" :notes ""}])) -(def popover-tooltip-css-desc +(def popover-tooltip-css-spec {:main {:class ["rc-popover-tooltip"]} :content-wrapper {} :v-box {:style (fn [{:keys [status]}] @@ -690,7 +690,7 @@ (or (validate-args-macro popover-tooltip-args-desc args) (let [label (deref-or-value label) - cmerger (merge-css popover-tooltip-css-desc args)] + cmerger (merge-css popover-tooltip-css-spec args)] (add-map-to-hiccup-call (cmerger :main) [popover-anchor-wrapper diff --git a/src/re_com/simple_v_table.cljs b/src/re_com/simple_v_table.cljs index d7514e2c..ee5c4b4d 100644 --- a/src/re_com/simple_v_table.cljs +++ b/src/re_com/simple_v_table.cljs @@ -63,7 +63,7 @@ :sub :end :text-top :center}) -(declare simple-v-table-css-desc) +(declare simple-v-table-css-spec) (defn column-header-item [{:keys [id row-label-fn width height align vertical-align header-label sort-by] :as column} parts sort-by-column] @@ -71,7 +71,7 @@ {current-key-fn :key-fn order :order} @sort-by-column on-click #(swap! sort-by-column swap!-sort-by-column key-fn comp) align (get vertical-align->align (keyword vertical-align) vertical-align) - cmerger (merge-css simple-v-table-css-desc {:parts parts})] + cmerger (merge-css simple-v-table-css-spec {:parts parts})] (cond-> (add-map-to-hiccup-call (cmerger :simple-column-header-item {:height height :align align :sort-by sort-by @@ -93,7 +93,7 @@ (defn column-header-renderer ":column-header-renderer AND :top-left-renderer - Render the table header" [columns parts sort-by-column] - (let [cmerger (merge-css simple-v-table-css-desc {:parts parts})] + (let [cmerger (merge-css simple-v-table-css-spec {:parts parts})] (add-map-to-hiccup-call (cmerger :simple-column-header {:attr {:on-click (handler-fn (v-table/show-row-data-on-alt-click columns 0 event))}}) @@ -105,7 +105,7 @@ (defn row-item "Render a single row item (column) of a single row" [row {:keys [width height align vertical-align row-label-fn] :as column} cell-style parts] - (let [cmerger (merge-css simple-v-table-css-desc {:parts parts})] + (let [cmerger (merge-css simple-v-table-css-spec {:parts parts})] [:div (cmerger :simple-row-item {:width width :height height @@ -120,7 +120,7 @@ (defn row-renderer ":row-renderer AND :row-header-renderer: Render a single row of the table data" [columns on-click-row on-enter-row on-leave-row striped? row-height row-style cell-style parts table-row-line-color row-index row] - (let [cmerger (merge-css simple-v-table-css-desc {:parts parts})] + (let [cmerger (merge-css simple-v-table-css-spec {:parts parts})] (into [:div (cmerger :simple-row @@ -158,7 +158,7 @@ simple-v-table-exclusive-parts-desc (map #(update % :level inc) v-table/v-table-parts-desc)))) -(def simple-v-table-css-desc +(def simple-v-table-css-spec {:simple-wrapper {:class ["rc-simple-v-table-wrapper"] :style (fn [{:keys [max-rows padding max-width table-row-line-color]}] {;; :flex setting @@ -296,7 +296,7 @@ (vec (reverse sorted)) (vec sorted)))) (deref-or-value model)))) - cmerger (merge-css simple-v-table-css-desc args)] + cmerger (merge-css simple-v-table-css-spec args)] (add-map-to-hiccup-call (cmerger :simple-wrapper {:max-rows max-rows :padding (px table-padding) diff --git a/src/re_com/splits.cljs b/src/re_com/splits.cljs index b77788fd..ef7f5291 100644 --- a/src/re_com/splits.cljs +++ b/src/re_com/splits.cljs @@ -10,7 +10,7 @@ [reagent.core :as reagent])) -(declare hv-split-css-desc) +(declare hv-split-css-spec) (defn drag-handle "Return a drag handle to go into a vertical or horizontal splitter bar: @@ -18,7 +18,7 @@ over?: When true, the mouse is assumed to be over the splitter so show a bolder color" [orientation over? parts] (let [vertical? (= orientation :vertical) - cmerger (merge-css hv-split-css-desc {:parts parts})] + cmerger (merge-css hv-split-css-spec {:parts parts})] [:div (flatten-attr (cmerger :handle {:vertical? vertical?})) [:div @@ -54,7 +54,7 @@ {:name :handle-bar-2 :level 3 :class "rc-v-split-handle-bar-2" :impl "[:div]" :notes "The splitter handle's second bar."} {:name :bottom :level 1 :class "rc-v-split-bottom" :impl "[:div]" :notes "Second (i.e. bottom) panel of the split."}])) -(def hv-split-css-desc +(def hv-split-css-spec {:main {:class (fn [{:keys [vertical?]}] [(if vertical? "rc-v-split" "rc-h-split") "display-flex"]) :style (fn [{:keys [size margin width height vertical?]}] @@ -179,7 +179,7 @@ (fn h-split-render [& {:keys [panel-1 panel-2 _size _width _height _on-split-change _initial-split _splitter-size _margin class style attr parts src]}] - (let [cmerger (merge-css hv-split-css-desc args)] + (let [cmerger (merge-css hv-split-css-spec args)] [:div (flatten-attr (cmerger :main {:vertical? false :size size :margin margin :width width :height height @@ -260,7 +260,7 @@ (fn v-split-render [& {:keys [panel-1 panel-2 _size _width _height _on-split-change _initial-split _splitter-size _margin class style attr parts src]}] - (let [cmerger (merge-css hv-split-css-desc args)] + (let [cmerger (merge-css hv-split-css-spec args)] [:div (flatten-attr (cmerger :main {:vertical? true :size size :margin margin :width width :height height diff --git a/src/re_com/tag_dropdown.cljs b/src/re_com/tag_dropdown.cljs index 0992d0f9..959b6d25 100644 --- a/src/re_com/tag_dropdown.cljs +++ b/src/re_com/tag_dropdown.cljs @@ -33,7 +33,7 @@ base-style)] base-style))) -(declare tag-dropdown-css-desc) +(declare tag-dropdown-css-spec) (defn text-tag [] @@ -50,7 +50,7 @@ placeholder? (= (:id tag-data) :$placeholder$) tag-label (label-fn tag-data) tag-description (when description-fn (description-fn tag-data)) - cmerger (merge-css tag-dropdown-css-desc args)] + cmerger (merge-css tag-dropdown-css-spec args)] (add-map-to-hiccup-call (cmerger :tag {:class (get-in parts [tag-id-kw :class]) :style (get-in parts [tag-id-kw :style]) @@ -136,7 +136,7 @@ (remove #(= :legacy (:type %))) (map #(update % :level (comp inc inc))))))) -(def tag-dropdown-css-desc +(def tag-dropdown-css-spec {:main {:class ["rc-tag-dropdown"] :style (fn [{:keys [disabled?]}] {:background-color (if disabled? "#EEE" "white") @@ -225,7 +225,7 @@ abbrev? (and (>= choices-num-chars abbrev-threshold) (number? abbrev-threshold) (fn? abbrev-fn)) - cmerger (merge-css tag-dropdown-css-desc args) + cmerger (merge-css tag-dropdown-css-spec args) placeholder-tag [text-tag :tag-data {:id :$placeholder$ diff --git a/src/re_com/v_table.cljs b/src/re_com/v_table.cljs index c2572352..70486b3a 100644 --- a/src/re_com/v_table.cljs +++ b/src/re_com/v_table.cljs @@ -20,7 +20,7 @@ (def px (memoize util/px)) -(declare v-table-css-desc) +(declare v-table-css-spec) (defn show-row-data-on-alt-click "Make a call to this function in the click event of your row renderer, then every time they Alt+Click on a row, @@ -31,7 +31,7 @@ (when (.-altKey event) (js/console.log (str "ROW-INDEX[" row-index "]") row))) -(def scrollbar-css-desc +(def scrollbar-css-spec {:main {:class (fn [{:keys [horizontal?]}] [(str (if horizontal? "horizontal" "vertical") "-scrollbar")]) :style (fn [{:keys [width horizontal? show? mouse-over? dragging?]}] @@ -133,7 +133,7 @@ max-scroll-pos (- length thumb-length) scrollbar-content-ratio (/ (- content-length length) max-scroll-pos) internal-scroll-pos (/ scroll-pos scrollbar-content-ratio) - cmerger (merge-css scrollbar-css-desc args)] + cmerger (merge-css scrollbar-css-spec args)] (reset! calcs {:length length :scroll-pos scroll-pos :thumb-ratio thumb-ratio @@ -180,7 +180,7 @@ (defn top-left-content "Render section 1 - the content component" [top-left-renderer column-header-height parts] - (let [cmerger (merge-css v-table-css-desc {:parts parts})] + (let [cmerger (merge-css v-table-css-spec {:parts parts})] (add-map-to-hiccup-call (cmerger :top-left) [box/box ;; content component @@ -205,7 +205,7 @@ - scroll-y current horizontal scrollbar position in px " [row-header-renderer key-fn top-row-index rows scroll-y parts] - (let [cmerger (merge-css v-table-css-desc {:parts parts})] + (let [cmerger (merge-css v-table-css-spec {:parts parts})] (add-map-to-hiccup-call (cmerger :row-header-content {:scroll-y scroll-y}) [box/v-box @@ -223,7 +223,7 @@ row-header-selection-fn [selection-renderer on-mouse-down on-mouse-enter on-mouse-leave] selection-allowed? row-viewport-height content-rows-height parts] - (let [cmerger (merge-css v-table-css-desc {:parts parts})] + (let [cmerger (merge-css v-table-css-spec {:parts parts})] (add-map-to-hiccup-call (cmerger :row-headers @@ -250,7 +250,7 @@ (defn bottom-left-content "Render section 3 - the content component" [bottom-left-renderer column-footer-height parts] - (let [cmerger (merge-css v-table-css-desc {:parts parts})] + (let [cmerger (merge-css v-table-css-spec {:parts parts})] (add-map-to-hiccup-call (cmerger :bottom-left) [box/box ;; content component @@ -271,7 +271,7 @@ - scroll-x current horizontal scrollbar position in px " [column-header-renderer scroll-x parts] - (let [cmerger (merge-css v-table-css-desc {:parts parts})] + (let [cmerger (merge-css v-table-css-spec {:parts parts})] (add-map-to-hiccup-call (cmerger :column-header-content {:scroll-x scroll-x}) [box/box @@ -285,7 +285,7 @@ column-header-selection-fn [selection-renderer on-mouse-down on-mouse-enter on-mouse-leave] selection-allowed? row-viewport-width column-header-height content-rows-width parts] - (let [cmerger (merge-css v-table-css-desc {:parts parts})] + (let [cmerger (merge-css v-table-css-spec {:parts parts})] (add-map-to-hiccup-call (cmerger :column-headers {:attr (when column-header-selection-fn @@ -321,7 +321,7 @@ - scroll-y current horizontal scrollbar position in px " [row-renderer key-fn top-row-index rows scroll-x scroll-y parts] - (let [cmerger (merge-css v-table-css-desc {:parts parts})] + (let [cmerger (merge-css v-table-css-spec {:parts parts})] (add-map-to-hiccup-call (cmerger :row-content {:scroll-x scroll-x :scroll-y scroll-y}) [box/v-box @@ -339,7 +339,7 @@ row-selection-fn [selection-renderer on-mouse-down on-mouse-enter on-mouse-leave] selection-allowed? row-viewport-height row-viewport-width row-viewport-id content-rows-height content-rows-width parts] - (let [cmerger (merge-css v-table-css-desc {:parts parts})] + (let [cmerger (merge-css v-table-css-spec {:parts parts})] (add-map-to-hiccup-call (cmerger :rows {:max-height content-rows-height :attr @@ -373,7 +373,7 @@ - scroll-x current horizontal scrollbar position in px " [column-footer-renderer scroll-x parts] - (let [cmerger (merge-css v-table-css-desc {:parts parts})] + (let [cmerger (merge-css v-table-css-spec {:parts parts})] (add-map-to-hiccup-call (cmerger :column-footer-content {:scroll-x scroll-x}) [box/box @@ -387,7 +387,7 @@ parts class style attr content-class content-style content-attr] - (let [cmerger (merge-css v-table-css-desc {:parts parts})] + (let [cmerger (merge-css v-table-css-spec {:parts parts})] (add-map-to-hiccup-call (cmerger :column-footers) [box/box ;; viewport component @@ -404,7 +404,7 @@ (defn top-right-content "Render section 7 - the content component" [top-right-renderer column-header-height parts] - (let [cmerger (merge-css v-table-css-desc {:parts parts})] + (let [cmerger (merge-css v-table-css-spec {:parts parts})] (add-map-to-hiccup-call (cmerger :top-right) [box/box ;; content component @@ -444,7 +444,7 @@ [row-footer-renderer key-fn top-row-index rows scroll-y row-viewport-height content-rows-height parts] - (let [cmerger (merge-css v-table-css-desc {:parts parts})] + (let [cmerger (merge-css v-table-css-spec {:parts parts})] (add-map-to-hiccup-call (cmerger :row-footers {:max-height content-rows-height}) [box/box ;; viewport component @@ -461,7 +461,7 @@ (defn bottom-right-content "Render section 9 - the content component" [bottom-right-renderer column-footer-height parts] - (let [cmerger (merge-css v-table-css-desc {:parts parts})] + (let [cmerger (merge-css v-table-css-spec {:parts parts})] (add-map-to-hiccup-call (cmerger :bottom-right) [box/box ;; content component @@ -500,7 +500,7 @@ {:type :legacy :level 2 :name-label "-" :impl "[box]" :notes "Legacy"} {:name :v-scroll :level 3 :class "rc-v-table-v-scroll" :impl "[box]" :notes "The vertical scrollbar"}])) -(def v-table-css-desc +(def v-table-css-spec {:wrapper {:class ["rc-v-table"] :style (fn [{:keys [max-width max-height]}] {:max-width max-width :max-height max-height}) @@ -571,9 +571,9 @@ }) ;;This is for the selection-renderer component embedded in v-table. Perhaps it should be a key in -;; v-table-css-desc, above. But it doesn't seem to be addressable using `parts` so for now just has +;; v-table-css-spec, above. But it doesn't seem to be addressable using `parts` so for now just has ;; its defaults defined in the separate structure below. -(def v-table-selection-css-desc +(def v-table-selection-css-spec {:main {:class ["rc-v-table-selection"] :style (fn [{:keys [top left width height]}] {:top (px top) :left (px left) :width (px width) :height (px height) @@ -989,7 +989,7 @@ (- @sel-content-x-start @scroll-x) (- @sel-content-x-start @scroll-x width)) cmerger (merge-css - v-table-selection-css-desc + v-table-selection-css-spec {:class class :style style :attr attr})] [:div (flatten-attr (cmerger :main {:width width :height height @@ -1201,7 +1201,7 @@ ;; TODO: [DJ] Suggested that the many merges below could be placed in the let above as reaction for performance improvements (readability would suffer a bit) - (let [cmerger (merge-css v-table-css-desc args)] + (let [cmerger (merge-css v-table-css-spec args)] (add-map-to-hiccup-call (cmerger :wrapper {:max-width max-width ;; Can't do equivalent of :max-height because we don't know column-header-width or column-footer-width :max-height From 7cbed4d47ddbdb3f84cef9196510c294a1946f17 Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Mon, 28 Nov 2022 09:18:14 -0800 Subject: [PATCH 30/65] Css split out --- src/re_com/typeahead.cljs | 121 +++++++++++++++++++++----------------- 1 file changed, 67 insertions(+), 54 deletions(-) diff --git a/src/re_com/typeahead.cljs b/src/re_com/typeahead.cljs index 5bd9023a..b87ab9f3 100644 --- a/src/re_com/typeahead.cljs +++ b/src/re_com/typeahead.cljs @@ -9,7 +9,7 @@ [re-com.debug :refer [->attr]] [re-com.throbber :refer [throbber]] [re-com.input-text :refer [input-text]] - [re-com.util :refer [deref-or-value px]] + [re-com.util :refer [deref-or-value px add-map-to-hiccup-call merge-css]] [re-com.popover :refer [popover-tooltip]] ;; need? [re-com.box :refer [h-box v-box box gap line flex-child-style align-style]] ;; need? [re-com.validate :refer [input-status-type? input-status-types-list regex? string-or-hiccup? css-style? html-attr? parts? number-or-string? @@ -259,6 +259,16 @@ {:name :src :required false :type "map" :validate-fn map? :description [:span "Used in dev builds to assist with debugging. Source code coordinates map containing keys" [:code ":file"] "and" [:code ":line"] ". See 'Debugging'."]} {:name :debug-as :required false :type "map" :validate-fn map? :description [:span "Used in dev builds to assist with debugging, when one component is used implement another component, and we want the implementation component to masquerade as the original component in debug output, such as component stacks. A map optionally containing keys" [:code ":component"] "and" [:code ":args"] "."]}])) +(def typeahead-css-spec + {:main {:class ["rc-typeahead"]} + :wrapper {} + :suggestions-container-wrapper {:style {:position "relative"}} + :suggestions-container {} + :throbber {} + :suggestion {:class (fn [{:keys [selected?]}] + ["rc-typeahead-suggestion" (when selected? "active")])}}) + + (defn typeahead "typeahead reagent component" [& {:keys [] :as args}] @@ -278,63 +288,66 @@ (let [{:as state :keys [suggestions waiting? suggestion-active-index external-model]} @state-atom last-data-source (:data-source state) latest-external-model (deref-or-value model) - width (or width "250px")] + width (or width "250px") + cmerger (merge-css typeahead-css-spec args)] (when (not= last-data-source data-source) (swap! state-atom change-data-source data-source)) (when (not= latest-external-model external-model) (swap! state-atom external-model-changed latest-external-model)) - [v-box - :src src - :debug-as (or debug-as (reflect-current-component)) - :class "rc-typeahead" - :attr attr - :width width - :children [[input-text - :src (at) - :model input-text-model - :class class - :style style - :disabled? disabled? - :status-icon? status-icon? - :status status - :status-tooltip status-tooltip - :width width - :height height - :placeholder placeholder - :on-change (partial input-text-on-change! state-atom) - :change-on-blur? false - :attr {:on-key-down (partial input-text-on-key-down! state-atom) - :on-focus #() - ;; on-blur should behave the same as tabbing off - :on-blur #(swap! state-atom input-text-will-blur)}] - (if (or (not-empty suggestions) waiting?) - [box - :src (at) - :style {:position "relative"} - :child [v-box - :src (at) - :class (str "rc-typeahead-suggestions-container " (get-in parts [:suggestions-container :class])) - :children [(when waiting? - [box - :src (at) - :align :center - :child [throbber - :src (at) - :size :small - :class (str "rc-typeahead-throbber " (get-in parts [:throbber :class]))]]) - (for [[i s] (map vector (range) suggestions) - :let [selected? (= suggestion-active-index i)]] - ^{:key i} - [box - :src (at) - :child (if render-suggestion - (render-suggestion s) - s) - :class (str "rc-typeahead-suggestion" - (when selected? " active") - (get-in parts [:suggestion :class])) - :attr {:on-mouse-over #(swap! state-atom activate-suggestion-by-index i) - :on-mouse-down #(do (.preventDefault %) (swap! state-atom choose-suggestion-by-index i))}])]]])]])))))) + (add-map-to-hiccup-call + (cmerger :wrapper) + [v-box + :src src + :debug-as (or debug-as (reflect-current-component)) + :width width + :children [(add-map-to-hiccup-call + (cmerger :main + {:attr {:on-key-down (partial input-text-on-key-down! state-atom) + :on-focus #() + ;; on-blur should behave the same as tabbing off + :on-blur #(swap! state-atom input-text-will-blur)}}) + [input-text + :src (at) + :model input-text-model + :disabled? disabled? + :status-icon? status-icon? + :status status + :status-tooltip status-tooltip + :width width + :height height + :placeholder placeholder + :on-change (partial input-text-on-change! state-atom) + :change-on-blur? false]) + (if (or (not-empty suggestions) waiting?) + (add-map-to-hiccup-call + (cmerger :suggestions-container-wrapper) + [box + :src (at) + :child (add-map-to-hiccup-call + (cmerger :suggestions-container) + [v-box + :src (at) + :children [(when waiting? + [box + :src (at) + :align :center + :child (add-map-to-hiccup-call + (cmerger :throbber) + [throbber + :src (at) + :size :small])]) + (for [[i s] (map vector (range) suggestions) + :let [selected? (= suggestion-active-index i)]] + (add-map-to-hiccup-call + (cmerger :suggestion {:selected? selected? + :attr {:on-mouse-over #(swap! state-atom activate-suggestion-by-index i) + :on-mouse-down #(do (.preventDefault %) (swap! state-atom choose-suggestion-by-index i))}}) + ^{:key i} + [box + :src (at) + :child (if render-suggestion + (render-suggestion s) + s)]))]])]))]]))))))) (defn- debounce "Return a channel which will receive a value from the `in` channel only From b612760b4c08cad3ab99218dd6824f287070e54a Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Mon, 28 Nov 2022 09:18:32 -0800 Subject: [PATCH 31/65] Sometimes we need metadata --- src/re_com/util.cljs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/re_com/util.cljs b/src/re_com/util.cljs index fdb362cf..65264254 100644 --- a/src/re_com/util.cljs +++ b/src/re_com/util.cljs @@ -265,12 +265,14 @@ (merge (dissoc stuff :attr) (:attr stuff))) (defn add-map-to-hiccup-call [map hiccup] - (reduce into [[(first hiccup)] - (for [[k v] map - :let [v (if (= k :class) (string/join " " v) v)] - itm [k v]] - itm) - (rest hiccup)])) + (with-meta + (reduce into [[(first hiccup)] + (for [[k v] map + :let [v (if (= k :class) (string/join " " v) v)] + itm [k v]] + itm) + (rest hiccup)]) + (meta hiccup))) (defn say-hiccup [itm] (println "SOURCE:") From 11c376cfe498a8340247940f0cb814b14ac26b12 Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Tue, 29 Nov 2022 07:51:06 -0800 Subject: [PATCH 32/65] Css split out --- src/re_com/input_text.cljs | 216 +++++++++++++++++++++---------------- 1 file changed, 123 insertions(+), 93 deletions(-) diff --git a/src/re_com/input_text.cljs b/src/re_com/input_text.cljs index 3edbd98b..654249ef 100644 --- a/src/re_com/input_text.cljs +++ b/src/re_com/input_text.cljs @@ -5,7 +5,7 @@ (:require [re-com.config :refer [include-args-desc?]] [re-com.debug :refer [->attr]] - [re-com.util :refer [deref-or-value px]] + [re-com.util :refer [deref-or-value px add-map-to-hiccup-call merge-css flatten-attr]] [re-com.popover :refer [popover-tooltip]] [re-com.throbber :refer [throbber]] [re-com.box :refer [h-box v-box box gap line flex-child-style align-style]] @@ -27,6 +27,57 @@ (when include-args-desc? (-> (map :name input-text-parts-desc) set))) +(def input-text-css-spec + {:main {:class ["form-control" "rc-input-text-field"] + :style (fn [{:keys [height]}] + (merge + (flex-child-style "none") + {:height height + :padding-right "12px"}))} + :inner {:class (fn [{:keys [status status-icon?]}] + ["rc-input-text-inner" + (case status + :success "has-success" + :warning "has-warning" + :error "has-error" + nil) + (when (and status status-icon?) "has-feedback")]) + :style (flex-child-style "auto")} + :wrapper {:class ["rc-input-text"]} + :popover {:style (merge (flex-child-style "none") + (align-style :align-self :center) + {:font-size "130%" + :margin-left "4px"})} + :throbber {:class ["smaller"]} + :tooltip-icon {:class (fn [{:keys [status]}] + ["zmdi" "zmdi-hc-fw" + (case + status + :success "zmdi-check-circle" + :warning "zmdi-alert-triangle" + :error "zmdi-alert-circle zmdi-spinner" + :validating "zmdi-hc-spin zmdi-rotate-right zmdi-spinner" + nil) + "form-control-feedback"]) + :style {:position "static" + :height "auto" + :opacity "1"}} + :tooltip-icon2 {:class (fn [{:keys [status]}] + ["zmdi" "zmdi-hc-fw" + (case + status + :success "zmdi-check-circle" + :warning "zmdi-alert-triangle" + :error "zmdi-alert-circle zmdi-spinner" + :validating "zmdi-hc-spin zmdi-rotate-right zmdi-spinner" + nil) + "form-control-feedback"]) + :style {:position "static" + :font-size "130%" + :margin-left "4px" + :height "auto" + :opacity "1"}}}) + (def input-text-args-desc (when include-args-desc? [{:name :model :required true :type "string/nil | r/atom" :validate-fn nillable-string-or-atom? :description "text of the input (can be atom or value/nil)"} @@ -93,112 +144,91 @@ (on-change @internal-model reset-fn) (do (on-change @internal-model) - (reset-fn))))))] + (reset-fn)))))) + cmerger (merge-css input-text-css-spec args)] (when (not= @external-model latest-ext-model) ;; Has model changed externally? (reset! external-model latest-ext-model) (reset! internal-model latest-ext-model)) - [h-box - :src src - :debug-as (or debug-as (reflect-current-component)) - :align :start - :class (str "rc-input-text " (get-in parts [:wrapper :class])) - :style (get-in parts [:wrapper :style]) - :attr (get-in parts [:wrapper :attr]) - :width (if width width "250px") - :children [[:div - (merge - {:class (str "rc-input-text-inner " ;; form-group - (case status - :success "has-success " - :warning "has-warning " - :error "has-error " - "") - (when (and status status-icon?) "has-feedback ") - (get-in parts [:inner :class])) - :style (merge (flex-child-style "auto") - (get-in parts [:inner :style]))} - (get-in parts [:inner :attr])) - [(if (= input-type :password) :input input-type) - (merge - {:class (str "form-control rc-input-text-field " class) - :type (case input-type + (add-map-to-hiccup-call + (cmerger :wrapper) + [h-box + :src src + :debug-as (or debug-as (reflect-current-component)) + :align :start + :width (if width width "250px") + :children [[:div + (flatten-attr + (cmerger :inner {:status status :status-icon? status-icon?})) + [(if (= input-type :password) :input input-type) + (merge + (flatten-attr + (cmerger :main {:height height})) + {:type (case input-type :input "text" :password "password" nil) :rows (when (= input-type :textarea) (or rows 3)) - :style (merge - (flex-child-style "none") - {:height height - :padding-right "12px"} ;; override for when icon exists - style) :placeholder placeholder :value @internal-model :disabled disabled? :on-change (handler-fn - (let [new-val-orig (-> event .-target .-value) - new-val (on-alter new-val-orig)] - (when (not= new-val new-val-orig) - (set! (-> event .-target .-value) new-val)) - (when (and - on-change - (not disabled?) - (if validation-regex (re-find validation-regex new-val) true)) - (reset! internal-model new-val) - (when-not change-on-blur? - (on-change-handler))))) + (let [new-val-orig (-> event .-target .-value) + new-val (on-alter new-val-orig)] + (when (not= new-val new-val-orig) + (set! (-> event .-target .-value) new-val)) + (when (and + on-change + (not disabled?) + (if validation-regex (re-find validation-regex new-val) true)) + (reset! internal-model new-val) + (when-not change-on-blur? + (on-change-handler))))) :on-blur (handler-fn - (when (and - change-on-blur? - (not= @internal-model @external-model)) - (on-change-handler))) + (when (and + change-on-blur? + (not= @internal-model @external-model)) + (on-change-handler))) :on-key-up (handler-fn - (if disabled? - (.preventDefault event) - (case (.-which event) - 13 (on-change-handler) - 27 (reset! internal-model @external-model) - true)))} + (if disabled? + (.preventDefault event) + (case (.-which event) + 13 (on-change-handler) + 27 (reset! internal-model @external-model) + true)))} attr)]] - (when (and status-icon? status) - (let [icon-class (case status :success "zmdi-check-circle" :warning "zmdi-alert-triangle" :error "zmdi-alert-circle zmdi-spinner" :validating "zmdi-hc-spin zmdi-rotate-right zmdi-spinner")] - (if status-tooltip - [popover-tooltip - :src (at) - :label status-tooltip - :position :right-center - :status status - ;:width "200px" - :showing? showing? - :anchor (if (= :validating status) - [throbber - :size :regular - :class "smaller" - :attr {:on-mouse-over (handler-fn (when (and status-icon? status) (reset! showing? true))) - :on-mouse-out (handler-fn (reset! showing? false))}] - [:i {:class (str "zmdi zmdi-hc-fw " icon-class " form-control-feedback") - :style {:position "static" - :height "auto" - :opacity (if (and status-icon? status) "1" "0")} - :on-mouse-over (handler-fn (when (and status-icon? status) (reset! showing? true))) - :on-mouse-out (handler-fn (reset! showing? false))}]) - :style (merge (flex-child-style "none") - (align-style :align-self :center) - {:font-size "130%" - :margin-left "4px"})] + (when (and status-icon? status) + (if status-tooltip + (add-map-to-hiccup-call + (cmerger :popover) + [popover-tooltip + :src (at) + :label status-tooltip + :position :right-center + :status status + ;:width "200px" + :showing? showing? + :anchor (if (= :validating status) + (add-map-to-hiccup-call + (cmerger :throbber + {:attr {:on-mouse-over (handler-fn (when (and status-icon? status) (reset! showing? true))) + :on-mouse-out (handler-fn (reset! showing? false))}}) + [throbber + :size :regular]) + [:i (merge + (flatten-attr + (cmerger :tooltip-icon {:status status})) + {:on-mouse-over (handler-fn (when (and status-icon? status) (reset! showing? true))) + :on-mouse-out (handler-fn (reset! showing? false))})])]) (if (= :validating status) - [throbber - :src (at) - :size :regular - :class "smaller"] - [:i {:class (str "zmdi zmdi-hc-fw " icon-class " form-control-feedback") - :style (merge (flex-child-style "none") - (align-style :align-self :center) - {:position "static" - :font-size "130%" - :margin-left "4px" - :opacity (if (and status-icon? status) "1" "0") - :height "auto"}) - :title status-tooltip}]))))]])))))) + (add-map-to-hiccup-call + (cmerger :throbber) + [throbber + :src (at) + :size :regular]) + [:i (flatten-attr + (cmerger :tooltip-icon2 + {:status status + :attr {:title status-tooltip}}))])))]]))))))) (defn input-text @@ -213,4 +243,4 @@ (defn input-textarea [& args] - (apply input-text-base :input-type :textarea :debug-as (reflect-current-component) args)) \ No newline at end of file + (apply input-text-base :input-type :textarea :debug-as (reflect-current-component) args)) From 02ddd9ad09d9583f5a16ee8b4ce640b29d2be761 Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Tue, 29 Nov 2022 15:16:37 -0800 Subject: [PATCH 33/65] Css split out --- src/re_com/tabs.cljs | 120 ++++++++++++++++++++++++------------------- 1 file changed, 66 insertions(+), 54 deletions(-) diff --git a/src/re_com/tabs.cljs b/src/re_com/tabs.cljs index 0337fb28..259e9fda 100644 --- a/src/re_com/tabs.cljs +++ b/src/re_com/tabs.cljs @@ -5,7 +5,7 @@ (:require [re-com.config :refer [include-args-desc?]] [re-com.debug :refer [->attr]] - [re-com.util :refer [deref-or-value]] + [re-com.util :refer [deref-or-value add-map-to-hiccup-call merge-css flatten-attr]] [re-com.box :refer [flex-child-style]] [re-com.validate :refer [css-style? html-attr? parts? vector-of-maps? position? position-options-list]] @@ -29,6 +29,15 @@ (when include-args-desc? (-> (map :name horizontal-tabs-parts-desc) set))) +(def horizontal-tabs-css-spec + {:wrapper {:class ["nav" "nav-tabs" "noselect" "rc-tabs"] + :style (flex-child-style "none") + :use-toplevel true} + :tab {:class (fn [{:keys [selected?]}] + ["rc-tab" (when selected? "active")])} + :anchor {:class ["rc-tab-anchor"] + :style {:cursor "pointer"}}}) + (def horizontal-tabs-args-desc (when include-args-desc? [{:name :model :required true :type "unique-id | r/atom" :description "the unique identifier of the currently selected tab"} @@ -51,11 +60,11 @@ (validate-args-macro horizontal-tabs-args-desc args) (let [current (deref-or-value model) tabs (deref-or-value tabs) - _ (assert (not-empty (filter #(= current (id-fn %)) tabs)) "model not found in tabs vector")] + _ (assert (not-empty (filter #(= current (id-fn %)) tabs)) "model not found in tabs vector") + cmerger (merge-css horizontal-tabs-css-spec args)] [:ul - (merge {:class (str "nav nav-tabs noselect rc-tabs " class) - :style (merge (flex-child-style "none") - (get-in parts [:wrapper :style]))} + (merge (flatten-attr + (cmerger :wrapper)) (->attr args) attr) (for [t tabs] @@ -63,18 +72,13 @@ label (label-fn t) selected? (= id current)] ;; must use current instead of @model to avoid reagent warnings [:li - (merge - {:class (str (if selected? "active rc-tab ") (get-in parts [:tab :class])) - :style (get-in parts [:tab :style]) - :key (str id)} - (get-in parts [:tab :attr])) + (flatten-attr + (cmerger :tab {:selected? selected? + :attr {:key (str id)}})) [:a - (merge - {:class (str "rc-tab-anchor " (get-in parts [:anchor :class])) - :style (merge {:cursor "pointer"} - style) - :on-click (when on-change (handler-fn (on-change id)))} - (get-in parts [:anchor :attr])) + (flatten-attr + (cmerger :anchor {:attr + {:on-click (when on-change (handler-fn (on-change id)))}})) label]]))]))) @@ -94,6 +98,14 @@ (when include-args-desc? (-> (map :name horizontal-tabs-parts-desc) set))) +(def bar-tabs-css-spec + {:wrapper {:class (fn [{:keys [vertical?]}] + ["noselect" (if vertical? "btn-group-vertical" "btn-group") "rc-tabs"]) + :style (flex-child-style "none")} + :tooltip {:class ["rc-tabs-tooltip"]} + :button {:class (fn [{:keys [selected?]}] + ["btn" "btn-default" (when selected? "active") "rc-tabs-btn"])}}) + (def bar-tabs-args-desc (when include-args-desc? (-> @@ -111,12 +123,12 @@ (fn [& {:keys [model tabs]}] (let [current (deref-or-value model) tabs (deref-or-value tabs) - _ (assert (or (not validate?) (not-empty (filter #(= current (id-fn %)) tabs))) "model not found in tabs vector")] + _ (assert (or (not validate?) (not-empty (filter #(= current (id-fn %)) tabs))) "model not found in tabs vector") + cmerger (merge-css bar-tabs-css-spec args)] (into [:div (merge - {:class (str "noselect btn-group" (if vertical? "-vertical") " rc-tabs " class) - :style (merge (flex-child-style "none") - (get-in parts [:wrapper :style]))} + (flatten-attr + (cmerger :wrapper {:vertical? vertical?})) (->attr args) attr)] (for [t tabs] @@ -126,26 +138,23 @@ selected? (= id current) the-button [:button (merge - {:type "button" - :key (str id) - :class (str "btn btn-default " (if selected? "active ") "rc-tabs-btn " (get-in parts [:button :class])) - :style style - :on-click (when on-change (handler-fn (on-change id)))} - (when tooltip - {:on-mouse-over (handler-fn (reset! showing id)) - :on-mouse-out (handler-fn (swap! showing #(when-not (= id %) %)))}) - (get-in parts [:button :attr])) + (cmerger :button {:selected? selected?}) + {:type "button" + :key (str id) + :on-click (when on-change (handler-fn (on-change id)))} + (when tooltip + {:on-mouse-over (handler-fn (reset! showing id)) + :on-mouse-out (handler-fn (swap! showing #(when-not (= id %) %)))})) label]] (if tooltip - [popover-tooltip - :src (at) - :label tooltip - :position (or tooltip-position :below-center) - :showing? (reagent/track #(= id @showing)) - :anchor the-button - :class (str "rc-tabs-tooltip " (get-in parts [:tooltip :class])) - :style (get-in parts [:tooltip :style]) - :attr (get-in parts [:tooltip :attr])] + (add-map-to-hiccup-call + (cmerger :tooltip) + [popover-tooltip + :src (at) + :label tooltip + :position (or tooltip-position :below-center) + :showing? (reagent/track #(= id @showing)) + :anchor the-button]) the-button)))))))) @@ -210,6 +219,15 @@ (when include-args-desc? (-> (map :name horizontal-tabs-parts-desc) set))) +(def pill-tabs-css-spec + {:wrapper {:class (fn [{:keys [vertical?]}] + ["rc-tabs" "noselect" "nav" "nav-pills" (when vertical? "nav-stacked")]) + :style (flex-child-style "none")} + :tab {:class (fn [{:keys [selected?]}] + ["rc-tabs-pill" (when selected? "active")])} + :anchor {:class ["rc-tabs-anchor"] + :style {:cursor "pointer"}}}) + (def pill-tabs-args-desc (when include-args-desc? (-> @@ -223,13 +241,12 @@ [& {:keys [model tabs on-change id-fn label-fn vertical? class style attr parts src] :as args}] (let [current (deref-or-value model) tabs (deref-or-value tabs) - _ (assert (not-empty (filter #(= current (id-fn %)) tabs)) "model not found in tabs vector")] + _ (assert (not-empty (filter #(= current (id-fn %)) tabs)) "model not found in tabs vector") + cmerger (merge-css pill-tabs-css-spec args)] [:ul (merge - {:class (str "rc-tabs noselect nav nav-pills" (when vertical? " nav-stacked") " " class) - :style (merge (flex-child-style "none") - (get-in parts [:wrapper :style])) - :role "tabslist"} + (flatten-attr (cmerger :wrapper {:vertical? vertical?})) + {:role "tabslist"} (->attr args) attr) (for [t tabs] @@ -237,18 +254,13 @@ label (label-fn t) selected? (= id current)] ;; must use 'current' instead of @model to avoid reagent warnings [:li - (merge - {:class (str "rc-tabs-pill " (if selected? "active " "") (get-in parts [:tab :class])) - :style (get-in parts [:tab :style]) - :key (str id)} - (get-in parts [:tab :attr])) + (flatten-attr + (cmerger :tab {:selected? selected? + :attr {:key (str id)}})) [:a - (merge - {:class (str "rc-tabs-anchor " (get-in parts [:anchor :class])) - :style (merge {:cursor "pointer"} - style) - :on-click (when on-change (handler-fn (on-change id)))} - (get-in parts [:anchor :attr])) + (flatten-attr + (cmerger :anchor + {:attr {:on-click (when on-change (handler-fn (on-change id)))}})) label]]))])) From 8ce370080e36094d21f5be8cc3f83a5636828cb3 Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Wed, 30 Nov 2022 08:28:58 -0800 Subject: [PATCH 34/65] Css split out --- src/re_com/selection_list.cljs | 145 +++++++++++++++++---------------- 1 file changed, 75 insertions(+), 70 deletions(-) diff --git a/src/re_com/selection_list.cljs b/src/re_com/selection_list.cljs index 88c726da..dbf43733 100644 --- a/src/re_com/selection_list.cljs +++ b/src/re_com/selection_list.cljs @@ -10,7 +10,7 @@ [re-com.radio-button :refer [radio-button]] [re-com.box :refer [box border h-box v-box]] [re-com.validate :refer [vector-of-maps? string-or-atom? set-or-atom? css-style? html-attr? parts?]] - [re-com.util :refer [fmap deref-or-value]])) + [re-com.util :refer [fmap deref-or-value merge-css add-map-to-hiccup-call flatten-attr]])) ;; ---------------------------------------------------------------------------- (defn label-style @@ -28,33 +28,34 @@ base-style)] base-style))) +(declare selection-list-css-spec) + (defn- as-checked [item id-fn selections on-change disabled? label-fn required? as-exclusions? parts] ;;TODO: Do we really need an anchor now that bootstrap styles not realy being used ? - (let [item-id (id-fn item)] - [box - :class (str "list-group-item compact rc-selection-list-group-item " (get-in parts [:list-group-item :class])) - :style (get-in parts [:list-group-item :style] {}) - :attr (merge - {:on-click (handler-fn - (let [num-selected (count selections) - only-item (when (= 1 num-selected) (first selections))] - (when (and (not disabled?) - (not (and required? (= only-item item-id)))) - (if (selections item-id) - (on-change (disj selections item-id)) - (on-change (conj selections item-id))))))} - (get-in parts [:list-group-item :attr])) - :child [checkbox - :src (at) - :class (str "rc-selection-list-checkbox " (get-in parts [:checkbox :class])) - :style (get-in parts [:checkbox :style]) - :attr (get-in parts [:checkbox :attr]) - :model (some? (selections item-id)) - :on-change #() ;; handled by enclosing box - :disabled? disabled? - :label-style (label-style (selections item-id) as-exclusions?) - :label (label-fn item)]])) + (let [item-id (id-fn item) + cmerger (merge-css selection-list-css-spec {:parts parts})] + (add-map-to-hiccup-call + (cmerger + :list-group-item + {:attr {:on-click (handler-fn + (let [num-selected (count selections) + only-item (when (= 1 num-selected) (first selections))] + (when (and (not disabled?) + (not (and required? (= only-item item-id)))) + (if (selections item-id) + (on-change (disj selections item-id)) + (on-change (conj selections item-id))))))}}) + [box + :child (add-map-to-hiccup-call + (cmerger :checkbox) + [checkbox + :src (at) + :model (some? (selections item-id)) + :on-change #() ;; handled by enclosing box + :disabled? disabled? + :label-style (label-style (selections item-id) as-exclusions?) + :label (label-fn item)])]))) (defn- radio-clicked @@ -63,29 +64,27 @@ (defn- as-radio [item id-fn selections on-change disabled? label-fn required? as-exclusions? parts] - (let [item-id (id-fn item)] - [box - :class (str "list-group-item compact rc-selection-list-group-item " (get-in parts [:list-group-item :class])) - :style (get-in parts [:list-group-item :style] {}) - :attr (merge {:on-click (handler-fn (when-not disabled? - ;; prevents on-change from firing if unselect is disabled (required?) - ;; and the item clicked is already selected. - (when-not (and required? (selections item-id)) - (on-change (radio-clicked selections item-id)))))} - (get-in parts [:list-group-item :attr])) - :child [radio-button - :src (at) - :class (str "rc-selection-list-radio-button " (get-in parts [:radio-button :class])) - :style (merge (get-in parts [:radio-button :style] {}) - (when disabled? - {:pointer-events "none"})) - :attr (get-in parts [:radio-button :attr]) - :model (first selections) - :value item-id - :on-change #() ;; handled by enclosing box - :disabled? disabled? - :label-style (label-style (selections item-id) as-exclusions?) - :label (label-fn item)]])) + (let [item-id (id-fn item) + cmerger (merge-css selection-list-css-spec {:parts parts})] + (add-map-to-hiccup-call + (cmerger + :list-group-item + {:attr {:on-click (handler-fn (when-not disabled? + ;; prevents on-change from firing if unselect is disabled (required?) + ;; and the item clicked is already selected. + (when-not (and required? (selections item-id)) + (on-change (radio-clicked selections item-id)))))}}) + [box + :child (add-map-to-hiccup-call + (cmerger :radio-button {:disabled? disabled?}) + [radio-button + :src (at) + :model (first selections) + :value item-id + :on-change #() ;; handled by enclosing box + :disabled? disabled? + :label-style (label-style (selections item-id) as-exclusions?) + :label (label-fn item)])]))) (def list-style @@ -121,6 +120,21 @@ (when include-args-desc? (-> (map :name selection-list-parts-desc) set))) +(def selection-list-css-spec + {:main {:class (fn [{:keys [disabled?]}] + ["rc-selection-list" (when disabled? "rc-disabled")])} + :list-group {:class ["rc-selection-list-group" "list-group" "noselect"] + :style (fn [{:keys [hide-border? width height max-height]}] + (merge + list-style + (if hide-border? spacing-unbordered spacing-bordered) + {:width width :height height :max-height max-height}))} + :list-group-item {:class ["rc-selection-list-group-item" "list-group-item" "compact"]} + :checkbox {:class ["rc-selection-list-checkbox"]} + :radio-button {:class ["rc-selection-list-radio-button"] + :style (fn [{:keys [disabled?]}] + (when disabled? {:pointer-events "none"}))}}) + (def selection-list-args-desc (when include-args-desc? [{:name :choices :required true :type "vector of choices | r/atom" :validate-fn vector-of-maps? :description [:span "the selectable items. Elements can be strings or more interesting data items like {:label \"some name\" :sort 5}. Also see " [:code ":label-fn"] " below (list of maps also allowed)"]} @@ -179,30 +193,21 @@ #(as-checked % id-fn selected on-change disabled? label-fn required? as-exclusions? parts) #(as-radio % id-fn selected on-change disabled? label-fn required? as-exclusions? parts))) choices) - bounds (select-keys args [:width :height :max-height]) - spacing (if hide-border? spacing-unbordered spacing-bordered)] + cmerger (merge-css selection-list-css-spec args)] ;; In single select mode force selections to one. This causes a second render ;; TODO: GR commented this out to fix the bug where #{nil} was being returned for an empty list. Remove when we're sure there are no ill effects. #_(when-not (= selected model) (on-change selected)) - [border - :src src - :debug-as (or debug-as (reflect-current-component)) - :class (str "rc-selection-list " - (when (deref-or-value disabled?) "rc-disabled") - class) - :style style - :attr attr - :radius "4px" - :border (when hide-border? "none") - :child (into [:div - (merge - {:class (str "list-group noselect rc-selection-list-group " (get-in parts [:list-group :class])) - :style (merge - list-style - bounds - spacing - (get-in parts [:list-group :style]))} - (get-in parts [:list-group :attr]))] - items)]))) + (add-map-to-hiccup-call + (cmerger :main {:disabled? (deref-or-value disabled?)}) + [border + :src src + :debug-as (or debug-as (reflect-current-component)) + :radius "4px" + :border (when hide-border? "none") + :child (into [:div + (flatten-attr + (cmerger :list-group {:width width :height height :max-height max-height + :hide-border? hide-border?}))] + items)])))) From 07182ed8909a76d4cdebaba1931253649c40fb1a Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Wed, 30 Nov 2022 10:12:55 -0800 Subject: [PATCH 35/65] Css split out --- src/re_com/alert.cljs | 182 ++++++++++++++++++++++-------------------- 1 file changed, 97 insertions(+), 85 deletions(-) diff --git a/src/re_com/alert.cljs b/src/re_com/alert.cljs index 9baa648c..adfe6884 100644 --- a/src/re_com/alert.cljs +++ b/src/re_com/alert.cljs @@ -7,7 +7,7 @@ [re-com.close-button :refer [close-button]] [re-com.config :refer [include-args-desc?]] [re-com.debug :refer [->attr]] - [re-com.util :refer [deref-or-value]] + [re-com.util :refer [deref-or-value add-map-to-hiccup-call merge-css flatten-attr]] [re-com.validate :refer [string-or-hiccup? alert-type? alert-types-list vector-of-maps? css-style? html-attr? parts?] :refer-macros [validate-args-macro]])) @@ -17,7 +17,7 @@ (def alert-box-parts-desc (when include-args-desc? - [{:type :legacy :level 0 :class "rc-alert-box" :impl "[alert-box]"} + [{:type :legacy :level 0 :class "rc-alert" :impl "[alert-box]"} {:name :heading :level 1 :class "rc-alert-heading" :impl "[h-box]"} {:name :h4 :level 2 :class "rc-alert-h4" :impl "[:h4]"} {:name :close-button :level 2 :class "rc-alert-close-button" :impl "[close-button]"} @@ -27,6 +27,27 @@ (when include-args-desc? (-> (map :name alert-box-parts-desc) set))) +(def alert-box-css-spec + {:main {:class (fn [{:keys [alert-type]}] + ["rc-alert" "alert" "fade" "in" + (case alert-type + :none nil + :info "alert-success" + :warning "alert-warning" + :danger "alert-danger" + nil)]) + :style (fn [{:keys [padding]}] + (merge (flex-child-style "none") + {:padding padding}))} + :heading {:class ["rc-alert-heading"] + :style (fn [{:keys [body]}] + {:margin-bottom (if body "10px" "0px")})} + :h4 {:class ["rc-alert-h4"] + :style {:margin-bottom "0px"}} + :close-button {:class ["rc-alert-close-button"]} + :body {:class ["rc-alert-body"]} +}) + (def alert-box-args-desc (when include-args-desc? [{:name :id :required false :type "anything" :description [:span "a unique identifier, usually an integer or string."]} @@ -50,54 +71,41 @@ :as args}] (or (validate-args-macro alert-box-args-desc args) - (let [close-alert [close-button - :src (at) - :class (str "rc-alert-close-button " (get-in parts [:close-button :class])) - :style (get-in parts [:close-button :style]) - :attr (get-in parts [:close-button :attr]) - :on-click #(on-close id) - :div-size 20 - :font-size 20] - alert-class (alert-type {:none "" - :info "alert-success" - :warning "alert-warning" - :danger "alert-danger"})] + (let [cmerger (merge-css alert-box-css-spec args) + close-alert (add-map-to-hiccup-call + (cmerger :close-button) + [close-button + :src (at) + :on-click #(on-close id) + :div-size 20 + :font-size 20])] [:div - (merge {:class (str "rc-alert alert fade in " alert-class " " class) - :style (merge (flex-child-style "none") - {:padding padding} - style)} + (merge (flatten-attr + (cmerger :main {:alert-type alert-type :padding padding})) (->attr args) attr) (when heading - [h-box - :src (at) - :justify :between - :align :center - :class (str "rc-alert-heading " (get-in parts [:heading :class])) - :style (merge {:margin-bottom (if body "10px" "0px")} - (get-in parts [:heading :style])) - :attr (get-in parts [:heading :attr] {}) - :children [[:h4 - (merge - {:class (str "rc-alert-h4 " (get-in parts [:h4 :class])) - :style (merge {:margin-bottom "0px"} - (get-in parts [:h4 :style]))} - (get-in parts [:h4 :attr])) ;; Override h4 - heading] - (when (and closeable? on-close) - close-alert)]]) + (add-map-to-hiccup-call + (cmerger :heading {:body body}) + [h-box + :src (at) + :justify :between + :align :center + :children [[:h4 + (flatten-attr (cmerger :h4)) + heading] + (when (and closeable? on-close) + close-alert)]])) (when body - [h-box - :src (at) - :justify :between - :align :center - :class (str "rc-alert-body " (get-in parts [:body :class])) - :style (get-in parts [:body :style] {}) - :attr (get-in parts [:body :attr] {}) - :children [[:div body] - (when (and (not heading) closeable? on-close) - close-alert)]])]))) + (add-map-to-hiccup-call + (cmerger :body) + [h-box + :src (at) + :justify :between + :align :center + :children [[:div body] + (when (and (not heading) closeable? on-close) + close-alert)]]))]))) ;;-------------------------------------------------------------------------------------------------- ;; Component: alert-list @@ -116,6 +124,14 @@ (when include-args-desc? (-> (map :name alert-list-parts-desc) set))) +(def alert-list-css-spec + {:wrapper {:class ["rc-alert-list-wrapper"]} + :main {:class ["rc-alert-list"]} + :scroller {:class ["rc-alert-list-scroller"] + :style (fn [{:keys [max-height]}] + {:max-height max-height})} + :v-box {:class ["rc-alert-list-v-box"]}}) + (def alert-list-args-desc (when include-args-desc? [{:name :alerts :required true :type "vector of maps | r/atom" :validate-fn vector-of-maps? :description "alerts to render (in the order supplied). Can also be a list of maps"} @@ -149,43 +165,39 @@ :as args}] (or (validate-args-macro alert-list-args-desc args) - (let [alerts (deref-or-value alerts)] - [box - :src src - :debug-as (or debug-as (reflect-current-component)) - :class (str "rc-alert-list-wrapper " (get-in parts [:wrapper :class])) - :style (get-in parts [:wrapper :style] {}) - :attr (get-in parts [:wrapper :attr] {}) - :child [border - :src (at) - :class (str "rc-alert-list " class) - :style style - :attr attr - :padding padding - :border border-style - :child [scroller - :src (at) - :v-scroll :auto - :class (str "rc-alert-list-scroller " (get-in parts [:scroller :class])) - :style (merge {:max-height max-height} - (get-in parts [:scroller :style])) - :attr (get-in parts [:scroller :attr]) - :child [v-box - :src (at) - :size "auto" - :class (str "rc-alert-list-v-box " (get-in parts [:v-box :class])) - :style (get-in parts [:v-box :style]) - :attr (get-in parts [:v-box :attr]) - :children [(for [alert alerts] - (let [{:keys [id alert-type heading body padding closeable?]} alert] - ^{:key id} [alert-box - :src (at) - :id id - :alert-type alert-type - :heading heading - :body body - :padding padding - :closeable? closeable? - :on-close on-close - :class alert-class - :style (merge alert-style (:style alert))]))]]]]]))) + (let [alerts (deref-or-value alerts) + cmerger (merge-css alert-list-css-spec args)] + (add-map-to-hiccup-call + (cmerger :wrapper) + [box + :src src + :debug-as (or debug-as (reflect-current-component)) + :child (add-map-to-hiccup-call + (cmerger :main) + [border + :src (at) + :padding padding + :border border-style + :child (add-map-to-hiccup-call + (cmerger :scroller {:max-height max-height}) + [scroller + :src (at) + :v-scroll :auto + :child (add-map-to-hiccup-call + (cmerger :v-box) + [v-box + :src (at) + :size "auto" + :children [(for [alert alerts] + (let [{:keys [id alert-type heading body padding closeable?]} alert] + ^{:key id} [alert-box + :src (at) + :id id + :alert-type alert-type + :heading heading + :body body + :padding padding + :closeable? closeable? + :on-close on-close + :class alert-class + :style (merge alert-style (:style alert))]))]])])])])))) From d4552a11405f6b4f9f2144bcfe1b071003511c83 Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Wed, 30 Nov 2022 11:01:45 -0800 Subject: [PATCH 36/65] Css split out --- src/re_com/input_time.cljs | 57 +++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/src/re_com/input_time.cljs b/src/re_com/input_time.cljs index dad3b9cf..90e1796c 100644 --- a/src/re_com/input_time.cljs +++ b/src/re_com/input_time.cljs @@ -9,7 +9,7 @@ [re-com.validate :refer [css-style? html-attr? parts? number-or-string?]] [re-com.text :refer [label]] [re-com.box :refer [h-box gap]] - [re-com.util :refer [pad-zero-number deref-or-value]])) + [re-com.util :refer [pad-zero-number deref-or-value add-map-to-hiccup-call merge-css flatten-attr]])) (defn- time->mins @@ -156,6 +156,17 @@ (when include-args-desc? (-> (map :name input-time-parts-desc) set))) +(def input-time-css-spec + {:wrapper {:class ["rc-input-time"]} + ;; Leaving time-entry class (below) for backwards compatibility only. + :main {:class ["rc-time-entry" "time-entry"] + :style (fn [{:keys [width]}] + {:width width})} + :time-icon-container {:class ["rc-time-icon-container" "time-icon"]} + :time-icon {:class ["rc-time-icon" "zmdi" "zmdi-hc-fw-rc" "zmdi-time"] + :style {:position "static" + :margin "auto"}}}) + (def input-time-args-desc (when include-args-desc? [{:name :model :required true :type "integer | string | r/atom" :validate-fn number-or-string? :description "a time in integer form. e.g. '09:30am' is 930"} @@ -195,43 +206,33 @@ style) new-val (deref-or-value model) new-val (if (< new-val minimum) minimum new-val) - new-val (if (> new-val maximum) maximum new-val)] + new-val (if (> new-val maximum) maximum new-val) + cmerger (merge-css input-time-css-spec args)] ;; if the model is different to that currently shown in text, then reset the text to match ;; other than that we want to keep the current text, because the user is probably typing (when (not= @previous-model new-val) (reset! text-model (time->text new-val)) (reset! previous-model new-val)) - [h-box - :src src - :debug-as (or debug-as (reflect-current-component)) - :class (str "rc-input-time " (get-in parts [:wrapper :class])) - :style (merge {:height height} (get-in parts [:wrapper :style])) - :attr (get-in parts [:wrapper :attr]) - :children [[:input - (merge + (add-map-to-hiccup-call + (cmerger :wrapper) + [h-box + :src src + :debug-as (or debug-as (reflect-current-component)) + :children [[:input + (merge + (flatten-attr + (cmerger :main {:width width})) {:type "text" - ;; Leaving time-entry class (below) for backwards compatibility only. - :class (str "time-entry rc-time-entry " class) - :style (merge {:width width} - style) :value @text-model :disabled (deref-or-value disabled?) :on-change (handler-fn (on-new-keypress event text-model)) :on-blur (handler-fn (on-defocus text-model minimum maximum on-change @previous-model)) :on-key-up (handler-fn (lose-focus-if-enter event))} attr)] - (when show-icon? - ;; Leaving time-icon class (below) for backwards compatibility only. - [:div - (merge - {:class (str "time-icon rc-time-icon-container " (get-in parts [:time-icon-container :class])) - :style (get-in parts [:time-icon-container :style] {})} - (get-in parts [:time-icon-container :attr])) - [:i - (merge - {:class (str "zmdi zmdi-hc-fw-rc zmdi-time rc-time-icon " (get-in parts [:time-icon :class])) - :style (merge {:position "static" - :margin "auto"} - (get-in parts [:time-icon :style]))} - (get-in parts [:time-icon :attr]))]])]])))))) + (when show-icon? + ;; Leaving time-icon class (below) for backwards compatibility only. + [:div + (flatten-attr (cmerger :time-icon-container)) + [:i + (flatten-attr (cmerger :time-icon))]])]]))))))) From f7c22410ed6931339f8cea32f5bfedb354c5ace9 Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Thu, 1 Dec 2022 10:08:24 -0800 Subject: [PATCH 37/65] Bugfix --- src/re_com/box.cljs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/re_com/box.cljs b/src/re_com/box.cljs index ebae1c7c..22b3b294 100644 --- a/src/re_com/box.cljs +++ b/src/re_com/box.cljs @@ -221,11 +221,10 @@ (def line-css-spec {:main {:class ["rc-line"] - :style (fn [{:keys [size color] - :or {color "lightgray"}}] + :style (fn [{:keys [size color]}] (merge (flex-child-style (str "0 0 " (or size "1px"))) - {:background-color color}))}}) + {:background-color (or color "lightgray")}))}}) (defn line "Returns a component which produces a line between children in a v-box/h-box along the main axis. From 9f4230f05f051e9222e3c46df5b48ee54284387e Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Thu, 1 Dec 2022 10:08:40 -0800 Subject: [PATCH 38/65] Css split out --- src/re_com/text.cljs | 114 +++++++++++++++++++++++++------------------ 1 file changed, 67 insertions(+), 47 deletions(-) diff --git a/src/re_com/text.cljs b/src/re_com/text.cljs index 3ced5ccc..55c5e9da 100644 --- a/src/re_com/text.cljs +++ b/src/re_com/text.cljs @@ -6,7 +6,7 @@ [re-com.config :refer [include-args-desc?]] [re-com.debug :refer [->attr]] [re-com.box :refer [v-box box line flex-child-style]] - [re-com.util :refer [deep-merge]] + [re-com.util :refer [deep-merge add-map-to-hiccup-call merge-css flatten-attr]] [re-com.validate :refer [title-levels-list title-level-type? css-style? html-attr? parts? string-or-hiccup?]])) @@ -23,6 +23,11 @@ (when include-args-desc? (-> (map :name label-parts-desc) set))) +(def label-css-spec + {:main {:class ["rc-label"] + :style (flex-child-style "none")} + :wrapper {:class ["rc-label-wrapper" "display-inline-flex"]}}) + (def label-args-desc (when include-args-desc? [{:name :label :required true :type "anything" :description "text or hiccup or whatever to display"} @@ -41,23 +46,21 @@ :as args}] (or (validate-args-macro label-args-desc args) - [box - :debug-as (or debug-as (reflect-current-component)) - :src src - :class (str "display-inline-flex rc-label-wrapper " (get-in parts [:wrapper :class])) - :style (get-in parts [:wrapper :style]) - :attr (get-in parts [:wrapper :attr]) - :width width - :align :start - :child [:span - (merge - {:class (str "rc-label " class) - :style (merge (flex-child-style "none") - style)} - (when on-click - {:on-click (handler-fn (on-click))}) - attr) - label]])) + (let [cmerger (merge-css label-css-spec args)] + (add-map-to-hiccup-call + (cmerger :wrapper) + [box + :debug-as (or debug-as (reflect-current-component)) + :src src + :width width + :align :start + :child [:span + (merge + (cmerger :main) + (when on-click + {:on-click (handler-fn (on-click))}) + attr) + label]])))) ;; ------------------------------------------------------------------------------------ @@ -74,6 +77,22 @@ (when include-args-desc? (-> (map :name title-parts-desc) set))) +(def title-css-spec + {:wrapper {:class (fn [{:keys [level]}] + ["rc-title-wrapper" (when level (name level))])} + :main {:class (fn [{:keys [level]}] + ["rc-title" "display-flex" (when level (name level))]) + :style (fn [{:keys [margin-top underline? margin-bottom]}] + (merge + (flex-child-style "none") + {:margin-top (or margin-top "0.6em") + :margin-bottom (when-not underline? (or margin-bottom "0.3em")) + ;; so that the margins are correct + :line-height 1}))} + :underline {:class ["rc-title-underline"] + :style (fn [{:keys [margin-bottom]}] + {:margin-bottom (or margin-bottom "0.3em")})}}) + (def title-args-desc (when include-args-desc? [{:name :label :required true :type "anything" :description "title or hiccup or anything to display"} @@ -91,37 +110,37 @@ (defn title "A title with four preset levels" [& {:keys [label level underline? margin-top margin-bottom class style attr parts src debug-as] - :or {margin-top "0.6em" margin-bottom "0.3em"} :as args}] (or (validate-args-macro title-args-desc args) - (let [preset-class (if (nil? level) "" (name level))] - [v-box - :src src - :debug-as (or debug-as (reflect-current-component)) - :class (str "rc-title-wrapper " preset-class " " (get-in parts [:wrapper :class])) - :style (get-in parts [:wrapper :style]) - :attr (get-in parts [:wrapper :attr]) - :children [[:span (merge {:class (str "display-flex rc-title " preset-class " " class) - :style (merge (flex-child-style "none") - {:margin-top margin-top} - {:line-height 1} ;; so that the margins are correct - (when-not underline? {:margin-bottom margin-bottom}) - style)} - attr) - label] - (when underline? [line - :src (at) - :size "1px" - :class (str "rc-title-underline " (get-in parts [:underline :class])) - :style (merge {:margin-bottom margin-bottom} (get-in parts [:underline :style])) - :attr (get-in parts [:underline :attr])])]]))) + (let [cmerger (merge-css title-css-spec args)] + (add-map-to-hiccup-call + (cmerger :wrapper {:level level}) + [v-box + :src src + :debug-as (or debug-as (reflect-current-component)) + :children [[:span (merge (flatten-attr + (cmerger :main {:level level :underline? underline? + :margin-top margin-top :margin-bottom margin-bottom})) + attr) + label] + (when underline? (add-map-to-hiccup-call + (cmerger :underline {:margin-bottom margin-bottom}) + [line + :src (at) + :size "1px"]))]])))) ;; ------------------------------------------------------------------------------------ ;; Component: p ;; ------------------------------------------------------------------------------------ +(def p-css-spec + {:main {:class ["rc-p"] + :style {:flex "none" + :width "450px" + :min-width "450px" + :margin-bottom "0.7em"}}}) (defn p "acts like [:p ] but uses a [:span] in place of the [:p] and adds bottom margin of 0.7ems which produces the same visual result. @@ -148,12 +167,13 @@ [m children] (if (map? child1) [child1 (rest children)] [{} children]) - m (deep-merge {:style {:flex "none" - :width "450px" - :min-width "450px" - :margin-bottom "0.7em"}} - m)] - [:span.rc-p m (into [:span] children)])) + user (merge {:attr (reduce (partial dissoc m) [:class :style])} + (when-let [c (:class m)] {:class c}) + (when-let [s (:style m)] {:style s})) + cmerger (merge-css p-css-spec {})] + [:span + (flatten-attr (cmerger :main user)) + (into [:span] children)])) ;; Alias for backwards compatibility; p and p-span used to be different implementations. -(def p-span p) \ No newline at end of file +(def p-span p) From 8be36431307453fa17e60ee7197d248e314c7b78 Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Thu, 1 Dec 2022 11:25:51 -0800 Subject: [PATCH 39/65] Css split out --- src/re_com/close_button.cljs | 101 +++++++++++++++++++++-------------- 1 file changed, 61 insertions(+), 40 deletions(-) diff --git a/src/re_com/close_button.cljs b/src/re_com/close_button.cljs index 4da5c136..b735809e 100644 --- a/src/re_com/close_button.cljs +++ b/src/re_com/close_button.cljs @@ -4,7 +4,7 @@ (:require [re-com.config :refer [include-args-desc?]] [re-com.debug :refer [->attr]] - [re-com.util :refer [deref-or-value px]] + [re-com.util :refer [deref-or-value px add-map-to-hiccup-call merge-css flatten-attr]] [re-com.validate :refer [string-or-hiccup? css-style? html-attr? parts?] :refer-macros [validate-args-macro]] [re-com.box :refer [box]] [reagent.core :as reagent])) @@ -19,6 +19,35 @@ ;; TODO: Add a demo page for this +(def close-button-css-spec + {:wrapper {:class ["rc-close-button"] + :style (fn [{:keys [div-size disabled?]}] + (merge + {:display "inline-block" + :position "relative" + :width (px (or div-size 16)) + :height (px (or div-size 16))} + (when disabled? {:pointer-events "none"})))} + :main {:style (fn [{:keys [disabled? + over? + font-size + div-size + top-offset + left-offset + color + hover-color]}] + (let [div-size (or div-size 16) + font-size (or font-size 16) + color (or color "#ccc") + hover-color (or hover-color "#999")] + {:position "absolute" + :cursor (when-not disabled? "pointer") + :font-size (px font-size) + :color (if over? hover-color color) + :top (px (- (/ (- font-size div-size) 2) top-offset) :negative) + :left (px (- (/ (- font-size div-size) 2) left-offset) :negative)}))} + :icon {:class ["rc-close-button-icon" "zmdi" "zmdi-hc-fw-rc" "zmdi-close"]}}) + (def close-button-args-desc (when include-args-desc? [{:name :on-click :required false :type "-> nil" :validate-fn fn? :description "a function which takes no params and returns nothing. Called when the button is clicked"} @@ -41,44 +70,36 @@ [] (let [over? (reagent/atom false)] (fn close-button-render - [& {:keys [on-click div-size font-size color hover-color tooltip top-offset left-offset disabled? class style attr parts src debug-as] :as args - :or {div-size 16 font-size 16 color "#ccc" hover-color "#999"}}] + [& {:keys [on-click div-size font-size color hover-color tooltip top-offset left-offset disabled? class style attr parts src debug-as] :as args}] (or (validate-args-macro close-button-args-desc args) - (let [disabled? (deref-or-value disabled?)] - [box - :src src - :debug-as (or debug-as (reflect-current-component)) - :class (str "rc-close-button " (get-in parts [:wrapper :class])) - :style (merge {:display "inline-block" - :position "relative" - :width (px div-size) - :height (px div-size)} - (when disabled? {:pointer-events "none"}) - (get-in parts [:wrapper :style])) - :attr (get-in parts [:wrapper :attr]) - :child [box - :src (at) - :class class - :style (merge - {:position "absolute" - :cursor (when-not disabled? "pointer") - :font-size (px font-size) - :color (if @over? hover-color color) - :top (px (- (/ (- font-size div-size) 2) top-offset) :negative) - :left (px (- (/ (- font-size div-size) 2) left-offset) :negative)} - style) - :attr (merge - {:title tooltip - :on-click (handler-fn - (when (and on-click (not disabled?)) - (on-click event) - (.stopPropagation event))) - :on-mouse-enter (handler-fn (reset! over? true)) - :on-mouse-leave (handler-fn (reset! over? false))} - attr) - :child [:i - (merge - {:class (str "rc-close-button-icon zmdi zmdi-hc-fw-rc zmdi zmdi-close " (get-in parts [:icon :class])) - :style (get-in parts [:icon :style] {})} - (get-in parts [:icon :attr]))]]]))))) + (let [disabled? (deref-or-value disabled?) + cmerger (merge-css close-button-css-spec args)] + (add-map-to-hiccup-call + (cmerger :wrapper {:div-size div-size :disabled? disabled?}) + [box + :src src + :debug-as (or debug-as (reflect-current-component)) + :child (add-map-to-hiccup-call + (cmerger :main + {:disabled? disabled? + :over? @over? + :font-size font-size + :div-size div-size + :top-offset top-offset + :left-offset left-offset + :color color + :hover-color hover-color + :attr (merge + {:title tooltip + :on-click (handler-fn + (when (and on-click (not disabled?)) + (on-click event) + (.stopPropagation event))) + :on-mouse-enter (handler-fn (reset! over? true)) + :on-mouse-leave (handler-fn (reset! over? false))} + attr)}) + [box + :src (at) + :child [:i + (flatten-attr (cmerger :icon))]])])))))) From 6a077db138dba031b68fbebe7ea537929373cdd5 Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Thu, 1 Dec 2022 12:21:55 -0800 Subject: [PATCH 40/65] Css split out --- src/re_com/radio_button.cljs | 60 +++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/src/re_com/radio_button.cljs b/src/re_com/radio_button.cljs index 8668fec5..24e07116 100644 --- a/src/re_com/radio_button.cljs +++ b/src/re_com/radio_button.cljs @@ -5,7 +5,7 @@ (:require [re-com.config :refer [include-args-desc?]] [re-com.debug :refer [->attr]] - [re-com.util :refer [deref-or-value px]] + [re-com.util :refer [deref-or-value px add-map-to-hiccup-call merge-css flatten-attr]] [re-com.popover :refer [popover-tooltip]] [re-com.box :refer [h-box v-box box gap line flex-child-style align-style]] [re-com.validate :refer [input-status-type? input-status-types-list regex? string-or-hiccup? css-style? html-attr? parts? @@ -26,6 +26,17 @@ (when include-args-desc? (-> (map :name radio-button-parts-desc) set))) +(def radio-button-css-spec + {:main {:class ["rc-radio-button"] + :style (merge + (flex-child-style "none") + {:cursor "default"})} + :wrapper {:class ["rc-radio-button-wrapper" "noselect"]} + :label {:class ["rc-radio-button-label"] + :style (merge (flex-child-style "none") + {:padding-left "8px" + :cursor "default"})}}) + (def radio-button-args-desc (when include-args-desc? [{:name :model :required true :type "anything | r/atom" :description [:span "selected value of the radio button group. See also " [:code ":value"]]} @@ -48,36 +59,29 @@ :as args}] (or (validate-args-macro radio-button-args-desc args) - (let [cursor "default" - model (deref-or-value model) + (let [model (deref-or-value model) disabled? (deref-or-value disabled?) callback-fn #(when (and on-change (not disabled?)) - (on-change value))] ;; call on-change with the :value arg - [h-box - :src src - :debug-as (or debug-as (reflect-current-component)) - :class (str "noselect rc-radio-button-wrapper " (get-in parts [:wrapper :class])) - :style (get-in parts [:wrapper :style]) - :attr (get-in parts [:wrapper :attr]) - :align :start - :children [[:input - (merge - {:class (str "rc-radio-button " class) - :style (merge - (flex-child-style "none") - {:cursor cursor} - style) - :type "radio" + (on-change value)) ;; call on-change with the :value arg + cmerger (merge-css radio-button-css-spec args)] + (add-map-to-hiccup-call + (cmerger :wrapper) + [h-box + :src src + :debug-as (or debug-as (reflect-current-component)) + :align :start + :children [[:input + (merge + (flatten-attr (cmerger :main)) + {:type "radio" :disabled disabled? :checked (= model value) :on-change (handler-fn (callback-fn))} attr)] - (when label - [:span - {:class (str "rc-radio-button-label " label-class) - :style (merge (flex-child-style "none") - {:padding-left "8px" - :cursor cursor} - label-style) - :on-click (handler-fn (callback-fn))} - label])]]))) \ No newline at end of file + (when label + [:span + (flatten-attr + (cmerger :label {:class label-class + :style label-style + :attr {:on-click (handler-fn (callback-fn))}})) + label])]])))) From ed883510474e643953acae9de9d2ec69c96e8f5c Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Thu, 1 Dec 2022 14:04:28 -0800 Subject: [PATCH 41/65] Css split out --- src/re_com/modal_panel.cljs | 79 ++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 37 deletions(-) diff --git a/src/re_com/modal_panel.cljs b/src/re_com/modal_panel.cljs index 2c639950..ac538ca1 100644 --- a/src/re_com/modal_panel.cljs +++ b/src/re_com/modal_panel.cljs @@ -5,6 +5,7 @@ (:require [re-com.config :refer [include-args-desc?]] [re-com.debug :refer [->attr]] + [re-com.util :refer [merge-css add-map-to-hiccup-call flatten-attr]] [re-com.validate :refer [string-or-hiccup? number-or-string? css-style? html-attr? parts?]])) ;; ------------------------------------------------------------------------------------ @@ -21,6 +22,32 @@ (when include-args-desc? (-> (map :name modal-panel-parts-desc) set))) +(def modal-panel-css-spec + {:main {:class ["rc-modal-panel" "display-flex"] + :style {:position "fixed" + :left "0px" + :top "0px" + :width "100%" + :height "100%" + :z-index 1020}} + :backdrop {:class ["rc-modal-panel-backdrop"] + :style (fn [{:keys [color opacity]}] + {:position "fixed" + :width "100%" + :height "100%" + :background-color (or color "black") + :opacity (or opacity 0.6) + :z-index 1})} + :child-container {:class ["rc-modal-panel-child-container"] + :style (fn [{:keys [wrap-nicely?]}] + (merge + {:margin "auto" + :z-index 2} + (when wrap-nicely? + {:background-color "white" + :padding "16px" + :border-radius "6px"})))}}) + (def modal-panel-args-desc (when include-args-desc? [{:name :child :required true :type "string | hiccup" :validate-fn string-or-hiccup? :description "hiccup to be centered within in the browser window"} @@ -42,43 +69,21 @@ Parameters: - child: The message to display in the modal (a string or a hiccup vector or function returning a hiccup vector)" [& {:keys [child wrap-nicely? backdrop-color backdrop-opacity backdrop-on-click class style attr parts] - :or {wrap-nicely? true backdrop-color "black" backdrop-opacity 0.6} + :or {wrap-nicely? true} :as args}] (or (validate-args-macro modal-panel-args-desc args) - [:div ;; Containing div - (merge {:class (str "display-flex rc-modal-panel " class) - :style (merge {:position "fixed" - :left "0px" - :top "0px" - :width "100%" - :height "100%" - :z-index 1020} - style)} - (->attr args) - attr) - [:div ;; Backdrop - (merge - {:class (str "rc-modal-panel-backdrop " (get-in parts [:backdrop :class])) - :style (merge {:position "fixed" - :width "100%" - :height "100%" - :background-color backdrop-color - :opacity backdrop-opacity - :z-index 1} - (get-in parts [:backdrop :style])) - :on-click (handler-fn (when backdrop-on-click (backdrop-on-click)) - (.preventDefault event) - (.stopPropagation event))} - (get-in parts [:backdrop :attr]))] - [:div ;; Child container - (merge - {:class (str "rc-modal-panel-child-container " (get-in parts [:child-container :class])) - :style (merge {:margin "auto" - :z-index 2} - (get-in parts [:child-container :style]) - (when wrap-nicely? {:background-color "white" - :padding "16px" - :border-radius "6px"}))} - (get-in parts [:child-container :attr])) - child]])) + (let [cmerger (merge-css modal-panel-css-spec args)] + [:div ;; Containing div + (merge (flatten-attr (cmerger :main)) + (->attr args) + attr) + [:div ;; Backdrop + (flatten-attr + (cmerger :backdrop {:color backdrop-color :opacity backdrop-opacity + :attr {:on-click (handler-fn (when backdrop-on-click (backdrop-on-click)) + (.preventDefault event) + (.stopPropagation event))}}))] + [:div ;; Child container + (cmerger :child-container {:wrap-nicely? wrap-nicely?}) + child]]))) From e7c41782024f4f16593f061544f2bb8d3557f4d7 Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Fri, 2 Dec 2022 06:20:23 -0800 Subject: [PATCH 42/65] Css split out --- src/re_com/slider.cljs | 49 +++++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/src/re_com/slider.cljs b/src/re_com/slider.cljs index 476319ae..f996e6c5 100644 --- a/src/re_com/slider.cljs +++ b/src/re_com/slider.cljs @@ -5,7 +5,7 @@ (:require [re-com.config :refer [include-args-desc?]] [re-com.debug :refer [->attr]] - [re-com.util :refer [deref-or-value px]] + [re-com.util :refer [deref-or-value px add-map-to-hiccup-call merge-css flatten-attr]] [re-com.popover :refer [popover-tooltip]] [re-com.box :refer [h-box v-box box gap line flex-child-style align-style]] [re-com.validate :refer [input-status-type? input-status-types-list regex? string-or-hiccup? css-style? html-attr? parts? @@ -24,6 +24,17 @@ (when include-args-desc? (-> (map :name slider-parts-desc) set))) +(def slider-css-spec + {:main {:class ["rc-slider"] + :style (fn [{:keys [width disabled?]}] + (merge + (flex-child-style "none") + {;:-webkit-appearance "slider-vertical" ;; TODO: Make a :orientation (:horizontal/:vertical) option + ;:writing-mode "bt-lr" ;; Make IE slider vertical + :width (or width "400px") + :cursor (if disabled? "default" "pointer")}))} + :wrapper {:class ["rc-slider-wrapper"]}}) + (def slider-args-desc (when include-args-desc? [{:name :model :required true :type "double | string | r/atom" :validate-fn number-or-string? :description "current value of the slider"} @@ -51,30 +62,24 @@ min (deref-or-value min) max (deref-or-value max) step (deref-or-value step) - disabled? (deref-or-value disabled?)] - [box - :src src - :debug-as (or debug-as (reflect-current-component)) - :class (str "rc-slider-wrapper " (get-in parts [:wrapper :class])) - :style (get-in parts [:wrapper :style] {}) - :attr (get-in parts [:wrapper :attr] {}) - :align :start - :child [:input - (merge - {:class (str "rc-slider " class) - :type "range" - ;:orient "vertical" ;; Make Firefox slider vertical (doesn't work because React ignores it, I think) - :style (merge - (flex-child-style "none") - {;:-webkit-appearance "slider-vertical" ;; TODO: Make a :orientation (:horizontal/:vertical) option - ;:writing-mode "bt-lr" ;; Make IE slider vertical - :width (or width "400px") - :cursor (if disabled? "default" "pointer")} - style) + disabled? (deref-or-value disabled?) + cmerger (merge-css slider-css-spec args)] + (add-map-to-hiccup-call + (cmerger :wrapper) + [box + :src src + :debug-as (or debug-as (reflect-current-component)) + :align :start + :child [:input + (merge + (flatten-attr + (cmerger :main {:width width :disabled? disabled?})) + {:type "range" + ;:orient "vertical" ;; Make Firefox slider vertical (doesn't work because React ignores it, I think) :min min :max max :step step :value model :disabled disabled? :on-change (handler-fn (on-change (js/Number (-> event .-target .-value))))} - attr)]]))) \ No newline at end of file + attr)]])))) From 9dc2047ab9259e8605c2ae79f4d1ffe635bf283e Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Fri, 2 Dec 2022 08:11:01 -0800 Subject: [PATCH 43/65] Css split out --- src/re_com/checkbox.cljs | 56 ++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/src/re_com/checkbox.cljs b/src/re_com/checkbox.cljs index 1e67e57e..d2755535 100644 --- a/src/re_com/checkbox.cljs +++ b/src/re_com/checkbox.cljs @@ -5,7 +5,7 @@ (:require [re-com.debug :refer [->attr]] [re-com.config :refer [include-args-desc?]] - [re-com.util :refer [deref-or-value px]] + [re-com.util :refer [deref-or-value px add-map-to-hiccup-call merge-css flatten-attr]] [re-com.popover :refer [popover-tooltip]] [re-com.box :refer [h-box v-box box gap line flex-child-style align-style]] [re-com.validate :refer [input-status-type? input-status-types-list regex? string-or-hiccup? css-style? html-attr? parts? @@ -26,6 +26,16 @@ (when include-args-desc? (-> (map :name checkbox-parts-desc) set))) +(def checkbox-css-spec + {:wrapper {:class ["rc-checkbox-wrapper" "noselect"]} + :main {:class ["rc-checkbox"] + :style (merge (flex-child-style "none") + {:cursor "default"})} + :label {:class ["rc-checkbox-label"] + :style (merge (flex-child-style "none") + {:padding-left "8px" + :cursor "default"})}}) + (def checkbox-args-desc (when include-args-desc? [{:name :model :required true :type "boolean | r/atom" :description "holds state of the checkbox when it is called"} @@ -51,31 +61,27 @@ model (deref-or-value model) disabled? (deref-or-value disabled?) callback-fn #(when (and on-change (not disabled?)) - (on-change (not model)))] ;; call on-change with either true or false - [h-box - :src src - :debug-as (or debug-as (reflect-current-component)) - :class (str "noselect rc-checkbox-wrapper " (get-in parts [:wrapper :class])) - :style (get-in parts [:wrapper :style]) - :attr (get-in parts [:wrapper :attr]) - :align :start - :children [[:input - (merge - {:class (str "rc-checkbox " class) - :type "checkbox" - :style (merge (flex-child-style "none") - {:cursor cursor} - style) + (on-change (not model))) ;; call on-change with either true or false + cmerger (merge-css checkbox-css-spec args)] + (add-map-to-hiccup-call + (cmerger :wrapper) + [h-box + :src src + :debug-as (or debug-as (reflect-current-component)) + :align :start + :children [[:input + (merge + (flatten-attr (cmerger :main)) + {:type "checkbox" :disabled disabled? :checked (boolean model) :on-change (handler-fn (callback-fn))} attr)] - (when label - [:span - {:class (str "rc-checkbox-label " label-class) - :style (merge (flex-child-style "none") - {:padding-left "8px" - :cursor cursor} - label-style) - :on-click (handler-fn (callback-fn))} - label])]]))) \ No newline at end of file + (when label + [:span + (flatten-attr + (cmerger :label + {:class label-class + :style label-style + :attr {:on-click (handler-fn (callback-fn))}})) + label])]])))) From a851cbb49122d74b8554c3bde1c9f72f7fc5e84a Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Fri, 2 Dec 2022 09:48:22 -0800 Subject: [PATCH 44/65] Css split out --- src/re_com/progress_bar.cljs | 55 +++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/src/re_com/progress_bar.cljs b/src/re_com/progress_bar.cljs index cd365048..c97f1810 100644 --- a/src/re_com/progress_bar.cljs +++ b/src/re_com/progress_bar.cljs @@ -5,7 +5,7 @@ (:require [re-com.config :refer [include-args-desc?]] [re-com.debug :refer [->attr]] - [re-com.util :refer [deref-or-value px]] + [re-com.util :refer [deref-or-value px add-map-to-hiccup-call merge-css flatten-attr]] [re-com.popover :refer [popover-tooltip]] [re-com.box :refer [h-box v-box box gap line flex-child-style align-style]] [re-com.validate :refer [input-status-type? input-status-types-list regex? string-or-hiccup? css-style? html-attr? parts? @@ -26,6 +26,19 @@ (when include-args-desc? (-> (map :name progress-bar-parts-desc) set))) +(def progress-bar-css-spec + {:wrapper {:class ["rc-progress-bar-wrapper"]} + :main {:class ["rc-progress-bar" "progress"] + :style (fn [{:keys [width]}] + (merge (flex-child-style "none") + {:width (or width "100%")}))} + :portion {:class (fn [{:keys [striped?]}] + ["progress-bar" "active" "rc-progress-bar-portion" + (when striped? "progress-bar-striped")]) + :style (fn [{:keys [percent]}] + {:width (str percent "%") + :transition "none"})}}) ;; Default BS transitions cause the progress bar to lag behind + (def progress-bar-args-desc (when include-args-desc? [{:name :model :required true :type "double | string | r/atom" :validate-fn number-or-string? :description "current value of the slider. A number between 0 and 100"} @@ -42,28 +55,26 @@ (defn progress-bar "Render a bootstrap styled progress bar" [& {:keys [model width striped? class bar-class style attr parts src debug-as] - :or {width "100%"} :as args}] (or (validate-args-macro progress-bar-args-desc args) - (let [model (deref-or-value model)] - [box - :src src - :debug-as (or debug-as (reflect-current-component)) - :class (str "rc-progress-bar-wrapper " (get-in parts [:wrapper :class])) - :style (get-in parts [:wrapper :style]) - :attr (get-in parts [:wrapper :attr]) - :align :start - :child [:div - (merge - {:class (str "progress rc-progress-bar " class) - :style (merge (flex-child-style "none") - {:width width} - style)} + (let [model (deref-or-value model) + cmerger (merge-css progress-bar-css-spec args)] + (add-map-to-hiccup-call + (cmerger :wrapper) + [box + :src src + :debug-as (or debug-as (reflect-current-component)) + :align :start + :child [:div + (merge + (flatten-attr + (cmerger :main {:width width})) attr) - [:div - {:class (str "progress-bar " (when striped? "progress-bar-striped active rc-progress-bar-portion ") bar-class) - :role "progressbar" - :style {:width (str model "%") - :transition "none"}} ;; Default BS transitions cause the progress bar to lag behind - (str model "%")]]]))) + [:div + (flatten-attr + (cmerger :portion + {:striped? striped? + :percent model + :attr {:role "progressbar"}})) + (str model "%")]]])))) From 159f0dfa6efffc65d511e985e4c443a458822b07 Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Fri, 2 Dec 2022 10:05:22 -0800 Subject: [PATCH 45/65] Css split out --- src/re_com/throbber.cljs | 57 ++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/src/re_com/throbber.cljs b/src/re_com/throbber.cljs index 37fa5778..26232180 100644 --- a/src/re_com/throbber.cljs +++ b/src/re_com/throbber.cljs @@ -5,7 +5,7 @@ (:require [re-com.config :refer [include-args-desc?]] [re-com.debug :refer [->attr]] - [re-com.util :refer [deref-or-value px]] + [re-com.util :refer [deref-or-value px add-map-to-hiccup-call merge-css flatten-attr]] [re-com.popover :refer [popover-tooltip]] [re-com.box :refer [h-box v-box box gap line flex-child-style align-style]] [re-com.validate :refer [input-status-type? input-status-types-list regex? string-or-hiccup? css-style? html-attr? parts? @@ -25,6 +25,20 @@ (when include-args-desc? (-> (map :name throbber-parts-desc) set))) +(def throbber-css-spec + {:wrapper {:class ["rc-throbber-wrapper"]} + :main {:class (fn [{:keys [size]}] + ["loader" "rc-throbber" + (case size + :regular nil + :smaller "smaller" + :small "small" + :large "large" + nil)])} + :segment {:class ["rc-throbber-segment"] + :style (fn [{:keys [color]}] + (when color {:background-color color}))}}) + (def throbber-args-desc (when include-args-desc? [{:name :size :required false :default :regular :type "keyword" :validate-fn throbber-size? :description [:span "one of " throbber-sizes-list]} @@ -41,30 +55,17 @@ [& {:keys [size color class style attr parts src debug-as] :as args}] (or (validate-args-macro throbber-args-desc args) - (let [seg (fn [] - [:li - (merge - {:class (str "rc-throbber-segment " (get-in parts [:segment :class])) - :style (merge - (when color {:background-color color}) - (get-in parts [:segment :style]))} - (get-in parts [:segment :attr]))])] - [box - :src src - :debug-as (or debug-as (reflect-current-component)) - :class (str "rc-throbber-wrapper " (get-in parts [:wrapper :class])) - :style (get-in parts [:wrapper :style]) - :attr (get-in parts [:wrapper :attr]) - :align :start - :child [:ul - (merge {:class (str "loader rc-throbber " - (case size :regular "" - :smaller "smaller " - :small "small " - :large "large " - "") - class) - :style style} - attr) - [seg] [seg] [seg] [seg] - [seg] [seg] [seg] [seg]]]))) ;; Each :li element in [seg] represents one of the eight circles in the throbber \ No newline at end of file + (let [cmerger (merge-css throbber-css-spec args) + seg (fn [] + [:li (cmerger :segment {:color color})])] + (add-map-to-hiccup-call + (cmerger :wrapper) + [box + :src src + :debug-as (or debug-as (reflect-current-component)) + :align :start + :child [:ul + (merge (cmerger :main {:size size}) + attr) + [seg] [seg] [seg] [seg] + [seg] [seg] [seg] [seg]]])))) ;; Each :li element in [seg] represents one of the eight circles in the throbber From 9cfd9d16d9c670048c85069caf4c0c4853695168 Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Fri, 2 Dec 2022 11:26:54 -0800 Subject: [PATCH 46/65] Css split out --- src/re_com/tour.cljs | 44 +++++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/src/re_com/tour.cljs b/src/re_com/tour.cljs index 4774931a..6994bd18 100644 --- a/src/re_com/tour.cljs +++ b/src/re_com/tour.cljs @@ -4,7 +4,8 @@ (:require [reagent.core :as reagent] [re-com.box :refer [flex-child-style]] - [re-com.buttons :refer [button]])) + [re-com.buttons :refer [button]] + [re-com.util :refer [add-map-to-hiccup-call merge-css flatten-attr]])) ;;-------------------------------------------------------------------------------------------------- @@ -13,6 +14,14 @@ ;; Strings together ;;-------------------------------------------------------------------------------------------------- +(def tour-css-spec + {:line {:style (merge (flex-child-style "none") + {:margin "10px 0px 10px"})} + :prev-button {:class ["btn-default" "rc-tour-btn-previous"] + :style {:margin-right "15px"}} + :next-button {:class (fn [{:keys [last-button?]}] + ["btn-default" (if last-button? "rc-tour-btn-finish" "rc-tour-btn-next")])}}) + (defn make-tour "Returns a map containing - A reagent atom for each tour step controlling popover show/hide (boolean) @@ -79,21 +88,22 @@ If last button in tour, change Next button to a Finish button" [tour] (let [on-first-button (= @(:current-step tour) 0) - on-last-button (= @(:current-step tour) (dec (count (:steps tour))))] + on-last-button (= @(:current-step tour) (dec (count (:steps tour)))) + cmerger (merge-css tour-css-spec {})] [:div - [:hr {:style (merge (flex-child-style "none") - {:margin "10px 0px 10px"})}] + [:hr (flatten-attr (cmerger :line))] (when-not on-first-button - [button - :src (at) - :label "Previous" - :on-click (handler-fn (prev-tour-step tour)) - :style {:margin-right "15px"} - :class "btn-default rc-tour-btn-previous"]) - [button - :src (at) - :label (if on-last-button "Finish" "Next") - :on-click (handler-fn (if on-last-button - (finish-tour tour) - (next-tour-step tour))) - :class (str "btn-default " (if on-last-button "rc-tour-btn-finish" "rc-tour-btn-next"))]])) + (add-map-to-hiccup-call + (cmerger :prev-button) + [button + :src (at) + :label "Previous" + :on-click (handler-fn (prev-tour-step tour))])) + (add-map-to-hiccup-call + (cmerger :next-button {:last-button? on-last-button}) + [button + :src (at) + :label (if on-last-button "Finish" "Next") + :on-click (handler-fn (if on-last-button + (finish-tour tour) + (next-tour-step tour)))])])) From 96815f6eac24f2d7361a6a398b12198576b642f6 Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Tue, 6 Dec 2022 09:54:46 -0800 Subject: [PATCH 47/65] Tests running - box-base needed some parameter adjustment - Class spec comes out as vector now, not string --- src/re_com/box.cljs | 4 ++-- test/re_com/box_test.cljs | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/re_com/box.cljs b/src/re_com/box.cljs index 22b3b294..e5aad3d8 100644 --- a/src/re_com/box.cljs +++ b/src/re_com/box.cljs @@ -156,12 +156,12 @@ (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] + margin padding border l-border r-border t-border b-border radius bk-color child class style attr] :as args}] (let [cmerger (merge-css box-base-css-spec args)] [:div (merge - (flatten-attr (cmerger :main args)) + (flatten-attr (cmerger :main (dissoc args :class :style :attr))) (->attr args) attr) child])) diff --git a/test/re_com/box_test.cljs b/test/re_com/box_test.cljs index 47f7cf46..54ef1c52 100644 --- a/test/re_com/box_test.cljs +++ b/test/re_com/box_test.cljs @@ -54,7 +54,7 @@ (deftest test-gap (are [expected actual] (= expected actual) [:div - {:class "rc-gap my-gap" + {:class ["rc-gap" "my-gap"] :style {:flex "0 0 1px" :-webkit-flex "0 0 1px"} :id "my-id"}] @@ -63,7 +63,7 @@ (deftest test-line (are [expected actual] (= expected actual) [:div - {:class "rc-line my-line" + {:class ["rc-line" "my-line"] :style {:flex "0 0 1px" :-webkit-flex "0 0 1px" :background-color "lightgray"} @@ -73,7 +73,7 @@ (deftest test-box (are [expected actual] (= expected actual) [:div - {:class "rc-box display-flex my-box" + {:class ["display-flex" "rc-box" "my-box"] :style {:flex "none" :-webkit-flex "none" :flex-flow "inherit" @@ -85,7 +85,7 @@ (deftest test-scroller (are [expected actual] (= expected actual) [:div - {:class "rc-scroller display-flex my-scroller" + {:class ["display-flex" "rc-scroller" "my-scroller"] :style {:flex "auto" :-webkit-flex "auto" :flex-flow "inherit" @@ -98,7 +98,7 @@ (deftest test-border (are [expected actual] (= expected actual) [:div - {:class "rc-border display-flex my-border" + {:class ["display-flex" "rc-border" "my-border"] :style {:flex "none" :-webkit-flex "none" :flex-flow "inherit" From 0545d6c2875c34f2492622ffb1ca51bc0001ea69 Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Wed, 7 Dec 2022 11:50:57 -0800 Subject: [PATCH 48/65] Change merge-css to handle toplevel :attr - Started with handling left manual, but this is inconsistent and hard to explain --- src/re_com/alert.cljs | 3 +-- src/re_com/box.cljs | 15 +++++---------- src/re_com/buttons.cljs | 21 +++++++-------------- src/re_com/checkbox.cljs | 3 +-- src/re_com/close_button.cljs | 6 ++---- src/re_com/datepicker.cljs | 4 +--- src/re_com/dropdown.cljs | 3 +-- src/re_com/input_time.cljs | 3 +-- src/re_com/modal_panel.cljs | 3 +-- src/re_com/multi_select.cljs | 3 +-- src/re_com/popover.cljs | 15 +++++---------- src/re_com/progress_bar.cljs | 6 ++---- src/re_com/radio_button.cljs | 3 +-- src/re_com/slider.cljs | 3 +-- src/re_com/tabs.cljs | 3 +-- src/re_com/text.cljs | 6 ++---- src/re_com/throbber.cljs | 3 +-- src/re_com/util.cljs | 4 ++-- 18 files changed, 36 insertions(+), 71 deletions(-) diff --git a/src/re_com/alert.cljs b/src/re_com/alert.cljs index adfe6884..e1fcf76d 100644 --- a/src/re_com/alert.cljs +++ b/src/re_com/alert.cljs @@ -82,8 +82,7 @@ [:div (merge (flatten-attr (cmerger :main {:alert-type alert-type :padding padding})) - (->attr args) - attr) + (->attr args)) (when heading (add-map-to-hiccup-call (cmerger :heading {:body body}) diff --git a/src/re_com/box.cljs b/src/re_com/box.cljs index e5aad3d8..05078689 100644 --- a/src/re_com/box.cljs +++ b/src/re_com/box.cljs @@ -162,8 +162,7 @@ [:div (merge (flatten-attr (cmerger :main (dissoc args :class :style :attr))) - (->attr args) - attr) + (->attr args)) child])) @@ -201,8 +200,7 @@ [:div (merge (->attr args) - (flatten-attr (cmerger :main {:size size :width width :height height})) - attr)]))) + (flatten-attr (cmerger :main {:size size :width width :height height})))]))) ;; ------------------------------------------------------------------------------------ @@ -237,8 +235,7 @@ [:div (merge (->attr args) - (cmerger :main {:size size :color color}) - attr)]))) + (cmerger :main {:size size :color color}))]))) ;; ------------------------------------------------------------------------------------ @@ -306,8 +303,7 @@ (into [:div (merge (->attr args) - (flatten-attr (cmerger :main args)) - attr)] + (flatten-attr (cmerger :main args)))] children)))) ;; ------------------------------------------------------------------------------------ @@ -374,8 +370,7 @@ (into [:div (merge (->attr args) - (flatten-attr (cmerger :main args)) - attr)] + (flatten-attr (cmerger :main args)))] children)))) diff --git a/src/re_com/buttons.cljs b/src/re_com/buttons.cljs index 4d1c3a06..a3a989c7 100644 --- a/src/re_com/buttons.cljs +++ b/src/re_com/buttons.cljs @@ -68,8 +68,7 @@ (on-click event)))} (when tooltip {:on-mouse-over (handler-fn (reset! showing? true)) - :on-mouse-out (handler-fn (reset! showing? false))}) - attr) + :on-mouse-out (handler-fn (reset! showing? false))})) label]] (when disabled? (reset! showing? false)) @@ -166,8 +165,7 @@ (on-click event)))} (when tooltip {:on-mouse-over (handler-fn (reset! showing? true)) - :on-mouse-out (handler-fn (reset! showing? false))}) - attr) + :on-mouse-out (handler-fn (reset! showing? false))})) [:i (flatten-attr (cmerger :icon {:md-icon-name md-icon-name}))]]] (add-map-to-hiccup-call (cmerger :wrapper) @@ -262,8 +260,7 @@ (on-click event)))} (when tooltip {:on-mouse-over (handler-fn (reset! showing? true)) - :on-mouse-out (handler-fn (reset! showing? false))}) - attr) + :on-mouse-out (handler-fn (reset! showing? false))})) [:i (cmerger :icon {:md-icon-name md-icon-name})]]] (add-map-to-hiccup-call (cmerger :wrapper) @@ -348,8 +345,7 @@ (cmerger :main {:disabled? disabled?})) {:on-click (handler-fn (when (not disabled?) - (swap! showing? not)))} - attr) + (swap! showing? not)))}) [:svg (merge (flatten-attr (cmerger :icon)) @@ -422,8 +418,7 @@ (on-click event)))} (when tooltip {:on-mouse-over (handler-fn (reset! showing? true)) - :on-mouse-out (handler-fn (reset! showing? false))}) ;; Need to return true to ALLOW default events to be performed - attr) + :on-mouse-out (handler-fn (reset! showing? false))})) ;; Need to return true to ALLOW default events to be performed [:i (flatten-attr (cmerger :icon {:md-icon-name md-icon-name}))]]] (add-map-to-hiccup-call (cmerger :wrapper) @@ -514,8 +509,7 @@ (on-click event)))} (when tooltip {:on-mouse-over (handler-fn (reset! showing? true)) - :on-mouse-out (handler-fn (reset! showing? false))}) - attr) + :on-mouse-out (handler-fn (reset! showing? false))})) label]])] (add-map-to-hiccup-call (cmerger :wrapper) @@ -606,8 +600,7 @@ {:href href}) (when tooltip {:on-mouse-over (handler-fn (reset! showing? true)) - :on-mouse-out (handler-fn (reset! showing? false))}) - attr) + :on-mouse-out (handler-fn (reset! showing? false))})) label]] (add-map-to-hiccup-call diff --git a/src/re_com/checkbox.cljs b/src/re_com/checkbox.cljs index d2755535..328ac2d3 100644 --- a/src/re_com/checkbox.cljs +++ b/src/re_com/checkbox.cljs @@ -75,8 +75,7 @@ {:type "checkbox" :disabled disabled? :checked (boolean model) - :on-change (handler-fn (callback-fn))} - attr)] + :on-change (handler-fn (callback-fn))})] (when label [:span (flatten-attr diff --git a/src/re_com/close_button.cljs b/src/re_com/close_button.cljs index b735809e..54b3de0b 100644 --- a/src/re_com/close_button.cljs +++ b/src/re_com/close_button.cljs @@ -90,15 +90,13 @@ :left-offset left-offset :color color :hover-color hover-color - :attr (merge - {:title tooltip + :attr {:title tooltip :on-click (handler-fn (when (and on-click (not disabled?)) (on-click event) (.stopPropagation event))) :on-mouse-enter (handler-fn (reset! over? true)) - :on-mouse-leave (handler-fn (reset! over? false))} - attr)}) + :on-mouse-leave (handler-fn (reset! over? false))}}) [box :src (at) :child [:i diff --git a/src/re_com/datepicker.cljs b/src/re_com/datepicker.cljs index 4acfb6ac..d9bd2856 100644 --- a/src/re_com/datepicker.cljs +++ b/src/re_com/datepicker.cljs @@ -179,9 +179,7 @@ :size "none" :border (when hide-border? "none") :child [:div - (merge - (flatten-attr (cmerger :main)) - attr) + (flatten-attr (cmerger :main)) table-div]])]])) (defn- prev-year-icon diff --git a/src/re_com/dropdown.cljs b/src/re_com/dropdown.cljs index 29f243ba..61aeae91 100644 --- a/src/re_com/dropdown.cljs +++ b/src/re_com/dropdown.cljs @@ -677,8 +677,7 @@ (when tooltip {:on-mouse-over (handler-fn (reset! over? true)) :on-mouse-out (handler-fn (reset! over? false))}) - (->attr args) - attr) + (->attr args)) (cond just-drop? nil free-text? [free-text-dropdown-top free-text-input select-free-text? free-text-focused? free-text-sel-range internal-model tab-index placeholder dropdown-click key-handler filter-box? drop-showing? cancel width free-text-change auto-complete? choices capitalize? disabled?] diff --git a/src/re_com/input_time.cljs b/src/re_com/input_time.cljs index 90e1796c..7fe7f762 100644 --- a/src/re_com/input_time.cljs +++ b/src/re_com/input_time.cljs @@ -228,8 +228,7 @@ :disabled (deref-or-value disabled?) :on-change (handler-fn (on-new-keypress event text-model)) :on-blur (handler-fn (on-defocus text-model minimum maximum on-change @previous-model)) - :on-key-up (handler-fn (lose-focus-if-enter event))} - attr)] + :on-key-up (handler-fn (lose-focus-if-enter event))})] (when show-icon? ;; Leaving time-icon class (below) for backwards compatibility only. [:div diff --git a/src/re_com/modal_panel.cljs b/src/re_com/modal_panel.cljs index ac538ca1..8112e70a 100644 --- a/src/re_com/modal_panel.cljs +++ b/src/re_com/modal_panel.cljs @@ -76,8 +76,7 @@ (let [cmerger (merge-css modal-panel-css-spec args)] [:div ;; Containing div (merge (flatten-attr (cmerger :main)) - (->attr args) - attr) + (->attr args)) [:div ;; Backdrop (flatten-attr (cmerger :backdrop {:color backdrop-color :opacity backdrop-opacity diff --git a/src/re_com/multi_select.cljs b/src/re_com/multi_select.cljs index 72ca68e1..c3863173 100644 --- a/src/re_com/multi_select.cljs +++ b/src/re_com/multi_select.cljs @@ -519,8 +519,7 @@ [:div (merge (flatten-attr (cmerger :main {:width width})) - (->attr args) - attr) ;; Prevent user text selection + (->attr args)) ;; Prevent user text selection (add-map-to-hiccup-call (cmerger :container) [box/h-box diff --git a/src/re_com/popover.cljs b/src/re_com/popover.cljs index 2207bb06..8ee8dd4e 100644 --- a/src/re_com/popover.cljs +++ b/src/re_com/popover.cljs @@ -173,8 +173,7 @@ (flatten-attr (cmerger :main {:attr {:on-click (handler-fn (on-click))}})) - (->attr args) - attr)]))) + (->attr args))]))) ;;-------------------------------------------------------------------------------------------------- @@ -223,8 +222,7 @@ [:h3 (merge (flatten-attr (cmerger :main)) - (->attr args) - attr) + (->attr args)) (add-map-to-hiccup-call (cmerger :container) [h-box @@ -410,8 +408,7 @@ :border-color popover-border-color :tooltip-style? tooltip-style? :orientation orientation :margin-left margin-left :margin-top margin-top :ready-to-show? @ready-to-show?}))) - (->attr args) - attr) + (->attr args)) [popover-arrow orientation @pop-offset arrow-length arrow-width grey-arrow? tooltip-style? popover-color popover-border-color parts] (when title title) (into [:div @@ -516,8 +513,7 @@ (cmerger :main {:no-clip? no-clip? :left-offset @left-offset :top-offset @top-offset})) - (->attr args) - attr) + (->attr args)) (when (and (deref-or-value showing-injected?) on-cancel) (add-map-to-hiccup-call (cmerger :backdrop) @@ -619,8 +615,7 @@ cmerger (merge-css popover-anchor-wrapper-css-spec args)] [:div (merge (flatten-attr (cmerger :main)) - (->attr args) - attr) + (->attr args)) [:div ;; Wrapper around the anchor and the "point" (flatten-attr (cmerger :point-wrapper {:flex-flow flex-flow})) diff --git a/src/re_com/progress_bar.cljs b/src/re_com/progress_bar.cljs index c97f1810..387e0180 100644 --- a/src/re_com/progress_bar.cljs +++ b/src/re_com/progress_bar.cljs @@ -67,10 +67,8 @@ :debug-as (or debug-as (reflect-current-component)) :align :start :child [:div - (merge - (flatten-attr - (cmerger :main {:width width})) - attr) + (flatten-attr + (cmerger :main {:width width})) [:div (flatten-attr (cmerger :portion diff --git a/src/re_com/radio_button.cljs b/src/re_com/radio_button.cljs index 24e07116..caf85b50 100644 --- a/src/re_com/radio_button.cljs +++ b/src/re_com/radio_button.cljs @@ -76,8 +76,7 @@ {:type "radio" :disabled disabled? :checked (= model value) - :on-change (handler-fn (callback-fn))} - attr)] + :on-change (handler-fn (callback-fn))})] (when label [:span (flatten-attr diff --git a/src/re_com/slider.cljs b/src/re_com/slider.cljs index f996e6c5..1f338730 100644 --- a/src/re_com/slider.cljs +++ b/src/re_com/slider.cljs @@ -81,5 +81,4 @@ :step step :value model :disabled disabled? - :on-change (handler-fn (on-change (js/Number (-> event .-target .-value))))} - attr)]])))) + :on-change (handler-fn (on-change (js/Number (-> event .-target .-value))))})]])))) diff --git a/src/re_com/tabs.cljs b/src/re_com/tabs.cljs index 259e9fda..b50bca97 100644 --- a/src/re_com/tabs.cljs +++ b/src/re_com/tabs.cljs @@ -65,8 +65,7 @@ [:ul (merge (flatten-attr (cmerger :wrapper)) - (->attr args) - attr) + (->attr args)) (for [t tabs] (let [id (id-fn t) label (label-fn t) diff --git a/src/re_com/text.cljs b/src/re_com/text.cljs index 55c5e9da..4fc50e34 100644 --- a/src/re_com/text.cljs +++ b/src/re_com/text.cljs @@ -58,8 +58,7 @@ (merge (cmerger :main) (when on-click - {:on-click (handler-fn (on-click))}) - attr) + {:on-click (handler-fn (on-click))})) label]])))) @@ -121,8 +120,7 @@ :debug-as (or debug-as (reflect-current-component)) :children [[:span (merge (flatten-attr (cmerger :main {:level level :underline? underline? - :margin-top margin-top :margin-bottom margin-bottom})) - attr) + :margin-top margin-top :margin-bottom margin-bottom}))) label] (when underline? (add-map-to-hiccup-call (cmerger :underline {:margin-bottom margin-bottom}) diff --git a/src/re_com/throbber.cljs b/src/re_com/throbber.cljs index 26232180..52dc2067 100644 --- a/src/re_com/throbber.cljs +++ b/src/re_com/throbber.cljs @@ -65,7 +65,6 @@ :debug-as (or debug-as (reflect-current-component)) :align :start :child [:ul - (merge (cmerger :main {:size size}) - attr) + (cmerger :main {:size size}) [seg] [seg] [seg] [seg] [seg] [seg] [seg] [seg]]])))) ;; Each :li element in [seg] represents one of the eight circles in the throbber diff --git a/src/re_com/util.cljs b/src/re_com/util.cljs index 65264254..cbc54e47 100644 --- a/src/re_com/util.cljs +++ b/src/re_com/util.cljs @@ -225,7 +225,7 @@ (do (when cond (println itm)) itm)) -(defn merge-css [css-desc {:as params :keys [class style parts]}] +(defn merge-css [css-desc {:as params :keys [class style attr parts]}] (for [[k v] css-desc :when (not (and (keyword? k) (map? v)))] (throw (js/Error. "CSS description must contain only keywords and maps"))) @@ -251,7 +251,7 @@ defaults (get css-desc (or tag :main)) use-toplevel (get :use-toplevel defaults (if (= tag :main) true false)) user (combine-css (get parts tag) - (and use-toplevel {:class class :style style})) + (and use-toplevel {:class class :style style :attr attr})) defaults (into {} (for [k [:class :style :attr] :when (contains? defaults k) :let [v (get defaults k)]] From 3995e4e5307ad2f2f86af27bb5e8aa6e70ad52ae Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Wed, 7 Dec 2022 11:52:48 -0800 Subject: [PATCH 49/65] Bugfix --- src/re_com/buttons.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/re_com/buttons.cljs b/src/re_com/buttons.cljs index a3a989c7..7393de71 100644 --- a/src/re_com/buttons.cljs +++ b/src/re_com/buttons.cljs @@ -215,7 +215,7 @@ :tooltip {:class ["rc-md-icon-button-tooltip"]} :icon {:class (fn [{:keys [md-icon-name]}] - ["zmdi" "zmdi-hc-fw-rc" md-icon-name "rc-md-circle-icon-button-icon"])} + ["zmdi" "zmdi-hc-fw-rc" md-icon-name "rc-md-icon-button-icon"])} }) (def md-icon-button-parts From c4260c6164efaff3bd85c2e903b5a161ed676a9d Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Wed, 7 Dec 2022 15:39:43 -0800 Subject: [PATCH 50/65] Bugfix --- src/re_com/buttons.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/re_com/buttons.cljs b/src/re_com/buttons.cljs index 7393de71..de0e9fe2 100644 --- a/src/re_com/buttons.cljs +++ b/src/re_com/buttons.cljs @@ -250,7 +250,7 @@ (validate-args-macro md-icon-button-args-desc args) (do (when-not tooltip (reset! showing? false)) ;; To prevent tooltip from still showing after button drag/drop - (let [cmerger (merge-css md-circle-icon-button-css-spec args) + (let [cmerger (merge-css md-icon-button-css-spec args) the-button [:div (merge (flatten-attr From 810a062b4efbf7fc9721a603c9f3351d6b73ec93 Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Fri, 9 Dec 2022 09:57:00 -0800 Subject: [PATCH 51/65] Bugfix --- src/re_com/box.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/re_com/box.cljs b/src/re_com/box.cljs index 05078689..edb85dde 100644 --- a/src/re_com/box.cljs +++ b/src/re_com/box.cljs @@ -235,7 +235,7 @@ [:div (merge (->attr args) - (cmerger :main {:size size :color color}))]))) + (flatten-attr (cmerger :main {:size size :color color})))]))) ;; ------------------------------------------------------------------------------------ From 488e0666ed2487ebe1f4e8a74574c3c09dc7298b Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Mon, 12 Dec 2022 10:02:11 -0800 Subject: [PATCH 52/65] Documentation --- src/re_com/util.cljs | 82 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 73 insertions(+), 9 deletions(-) diff --git a/src/re_com/util.cljs b/src/re_com/util.cljs index cbc54e47..e77472eb 100644 --- a/src/re_com/util.cljs +++ b/src/re_com/util.cljs @@ -221,9 +221,78 @@ 0 0 0 0))) -(defn say-when [cond itm] - (do (when cond (println itm)) - itm)) + +;; Merge-css - a tool for handling the css that comes into re-com components +;; +;; # Rationale +;; +;; Hard coded CSS is bad. Flexibility is good. Re-com has a few ways to keep the CSS flexible. For the end user, an extended 'parts' structure can be passed into components that allows :class :style and :attr of most elements in the component to be addressed. This is on top of the main :class, :style, and :attr that most components receive. Another method is meant for internal re-com use. It's a structure similar to 'parts', and generally stored with a *-css-spec name. This is meant to give access to serious re-com modifiers who want to use another CSS framework or no CSS at all. It is a place to put CSS values that would normally be hard coded into the components. +;; +;; Merge-css is the tool to pull all of the sources of styling together and apply them appropriately. +;; +;; # Basic Anatomy +;; +;; Merge-css takes two parameters and returns a closure. The first parameter is the *-css-spec for the component in operation. The second is a map containing parameters. Merge-css will be looking for :class, :style, :attr, and :parts keys in this map. Typically, the component's `args` will be passed in as this parameters map. +;; +;; The closure returned by merge-css, conventionally stored as `cmerger`, takes two parameters. The first is a keyword designating the section to retrieve from the *-css-spec and the `parts` structures. The second is a parameters map. The keys :class, :style, and :attr will be extracted from this map and applied as last minute hard coded overrides to the css. The remainder of the map will be passed as run time variables to functions found in the *-css-spec structure. +;; +;; # Components vs. Elements +;; +;; Re-com components, as called from hiccup, do not take options the same way that HTML elements do. The former takes :class, :style and :attr parameters directly, where the latter takes a map of attributes containing :class and :style as the first parameter. The return value from `cmerger` - a map containing :class, :style and :attr - is not directly usable by either. A special utility is available for each case. +;; +;; Components: +;; +;; > (add-map-to-hiccup-call +;; > (cmerger :some-place {:optional :options...}) +;; > [my-component +;; > :id 101 +;; > etc... +;; +;; The hiccup 'call' to my-component will be rewritten to have :class, :style and :attr options added to it from the map supplied by `cmerger`. Note: results are undefined if you put :class, etc. into [my-component ...] by hand. Add-map-to-hiccup-call will not check for existing keys! +;; +;; Elements: +;; +;; > [:div +;; > (flatten-attr (cmerger :some-place)) ;;No options this time +;; > ... +;; +;; Because the contents of :attr from `cmerger` belong directly in first parameter of the :div element, flatten-attr will get rid of the :attr key and place its contents in the toplevel of the returned map. +;; +;; What if you want to add something - say, an event handler - to the attributes of the :div? This can be done in two ways: +;; +;; > [:div +;; > (flatten-attr (cmerger :some-place +;; > {:attr {:on-squirm ...}})) +;; > ... +;; +;; +;; > [:div +;; > (merge +;; > (flatten-attr (cmerger :some-place)) +;; > {:on-squirm ...}) +;; > ... +;; +;; ## Fixme: other component parameters +;; +;; Currently there is no way to specify other parameters for re-com components. The goal is to have all hard coded CSS values be stored in -css-spec structures where they can be replaced easily. At first glance, the :attr section may seem to be the place for these other parameters, but it specifically addresses the attributes of the main internal element of the component. Another section for other parameters and variables may need to be added. +;; +;; # The -css-spec structure +;; +;; A css-spec structure will take the following form: +;; +;; > (def button-css-spec +;; > {:main {:class ["rc-button" "btn"] +;; > :style {:background-color ... } +;; > :wrapper {:class (fn [{:keys [... ]}] +;; > ...)} +;; > ...}) +;; +;; At root it is a map, with a key for each section or element in the component. The key names should line up with the keys in the -parts-desc structure whereever possible. Each section is a map that may contain a :class, :style and an :attr object. The class spec should be a vector or a function. The others should be a map or a function. The function should return, respectively, a vector or a map. The function will receive the 'other' parameters mentioned above in the discussion of the `cmerger` closure. +;; +;; ## The :main key +;; +;; The :main key is a special case. Most re-com components receive a :parts parameter for fine-grained css control. But they also receive a simpler set of :class, :style and :attr components. What happens to these? They are generally directed toward the element that the end user will perceive as being the central one. The :main one, in other words. So merge-css will take these toplevel CSS parameters and apply them to the request named :main. The :use-toplevel key, placed in the :main section of the -css-spec structure with a `false` value, will suppress this behavior. Placed in another section - say, :wrapper - with a `true` value, it will cause that element to receive the toplevel user CSS parameters. + (defn merge-css [css-desc {:as params :keys [class style attr parts]}] (for [[k v] css-desc @@ -274,9 +343,4 @@ (rest hiccup)]) (meta hiccup))) -(defn say-hiccup [itm] - (println "SOURCE:") - (println itm) - (println "HTML:") - (println (render-to-string itm)) - itm) + From de21d6e42a6da79b05d81f339fd43f0bfc3537b8 Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Thu, 26 Jan 2023 06:10:15 -0800 Subject: [PATCH 53/65] Pass vertical? to buttons --- src/re_com/tabs.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/re_com/tabs.cljs b/src/re_com/tabs.cljs index b50bca97..3c8ca6ac 100644 --- a/src/re_com/tabs.cljs +++ b/src/re_com/tabs.cljs @@ -137,7 +137,7 @@ selected? (= id current) the-button [:button (merge - (cmerger :button {:selected? selected?}) + (cmerger :button {:selected? selected? :vertical? vertical?}) {:type "button" :key (str id) :on-click (when on-change (handler-fn (on-change id)))} From 99714085ad30d3fd24cb1e0df1c9e4e2c2b1b64c Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Wed, 1 Feb 2023 10:15:39 -0800 Subject: [PATCH 54/65] Fixes - Allow vectors in class descriptor for buttons - Pass :selected? through to tab core --- src/re_com/buttons.cljs | 2 +- src/re_com/tabs.cljs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/re_com/buttons.cljs b/src/re_com/buttons.cljs index de0e9fe2..7c063e2d 100644 --- a/src/re_com/buttons.cljs +++ b/src/re_com/buttons.cljs @@ -38,7 +38,7 @@ {:name :tooltip :required false :type "string | hiccup" :validate-fn string-or-hiccup? :description "what to show in the tooltip"} {:name :tooltip-position :required false :default :below-center :type "keyword" :validate-fn position? :description [:span "relative to this anchor. One of " position-options-list]} {:name :disabled? :required false :default false :type "boolean | atom" :description "if true, the user can't click the button"} - {:name :class :required false :type "string" :validate-fn string? :description "CSS class names, space separated (applies to the button, not the wrapping div)"} + {:name :class :required false :type "string | vector" :validate-fn #(or (string? %) (vector? %)) :description "CSS class names, space separated (applies to the button, not the wrapping div)"} {:name :style :required false :type "CSS style map" :validate-fn css-style? :description "CSS styles (applies to the button, not the wrapping div)"} {: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 (applies to the button, not the wrapping div)"]} {:name :parts :required false :type "map" :validate-fn (parts? button-parts) :description "See Parts section below."} diff --git a/src/re_com/tabs.cljs b/src/re_com/tabs.cljs index 3c8ca6ac..25d43874 100644 --- a/src/re_com/tabs.cljs +++ b/src/re_com/tabs.cljs @@ -76,7 +76,8 @@ :attr {:key (str id)}})) [:a (flatten-attr - (cmerger :anchor {:attr + (cmerger :anchor {:selected? selected? + :attr {:on-click (when on-change (handler-fn (on-change id)))}})) label]]))]))) From c1066fc586d83f19568a30f9b567a4bbb4660a00 Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Thu, 2 Feb 2023 07:34:19 -0800 Subject: [PATCH 55/65] Pass more data to css-spec functions --- src/re_com/tabs.cljs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/re_com/tabs.cljs b/src/re_com/tabs.cljs index 25d43874..1d03844a 100644 --- a/src/re_com/tabs.cljs +++ b/src/re_com/tabs.cljs @@ -255,12 +255,13 @@ selected? (= id current)] ;; must use 'current' instead of @model to avoid reagent warnings [:li (flatten-attr - (cmerger :tab {:selected? selected? + (cmerger :tab {:selected? selected? :vertical? vertical? :attr {:key (str id)}})) [:a (flatten-attr (cmerger :anchor - {:attr {:on-click (when on-change (handler-fn (on-change id)))}})) + {:selected? selected? + :attr {:on-click (when on-change (handler-fn (on-change id)))}})) label]]))])) From cf921edaa7efb119427bca5b9de838f05fa2755d Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Sat, 1 Apr 2023 09:14:27 -0700 Subject: [PATCH 56/65] Bugfix - Scope issue --- src/re_com/dropdown.cljs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/re_com/dropdown.cljs b/src/re_com/dropdown.cljs index 61aeae91..c964d127 100644 --- a/src/re_com/dropdown.cljs +++ b/src/re_com/dropdown.cljs @@ -520,7 +520,7 @@ focus-free-text #(when @free-text-input (.focus @free-text-input)) node (reagent/atom nil) focus-anchor #(some-> @node (.getElementsByClassName "chosen-single") (.item 0) (.focus)) - cmerger (merge-css single-dropdown-css-spec args)] + ] (load-choices "" regex-filter? false) (fn single-dropdown-render [& {:keys [choices model on-change id-fn label-fn group-fn render-fn disabled? filter-box? regex-filter? placeholder title? free-text? auto-complete? capitalize? enter-drop? cancelable? set-to-filter filter-placeholder can-drop-above? est-item-height repeat-change? i18n on-drop width max-height tab-index debounce-delay tooltip tooltip-position class style attr parts] @@ -528,7 +528,8 @@ :as args}] (or (validate-args-macro single-dropdown-args-desc args) - (let [choices (if choices-fn? (:choices @choices-state) (deref-or-value choices)) + (let [cmerger (merge-css single-dropdown-css-spec args) + choices (if choices-fn? (:choices @choices-state) (deref-or-value choices)) id-fn (if free-text? identity id-fn) label-fn (if free-text? identity label-fn) render-fn (if free-text? identity render-fn) From 0ae0731a02ab5da9e267c0bf7112ce58eb243dc7 Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Sat, 1 Apr 2023 16:27:28 -0700 Subject: [PATCH 57/65] Improve button default class handling --- src/re_com/buttons.cljs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/re_com/buttons.cljs b/src/re_com/buttons.cljs index 7c063e2d..dc9bc60f 100644 --- a/src/re_com/buttons.cljs +++ b/src/re_com/buttons.cljs @@ -22,7 +22,8 @@ {:type :legacy :level 1 :class "rc-button" :impl "[:button]" :notes "The actual button."}])) (def button-css-spec - {:main {:class ["rc-button" "btn"] + {:main {:class (fn [{:keys [class]}] + ["rc-button" "btn" (when (empty? class) "btn-default")]) :style (flex-child-style "none") } :wrapper {:class ["rc-button-wrapper" "display-inline-flex"]} :tooltip {:class ["rc-button-tooltip"]}}) @@ -51,7 +52,6 @@ (let [showing? (reagent/atom false)] (fn [& {:keys [label on-click tooltip tooltip-position disabled? class style attr parts src debug-as] - :or {class "btn-default"} :as args}] (or (validate-args-macro button-args-desc args) @@ -61,7 +61,7 @@ cmerger (merge-css button-css-spec args) the-button [:button (merge - (flatten-attr (cmerger :main)) + (flatten-attr (cmerger :main {:class class :disabled? disabled?})) {:disabled disabled? :on-click (handler-fn (when (and on-click (not disabled?)) From f44e1e4c28d8c2230990aadf7d903fd87b129872 Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Wed, 26 Apr 2023 19:50:30 -0700 Subject: [PATCH 58/65] Bugfix in popover-border :right and :bottom weren't being sent through to CSS, causing confusion about the location of the popup. --- src/re_com/popover.cljs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/re_com/popover.cljs b/src/re_com/popover.cljs index 8ee8dd4e..8ac39890 100644 --- a/src/re_com/popover.cljs +++ b/src/re_com/popover.cljs @@ -287,11 +287,12 @@ (def popover-border-css-spec {:main {:class ["popover" "fade" "in" "rc-popover-border"] :style (fn [{:keys [top left width height background-color border-color tooltip-style? - orientation margin-left margin-top ready-to-show?] :as params}] + orientation margin-left margin-top ready-to-show? right bottom] + :as params}] (merge (into {} (for [k [:top :left :width :height :background-color :border-color - :margin-left :margin-top] + :margin-left :margin-top :right :bottom] :let [v (get params k)] :when v] [k v])) @@ -595,7 +596,8 @@ (validate-args-macro popover-anchor-wrapper-args-desc args) (let [external-position (reagent/atom position) internal-position (reagent/atom @external-position) - reset-on-hide (reaction (when-not (deref-or-value showing?) (reset! internal-position @external-position)))] + reset-on-hide (reaction (when-not (deref-or-value showing?) + (reset! internal-position @external-position)))] (reagent/create-class {:display-name "popover-anchor-wrapper" From 76bcc0b0147c1afef2987e969829f8e2780ff83e Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Tue, 6 Jun 2023 11:32:40 -0700 Subject: [PATCH 59/65] Some tests for merge-css --- test/re_com/cmerger_test.cljs | 48 +++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 test/re_com/cmerger_test.cljs diff --git a/test/re_com/cmerger_test.cljs b/test/re_com/cmerger_test.cljs new file mode 100644 index 00000000..dbf1ba1a --- /dev/null +++ b/test/re_com/cmerger_test.cljs @@ -0,0 +1,48 @@ +(ns re-com.cmerger-test + (:require [cljs.test :refer-macros [is are deftest]] + [reagent.core :as reagent] + [re-com.util :as util])) + +(deftest test-cmerger-minimal + (let [result ((util/merge-css {:main {}} {}) :main)] + (is (map? result)) + (is (empty? result)))) + +(def fake-css-spec + {:main {:class ["c"] + :style {:position "extradimensional"} + :attr {:personality "nice"}} + :tale {:class (fn [{:keys [is?]}] + (if is? "sickle" "less"))}}) + +(deftest test-cmerger-main + (are [expected actual] (= expected actual) + ["c"] (:class ((util/merge-css fake-css-spec {}) :main)) + + "extradimensional" (-> + ((util/merge-css fake-css-spec {}) :main) + :style :position) + "here" (-> + ((util/merge-css + fake-css-spec + {}) + :main + {:style {:position "here"}}) + :style :position) + "there" (-> + ((util/merge-css + fake-css-spec + {:parts {:main {:style {:position "there"}}}}) + :main + {:style {:position "here"}}) + :style :position) + "anywhere" (-> + ((util/merge-css + fake-css-spec + {:style {:position "anywhere"} + :parts {:main {:style {:position "there"}}}}) + :main + {:style {:position "here"}}) + :style :position) + + )) From 1297802247d4b231398e9c7789bf143361f91b40 Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Wed, 7 Jun 2023 08:27:30 -0700 Subject: [PATCH 60/65] More tests for merge-css --- test/re_com/cmerger_test.cljs | 43 ++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/test/re_com/cmerger_test.cljs b/test/re_com/cmerger_test.cljs index dbf1ba1a..1a486381 100644 --- a/test/re_com/cmerger_test.cljs +++ b/test/re_com/cmerger_test.cljs @@ -10,15 +10,14 @@ (def fake-css-spec {:main {:class ["c"] - :style {:position "extradimensional"} - :attr {:personality "nice"}} + :style {:position "extradimensional"}} :tale {:class (fn [{:keys [is?]}] - (if is? "sickle" "less"))}}) + (if is? "sickle" "less")) + :attr {:personality "nice"}}}) (deftest test-cmerger-main (are [expected actual] (= expected actual) - ["c"] (:class ((util/merge-css fake-css-spec {}) :main)) - + "extradimensional" (-> ((util/merge-css fake-css-spec {}) :main) :style :position) @@ -45,4 +44,36 @@ {:style {:position "here"}}) :style :position) - )) + ["c"] (:class ((util/merge-css fake-css-spec {}) :main)) + ["c" "stowage" "2nd" "1st"] + (:class ((util/merge-css + fake-css-spec + {:class "1st" + :parts {:main {:class "2nd"}}}) + :main + {:class "stowage"})))) + +(deftest test-cmerger-parts + (are [expected actual] (= expected actual) + + ["less"] (:class ((util/merge-css fake-css-spec {}) :tale)) + ["sickle"] (:class ((util/merge-css fake-css-spec {}) :tale {:is? true})) + + "nice" (-> + ((re-com.util/merge-css + fake-css-spec + {:attr {:personality "cranky"} + :parts {:main {:attr {:personality "agressive"}}}}) + :tale) + :attr :personality) + + "split" (-> + ((re-com.util/merge-css + fake-css-spec + {:attr {:personality "cranky"} + :parts {:main {:attr {:personality "aggressive"}}}}) + :tale + {:attr {:personality "split"}}) + :attr :personality))) + + From 58188137d1d2856cb7177b847884d8d85dfa2ebc Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Thu, 17 Aug 2023 08:24:48 -0700 Subject: [PATCH 61/65] Add arrow-renderer option - Available in popover-border, popover-content-wrapper --- src/re_com/popover.cljs | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/re_com/popover.cljs b/src/re_com/popover.cljs index 8ac39890..cecb3767 100644 --- a/src/re_com/popover.cljs +++ b/src/re_com/popover.cljs @@ -119,9 +119,9 @@ :stroke (or popover-border-color (when-not no-border? "rgba(0, 0, 0, .2)")) :stroke-width "1"})}}) -(defn- popover-arrow +(defn popover-arrow "Render the triangle which connects the popover to the anchor (using SVG)" - [orientation pop-offset arrow-length arrow-width grey-arrow? no-border? popover-color popover-border-color parts] + [& {:keys [orientation pop-offset arrow-length arrow-width grey-arrow? no-border? popover-color popover-border-color parts]}] (let [half-arrow-width (/ arrow-width 2) arrow-shape {:left (str (point 0 0) (point arrow-length half-arrow-width) (point 0 arrow-width)) :right (str (point arrow-length 0) (point 0 half-arrow-width) (point arrow-length arrow-width)) @@ -339,6 +339,7 @@ {:name :margin-left :required false :type "string" :validate-fn string? :description "a CSS style describing the horiztonal offset from anchor after position"} {:name :margin-top :required false :type "string" :validate-fn string? :description "a CSS style describing the vertical offset from anchor after position"} {:name :tooltip-style? :required false :default false :type "boolean" :description "setup popover styles for a tooltip"} + {:name :arrow-renderer :required false :type "-> hiccup" :validate-fn fn? :description "A function that may take any of the keywords received by popover-arrow in popover.cljs. Returns hiccup for the popover arrow."} {:name :title :required false :type "string | markup" :description "describes a title"} {:name :class :required false :type "string" :validate-fn string? :description "CSS class names, space separated (applies to the outer container)"} {:name :style :required false :type "CSS style map" :validate-fn css-style? :description "override component style(s) with a style map, only use in case of emergency (applies to the outer container)"} @@ -388,7 +389,8 @@ :reagent-render (fn popover-border-render [& {:keys [children position position-offset width height popover-color popover-border-color arrow-length - arrow-width arrow-gap padding margin-left margin-top tooltip-style? title class style attr parts src] + arrow-width arrow-gap padding margin-left margin-top tooltip-style? arrow-renderer + title class style attr parts src] :or {arrow-length 11 arrow-width 22 arrow-gap -1} :as args}] (or @@ -410,7 +412,16 @@ :orientation orientation :margin-left margin-left :margin-top margin-top :ready-to-show? @ready-to-show?}))) (->attr args)) - [popover-arrow orientation @pop-offset arrow-length arrow-width grey-arrow? tooltip-style? popover-color popover-border-color parts] + [(or arrow-renderer popover-arrow) + :orientation orientation + :pop-offset @pop-offset + :arrow-length arrow-length + :arrow-width arrow-width + :grey-arrow? grey-arrow? + :tooltip-style? tooltip-style? + :popover-color popover-color + :popover-border-color popover-border-color + :parts parts] (when title title) (into [:div (flatten-attr @@ -459,6 +470,7 @@ {:name :close-button? :required false :default true :type "boolean" :description "when true, displays the close button"} {:name :body :required false :type "string | hiccup" :validate-fn string-or-hiccup? :description "describes the popover body. Must be a single component"} {:name :tooltip-style? :required false :default false :type "boolean" :description "setup popover styles for a tooltip"} + {:name :arrow-renderer :required false :type "-> hiccup" :validate-fn fn? :description "A function that may take any of the keywords received by popover-arrow in popover.cljs. Returns hiccup for the popover arrow."} {:name :popover-color :required false :default "white" :type "string" :validate-fn string? :description "fill color of the popover"} {:name :popover-border-color :required false :type "string" :validate-fn string? :description "color of the popover border, including the arrow"} {:name :arrow-length :required false :default 11 :type "integer | string" :validate-fn number-or-string? :description "the length in pixels of the arrow (from pointy part to middle of arrow base)"} @@ -502,7 +514,7 @@ (fn popover-content-wrapper-render [& {:keys [showing-injected? position-injected position-offset no-clip? width height backdrop-opacity on-cancel title close-button? body tooltip-style? popover-color popover-border-color arrow-length arrow-width - arrow-gap padding class style attr parts] + arrow-gap arrow-renderer padding class style attr parts] :or {arrow-length 11 arrow-width 22 arrow-gap -1} :as args}] (or @@ -531,6 +543,7 @@ :width width :height height :tooltip-style? tooltip-style? + :arrow-renderer arrow-renderer :popover-color popover-color :popover-border-color popover-border-color :arrow-length arrow-length From 605d023eda9dfb4dafe766bfcaae4ea3d7da6185 Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Wed, 21 Feb 2024 08:52:23 -0800 Subject: [PATCH 62/65] Bugfixes --- package-lock.json | 49 ++++++++++++ package.json | 3 + src/re_com/dropdown.cljs | 3 +- src/re_com/multi_select.cljs | 21 +++++ src/re_com/popover.cljs | 8 +- src/re_com/simple_v_table.cljs | 140 +++++++++++++++++---------------- src/re_com/tabs.cljs | 2 +- 7 files changed, 155 insertions(+), 71 deletions(-) diff --git a/package-lock.json b/package-lock.json index 39d15009..33be2395 100644 --- a/package-lock.json +++ b/package-lock.json @@ -769,6 +769,11 @@ "dev": true, "optional": true }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -804,6 +809,14 @@ "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", "dev": true }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, "hash-base": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", @@ -925,6 +938,14 @@ "binary-extensions": "^2.0.0" } }, + "is-core-module": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", + "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", + "requires": { + "has": "^1.0.3" + } + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -1025,6 +1046,14 @@ "which": "^1.2.1" } }, + "karma-cli": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/karma-cli/-/karma-cli-2.0.0.tgz", + "integrity": "sha512-1Kb28UILg1ZsfqQmeELbPzuEb5C6GZJfVIk0qOr8LNYQuYWmAaqP16WpbpKEjhejDrDYyYOwwJXSZO6u7q5Pvw==", + "requires": { + "resolve": "^1.3.3" + } + }, "karma-cljs-test": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/karma-cljs-test/-/karma-cljs-test-0.1.0.tgz", @@ -1279,6 +1308,11 @@ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, "pbkdf2": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", @@ -1479,6 +1513,16 @@ "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", "dev": true }, + "resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "requires": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, "rfdc": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", @@ -1751,6 +1795,11 @@ "ansi-regex": "^5.0.0" } }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" + }, "timers-browserify": { "version": "2.0.12", "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", diff --git a/package.json b/package.json index e303d6fe..47ecdf2f 100644 --- a/package.json +++ b/package.json @@ -8,5 +8,8 @@ "react": "17.0.2", "react-dom": "17.0.2", "shadow-cljs": "2.15.2" + }, + "dependencies": { + "karma-cli": "^2.0.0" } } diff --git a/src/re_com/dropdown.cljs b/src/re_com/dropdown.cljs index af84a5bf..771dd0f0 100644 --- a/src/re_com/dropdown.cljs +++ b/src/re_com/dropdown.cljs @@ -176,7 +176,8 @@ :reagent-render (fn [filter-box? filter-text key-handler drop-showing? set-filter-text filter-placeholder] (let [cmerger (merge-css single-dropdown-css-spec {})] [:div - (flatten-attr (cmerger :filter-wrapper)) + (merge (flatten-attr (cmerger :filter-wrapper)) + {:ref ref!}) [:input (flatten-attr (cmerger diff --git a/src/re_com/multi_select.cljs b/src/re_com/multi_select.cljs index 6893f1ba..5745d04a 100644 --- a/src/re_com/multi_select.cljs +++ b/src/re_com/multi_select.cljs @@ -416,6 +416,7 @@ (reset! *internal-model @*latest-ext-model)) changeable? (and on-change (not disabled?)) excludable? (and @*current-selection-id (> (count @*internal-model) (if required? 1 0))) + filter-fn (or filter-fn (comp str label-fn)) choices-filter-fn (if regex-filter? (filter-items-regex group-fn filter-fn @*filter-choices-text) (filter-items group-fn filter-fn @*filter-choices-text)) @@ -448,6 +449,25 @@ (reset! *selection-group-heading-selected? group-heading-selected?) (reset! *warning-message nil)) include-filtered-click #(do (if (and (some? max-selected-items) (> (+ (count @*internal-model) (count filtered-choices)) max-selected-items)) + (reset! *warning-message max-msg) + (do + (swap! *internal-model conj @*current-choice-id) + (reset! *warning-message nil))) + (when (and changeable? (not= @*internal-model @*latest-ext-model)) + (reset! *external-model @*internal-model) + (on-change @*internal-model)) + (reset! *current-choice-id nil)) + include-click #(do (if @*choice-group-heading-selected? + (let [choices-to-include (->> filtered-choices + (filter (fn [item] (= (first @*current-choice-id) (group-fn item)))) + (map id-fn) ;; TODO: Need to realise map output for prod build (dev doesn't need it). Why? + set)] ;; TODO: See https://github.com/day8/apps-lib/issues/35 + (if (and (some? max-selected-items) (> (+ (count @*internal-model) (count choices-to-include)) max-selected-items)) + (reset! *warning-message max-msg) + (do + (reset! *internal-model (set (concat @*internal-model choices-to-include))) + (reset! *choice-group-heading-selected? false)))) + (if (and (some? max-selected-items) (>= (count @*internal-model) max-selected-items)) (reset! *warning-message max-msg) (do (swap! *internal-model conj @*current-choice-id) @@ -456,6 +476,7 @@ (reset! *external-model @*internal-model) (on-change @*internal-model)) (reset! *current-choice-id nil)) + exclude-click #(do (if excludable? (if @*selection-group-heading-selected? (let [new-internal-model (->> filtered-selections diff --git a/src/re_com/popover.cljs b/src/re_com/popover.cljs index 77650a7c..889f8103 100644 --- a/src/re_com/popover.cljs +++ b/src/re_com/popover.cljs @@ -488,11 +488,12 @@ (validate-args-macro popover-content-wrapper-args-desc args) (let [left-offset (reagent/atom 0) top-offset (reagent/atom 0) + !ref (atom nil) + ref! (partial reset! !ref) position-no-clip-popover (fn position-no-clip-popover [this] (when no-clip? - (let [node (rdom/dom-node this) - popover-point-node (.-parentNode node) ;; Get reference to rc-popover-point node + (let [popover-point-node (.-parentNode @!ref) ;; Get reference to rc-popover-point node bounding-rect (.getBoundingClientRect popover-point-node)] ;; The modern magical way of getting offsetLeft and offsetTop. Returns this: https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIDOMClientRect (reset! left-offset (.-left bounding-rect)) (reset! top-offset (.-top bounding-rect)))))] @@ -523,7 +524,8 @@ (cmerger :main {:no-clip? no-clip? :left-offset @left-offset :top-offset @top-offset})) - (->attr args)) + (->attr args) + {:ref ref!}) (when (and (deref-or-value showing-injected?) on-cancel) (add-map-to-hiccup-call (cmerger :backdrop) diff --git a/src/re_com/simple_v_table.cljs b/src/re_com/simple_v_table.cljs index 9b862fd0..3f310fd2 100644 --- a/src/re_com/simple_v_table.cljs +++ b/src/re_com/simple_v_table.cljs @@ -4,6 +4,7 @@ [re-com.validate :refer [validate-args-macro]]) (:require [reagent.core :as reagent] + [re-com.buttons :refer [hyperlink row-button]] [re-com.config :refer [include-args-desc?]] [re-com.box :refer [box h-box gap]] [re-com.util :refer [px deref-or-value assoc-in-if-empty merge-css add-map-to-hiccup-call flatten-attr ->v position-for-id item-for-id remove-id-item clipboard-write! table->tsv]] @@ -306,88 +307,95 @@ " [& {:keys [src] :as args}] (or - (validate-args-macro simple-v-table-args-desc args) - (let [sort-by-column (reagent/atom nil)] - (fn simple-v-table-render - [& {:keys [model columns fixed-column-count fixed-column-border-color column-header-height column-header-renderer - max-width max-rows row-height table-padding table-row-line-color on-click-row on-enter-row on-leave-row - striped? row-style cell-style class parts src debug-as] + (validate-args-macro simple-v-table-args-desc args) + (let [sort-by-column (reagent/atom nil)] + (fn simple-v-table-render + [& {:keys [model columns fixed-column-count fixed-column-border-color column-header-height column-header-renderer + max-width max-rows row-height table-padding table-row-line-color on-click-row on-enter-row on-leave-row + show-export-button? on-export export-button-renderer + striped? row-style cell-style class parts src debug-as] - :or {column-header-height 31 - row-height 31 - fixed-column-count 0 - table-padding 19 - fixed-column-border-color "#BBBEC0" - column-header-renderer column-header-renderer} - :as args}] - (or - (validate-args-macro simple-v-table-args-desc args) - (let [fcc-bounded (min fixed-column-count (count columns)) - fixed-cols (subvec columns 0 fcc-bounded) - content-cols (subvec columns fcc-bounded (count columns)) - fixed-content-width (->> fixed-cols (map :width) (reduce + 0)) - content-width (->> content-cols (map :width) (reduce + 0)) - fixed-col-border-style (str "1px solid " fixed-column-border-color) - actual-table-width (+ fixed-content-width - (when (pos? fixed-column-count) 1) ;; 1 border width (for fixed-col-border) - content-width - v-table/scrollbar-tot-thick - (* 2 table-padding) - 2) ;; 2 border widths - cmerger (merge-css simple-v-table-css-spec args)] - (add-map-to-hiccup-call - (cmerger :simple-wrapper {:max-rows max-rows - :padding (px table-padding) - :max-width (or max-width (px actual-table-width)) - :table-row-line-color table-row-line-color}) - [box - :src src - :debug-as (or debug-as (reflect-current-component)) - :child [v-table/v-table - :src (at) - :model model - :sort-comp (multi-comparator (->v @sort-by-column)) + :or {column-header-height 31 + row-height 31 + fixed-column-count 0 + table-padding 19 + table-row-line-color "#EAEEF1" + fixed-column-border-color "#BBBEC0" + column-header-renderer column-header-renderer + show-export-button? false + on-export (fn [{:keys [columns rows]}] (-> (remove (comp false? :export?) columns) + (table->tsv rows) + clipboard-write!)) + export-button-renderer clipboard-export-button} + :as args}] + (or + (validate-args-macro simple-v-table-args-desc args) + (let [fcc-bounded (min fixed-column-count (count columns)) + fixed-cols (subvec columns 0 fcc-bounded) + content-cols (subvec columns fcc-bounded (count columns)) + fixed-content-width (->> fixed-cols (map :width) (reduce + 0)) + content-width (->> content-cols (map :width) (reduce + 0)) + fixed-col-border-style (str "1px solid " fixed-column-border-color) + actual-table-width (+ fixed-content-width + (when (pos? fixed-column-count) 1) ;; 1 border width (for fixed-col-border) + content-width + v-table/scrollbar-tot-thick + (* 2 table-padding) + 2) ;; 2 border widths + cmerger (merge-css simple-v-table-css-spec args)] + (add-map-to-hiccup-call + (cmerger :simple-wrapper {:max-rows max-rows + :padding (px table-padding) + :max-width (or max-width (px actual-table-width)) + :table-row-line-color table-row-line-color}) + [box + :src src + :debug-as (or debug-as (reflect-current-component)) + :child [v-table/v-table + :src (at) + :model model + :sort-comp (multi-comparator (->v @sort-by-column)) ;; ===== Column header (section 4) - :column-header-renderer (partial column-header-renderer content-cols parts sort-by-column) - :column-header-height column-header-height + :column-header-renderer (partial column-header-renderer content-cols parts sort-by-column) + :column-header-height column-header-height ;; ===== Row header (section 2) - :row-header-renderer (partial row-renderer fixed-cols on-click-row on-enter-row on-leave-row striped? row-height row-style cell-style parts table-row-line-color) + :row-header-renderer (partial row-renderer fixed-cols on-click-row on-enter-row on-leave-row striped? row-height row-style cell-style parts table-row-line-color) ;; ===== Rows (section 5) - :row-renderer (partial row-renderer content-cols on-click-row on-enter-row on-leave-row striped? row-height row-style cell-style parts table-row-line-color) - :row-content-width content-width - :row-height row-height - :max-row-viewport-height (when max-rows (* max-rows row-height)) + :row-renderer (partial row-renderer content-cols on-click-row on-enter-row on-leave-row striped? row-height row-style cell-style parts table-row-line-color) + :row-content-width content-width + :row-height row-height + :max-row-viewport-height (when max-rows (* max-rows row-height)) ;:max-width (px (or max-width (+ fixed-content-width content-width v-table/scrollbar-tot-thick))) ; :max-width handled by enclosing parent above ;; ===== Corners (section 1) - :top-left-renderer (partial column-header-renderer fixed-cols parts sort-by-column) ;; Used when there are fixed columns - :top-right-renderer (when show-export-button? - #(let [rows (deref-or-value model) - columns (deref-or-value columns) - sort-by-column (deref-or-value sort-by-column)] - [export-button-renderer {:rows rows - :columns columns - :on-export (fn [_] (on-export {:columns columns - :rows (cond->> rows - sort-by-column (sort (multi-comparator (->v sort-by-column))))}))}])) + :top-left-renderer (partial column-header-renderer fixed-cols parts sort-by-column) ;; Used when there are fixed columns + :top-right-renderer (when show-export-button? + #(let [rows (deref-or-value model) + columns (deref-or-value columns) + sort-by-column (deref-or-value sort-by-column)] + [export-button-renderer {:rows rows + :columns columns + :on-export (fn [_] (on-export {:columns columns + :rows (cond->> rows + sort-by-column (sort (multi-comparator (->v sort-by-column))))}))}])) ;; ===== Styling - :class class - :parts (cond-> (-> + :class class + :parts (cond-> (-> ;; Remove the parts that are exclusive to simple-v-table, or v-table part ;; validation will fail: - (apply dissoc (into [parts] simple-v-table-exclusive-parts)) + (apply dissoc (into [parts] simple-v-table-exclusive-parts)) ;; Inject styles, if not set already, into parts. merge is not safe as it is not ;; recursive so e.g. simply setting :attr would delete :style map. ;(assoc-in-if-empty [:wrapper :style :background-color] "antiquewhite") ;; DEBUG - (assoc-in-if-empty [:wrapper :style :font-size] "13px") - (assoc-in-if-empty [:wrapper :style :cursor] "default")) + (assoc-in-if-empty [:wrapper :style :font-size] "13px") + (assoc-in-if-empty [:wrapper :style :cursor] "default")) - (pos? fixed-column-count) - (-> - (assoc-in-if-empty [:top-left :style :border-right] fixed-col-border-style) - (assoc-in-if-empty [:row-headers :style :border-right] fixed-col-border-style)))]]))))))) + (pos? fixed-column-count) + (-> + (assoc-in-if-empty [:top-left :style :border-right] fixed-col-border-style) + (assoc-in-if-empty [:row-headers :style :border-right] fixed-col-border-style)))]]))))))) diff --git a/src/re_com/tabs.cljs b/src/re_com/tabs.cljs index c4438fd7..af69f15d 100644 --- a/src/re_com/tabs.cljs +++ b/src/re_com/tabs.cljs @@ -247,7 +247,7 @@ {:name :parts :required false :type "map" :validate-fn (parts? pill-tabs-parts) :description "See Parts section below."})))) (defn- pill-tabs ;; tabs-like in action - [& {:keys [model tabs on-change id-fn label-fn vertical? class style attr parts src] :as args}] + [& {:keys [model tabs on-change id-fn label-fn vertical? class style attr parts src disabled?] :as args}] (let [current (deref-or-value model) tabs (deref-or-value tabs) disabled? (deref-or-value disabled?) From d85fdf4c5f36e8d1112c4edf1b7253be43b81577 Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Wed, 21 Feb 2024 09:15:35 -0800 Subject: [PATCH 63/65] Run cljfmt - Make things more consistent with upstream master. --- src/re_com/alert.cljs | 147 ++++---- src/re_com/box.cljs | 295 ++++++++-------- src/re_com/buttons.cljs | 492 +++++++++++++------------- src/re_com/checkbox.cljs | 72 ++-- src/re_com/close_button.cljs | 71 ++-- src/re_com/datepicker.cljs | 148 ++++---- src/re_com/dropdown.cljs | 610 ++++++++++++++++----------------- src/re_com/input_text.cljs | 203 ++++++----- src/re_com/input_time.cljs | 96 +++--- src/re_com/modal_panel.cljs | 18 +- src/re_com/multi_select.cljs | 554 +++++++++++++++--------------- src/re_com/popover.cljs | 610 ++++++++++++++++----------------- src/re_com/progress_bar.cljs | 50 +-- src/re_com/radio_button.cljs | 68 ++-- src/re_com/selection_list.cljs | 84 ++--- src/re_com/simple_v_table.cljs | 110 +++--- src/re_com/slider.cljs | 62 ++-- src/re_com/splits.cljs | 282 ++++++++------- src/re_com/tabs.cljs | 139 ++++---- src/re_com/tag_dropdown.cljs | 228 ++++++------ src/re_com/text.cljs | 74 ++-- src/re_com/throbber.cljs | 42 +-- src/re_com/tour.cljs | 9 +- src/re_com/typeahead.cljs | 155 +++++---- src/re_com/util.cljs | 11 +- src/re_com/v_table.cljs | 459 ++++++++++++------------- 26 files changed, 2520 insertions(+), 2569 deletions(-) diff --git a/src/re_com/alert.cljs b/src/re_com/alert.cljs index 6f9afaf9..4433492a 100644 --- a/src/re_com/alert.cljs +++ b/src/re_com/alert.cljs @@ -45,8 +45,7 @@ :h4 {:class ["rc-alert-h4"] :style {:margin-bottom "0px"}} :close-button {:class ["rc-alert-close-button"]} - :body {:class ["rc-alert-body"]} -}) + :body {:class ["rc-alert-body"]}}) (def alert-box-args-desc (when include-args-desc? @@ -70,41 +69,41 @@ :or {alert-type :info} :as args}] (or - (validate-args-macro alert-box-args-desc args) - (let [cmerger (merge-css alert-box-css-spec args) - close-alert (add-map-to-hiccup-call - (cmerger :close-button) - [close-button - :src (at) - :on-click #(on-close id) - :div-size 20 - :font-size 20])] - [:div - (merge (flatten-attr - (cmerger :main {:alert-type alert-type :padding padding})) - (->attr args)) - (when heading - (add-map-to-hiccup-call - (cmerger :heading {:body body}) - [h-box - :src (at) - :justify :between - :align :center - :children [[:h4 - (flatten-attr (cmerger :h4)) - heading] - (when (and closeable? on-close) - close-alert)]])) - (when body - (add-map-to-hiccup-call - (cmerger :body) - [h-box - :src (at) - :justify :between - :align :center - :children [[:div body] - (when (and (not heading) closeable? on-close) - close-alert)]]))]))) + (validate-args-macro alert-box-args-desc args) + (let [cmerger (merge-css alert-box-css-spec args) + close-alert (add-map-to-hiccup-call + (cmerger :close-button) + [close-button + :src (at) + :on-click #(on-close id) + :div-size 20 + :font-size 20])] + [:div + (merge (flatten-attr + (cmerger :main {:alert-type alert-type :padding padding})) + (->attr args)) + (when heading + (add-map-to-hiccup-call + (cmerger :heading {:body body}) + [h-box + :src (at) + :justify :between + :align :center + :children [[:h4 + (flatten-attr (cmerger :h4)) + heading] + (when (and closeable? on-close) + close-alert)]])) + (when body + (add-map-to-hiccup-call + (cmerger :body) + [h-box + :src (at) + :justify :between + :align :center + :children [[:div body] + (when (and (not heading) closeable? on-close) + close-alert)]]))]))) ;;-------------------------------------------------------------------------------------------------- ;; Component: alert-list @@ -163,40 +162,40 @@ :or {padding "4px"} :as args}] (or - (validate-args-macro alert-list-args-desc args) - (let [alerts (deref-or-value alerts) - cmerger (merge-css alert-list-css-spec args)] - (add-map-to-hiccup-call - (cmerger :wrapper) - [box - :src src - :debug-as (or debug-as (reflect-current-component)) - :child (add-map-to-hiccup-call - (cmerger :main) - [border - :src (at) - :padding padding - :border border-style - :child (add-map-to-hiccup-call - (cmerger :scroller {:max-height max-height}) - [scroller - :src (at) - :v-scroll :auto - :child (add-map-to-hiccup-call - (cmerger :v-box) - [v-box - :src (at) - :size "auto" - :children [(for [alert alerts] - (let [{:keys [id alert-type heading body padding closeable?]} alert] - ^{:key id} [alert-box - :src (at) - :id id - :alert-type alert-type - :heading heading - :body body - :padding padding - :closeable? closeable? - :on-close on-close - :class alert-class - :style (merge alert-style (:style alert))]))]])])])])))) + (validate-args-macro alert-list-args-desc args) + (let [alerts (deref-or-value alerts) + cmerger (merge-css alert-list-css-spec args)] + (add-map-to-hiccup-call + (cmerger :wrapper) + [box + :src src + :debug-as (or debug-as (reflect-current-component)) + :child (add-map-to-hiccup-call + (cmerger :main) + [border + :src (at) + :padding padding + :border border-style + :child (add-map-to-hiccup-call + (cmerger :scroller {:max-height max-height}) + [scroller + :src (at) + :v-scroll :auto + :child (add-map-to-hiccup-call + (cmerger :v-box) + [v-box + :src (at) + :size "auto" + :children [(for [alert alerts] + (let [{:keys [id alert-type heading body padding closeable?]} alert] + ^{:key id} [alert-box + :src (at) + :id id + :alert-type alert-type + :heading heading + :body body + :padding padding + :closeable? closeable? + :on-close on-close + :class alert-class + :style (merge alert-style (:style alert))]))]])])])])))) diff --git a/src/re_com/box.cljs b/src/re_com/box.cljs index 88854c67..aac34847 100644 --- a/src/re_com/box.cljs +++ b/src/re_com/box.cljs @@ -3,12 +3,12 @@ [re-com.core :refer [at]] [re-com.validate :refer [validate-args-macro]]) (:require - [clojure.string :as string] - [re-com.config :refer [include-args-desc?]] - [re-com.util :refer [add-map-to-hiccup-call flatten-attr merge-css]] - [re-com.debug :refer [->attr]] - [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?]])) + [clojure.string :as string] + [re-com.config :refer [include-args-desc?]] + [re-com.util :refer [add-map-to-hiccup-call flatten-attr merge-css]] + [re-com.debug :refer [->attr]] + [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?]])) (def visualise-flow? false) @@ -121,32 +121,32 @@ {:main {:class ["display-flex"] :style (fn [{: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 style attr]}] - (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 visualise-flow? {:background-color "lightblue"} {}))))}}) + (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 visualise-flow? {:background-color "lightblue"} {}))))}}) (defn- box-base "This should generally NOT be used as it is the basis for the box, scroller and border components" @@ -189,13 +189,12 @@ [& {:keys [size width height class style attr] :as args}] (or - (validate-args-macro gap-args-desc args) - (let [cmerger (merge-css gap-css-spec args)] - [:div - (merge - (->attr args) - (flatten-attr (cmerger :main {:size size :width width :height height})))]))) - + (validate-args-macro gap-args-desc args) + (let [cmerger (merge-css gap-css-spec args)] + [:div + (merge + (->attr args) + (flatten-attr (cmerger :main {:size size :width width :height height})))]))) ;; ------------------------------------------------------------------------------------ ;; Component: line @@ -224,13 +223,12 @@ [& {:keys [size color class style attr] :as args}] (or - (validate-args-macro line-args-desc args) - (let [cmerger (merge-css line-css-spec args)] - [:div - (merge - (->attr args) - (flatten-attr (cmerger :main {:size size :color color})))]))) - + (validate-args-macro line-args-desc args) + (let [cmerger (merge-css line-css-spec args)] + [:div + (merge + (->attr args) + (flatten-attr (cmerger :main {:size size :color color})))]))) ;; ------------------------------------------------------------------------------------ ;; Component: h-box (visualise-flow? color: gold) @@ -285,20 +283,20 @@ [& {:keys [size width height min-width min-height max-width max-height justify align align-self margin padding gap children class style attr] :as args}] (or - (validate-args-macro h-box-args-desc args) - (let [cmerger (merge-css h-box-css-spec args) - gap-form (when gap [re-com.box/gap - :src (at) - :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 - (->attr args) - (flatten-attr (cmerger :main args)))] - children)))) + (validate-args-macro h-box-args-desc args) + (let [cmerger (merge-css h-box-css-spec args) + gap-form (when gap [re-com.box/gap + :src (at) + :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 + (->attr args) + (flatten-attr (cmerger :main args)))] + children)))) ;; ------------------------------------------------------------------------------------ ;; Component: v-box (visualise-flow? color: antiquewhite) @@ -352,21 +350,20 @@ [& {:keys [size width height min-width min-height max-width max-height justify align align-self margin padding gap children class style attr] :as args}] (or - (validate-args-macro v-box-args-desc args) - (let [cmerger (merge-css v-box-css-spec args) - gap-form (when gap [re-com.box/gap - :src (at) - :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 - (->attr args) - (flatten-attr (cmerger :main args)))] - children)))) - + (validate-args-macro v-box-args-desc args) + (let [cmerger (merge-css v-box-css-spec args) + gap-form (when gap [re-com.box/gap + :src (at) + :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 + (->attr args) + (flatten-attr (cmerger :main args)))] + children)))) ;; ------------------------------------------------------------------------------------ ;; Component: box @@ -403,28 +400,27 @@ :or {size "none"} :as args}] (or - (validate-args-macro box-args-desc args) - (let [cmerger (merge-css box-css-spec args) - {:keys [class style attr]} (cmerger :main {:attr attr})] - (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 class - :style style - :attr attr - :src src - :debug-as debug-as)))) - + (validate-args-macro box-args-desc args) + (let [cmerger (merge-css box-css-spec args) + {:keys [class style attr]} (cmerger :main {:attr attr})] + (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 class + :style style + :attr attr + :src src + :debug-as debug-as)))) ;; ------------------------------------------------------------------------------------ ;; Component: scroller @@ -478,33 +474,32 @@ :or {size "auto"} :as args}] (or - (validate-args-macro scroller-args-desc args) - (let [not-v-or-h (and (nil? v-scroll) (nil? h-scroll)) - scroll (if (and (nil? scroll) not-v-or-h) :auto scroll) - cmerger (merge-css scroller-css-spec args) - {:keys [class style attr]} (cmerger :main {:attr attr})] - (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 class - :style style - :attr attr - :src src - :debug-as debug-as)))) - + (validate-args-macro scroller-args-desc args) + (let [not-v-or-h (and (nil? v-scroll) (nil? h-scroll)) + scroll (if (and (nil? scroll) not-v-or-h) :auto scroll) + cmerger (merge-css scroller-css-spec args) + {:keys [class style attr]} (cmerger :main {:attr attr})] + (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 class + :style style + :attr attr + :src src + :debug-as debug-as)))) ;; ------------------------------------------------------------------------------------ ;; Component: border @@ -548,29 +543,29 @@ :or {size "none"} :as args}] (or - (validate-args-macro border-args-desc args) - (let [no-border (every? nil? [border l-border r-border t-border b-border]) - default-border "1px solid lightgrey" - cmerger (merge-css border-css-spec args) - {:keys [class style attr]} (cmerger :main {:attr attr})] - (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 class - :style style - :attr attr - :src src - :debug-as debug-as)))) + (validate-args-macro border-args-desc args) + (let [no-border (every? nil? [border l-border r-border t-border b-border]) + default-border "1px solid lightgrey" + cmerger (merge-css border-css-spec args) + {:keys [class style attr]} (cmerger :main {:attr attr})] + (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 class + :style style + :attr attr + :src src + :debug-as debug-as)))) diff --git a/src/re_com/buttons.cljs b/src/re_com/buttons.cljs index 1fc607b1..38478834 100644 --- a/src/re_com/buttons.cljs +++ b/src/re_com/buttons.cljs @@ -2,14 +2,14 @@ (:require-macros [re-com.core :refer [handler-fn at reflect-current-component]]) (:require - [re-com.util :refer [deref-or-value px merge-css add-map-to-hiccup-call flatten-attr]] - [re-com.config :refer [include-args-desc?]] - [re-com.debug :refer [->attr]] - [re-com.validate :refer [position? position-options-list button-size? button-sizes-list - string-or-hiccup? css-style? html-attr? string-or-atom? parts?] :refer-macros [validate-args-macro]] - [re-com.popover :refer [popover-tooltip]] - [re-com.box :refer [h-box v-box box gap line flex-child-style]] - [reagent.core :as reagent])) + [re-com.util :refer [deref-or-value px merge-css add-map-to-hiccup-call flatten-attr]] + [re-com.config :refer [include-args-desc?]] + [re-com.debug :refer [->attr]] + [re-com.validate :refer [position? position-options-list button-size? button-sizes-list + string-or-hiccup? css-style? html-attr? string-or-atom? parts?] :refer-macros [validate-args-macro]] + [re-com.popover :refer [popover-tooltip]] + [re-com.box :refer [h-box v-box box gap line flex-child-style]] + [reagent.core :as reagent])) ;; ------------------------------------------------------------------------------------ ;; Component: button @@ -24,7 +24,7 @@ (def button-css-spec {:main {:class (fn [{:keys [class]}] ["rc-button" "btn" (when (empty? class) "btn-default")]) - :style (flex-child-style "none") } + :style (flex-child-style "none")} :wrapper {:class ["rc-button-wrapper" "display-inline-flex"]} :tooltip {:class ["rc-button-tooltip"]}}) @@ -54,41 +54,40 @@ [& {:keys [label on-click tooltip tooltip-position disabled? class style attr parts src debug-as] :as args}] (or - (validate-args-macro button-args-desc args) - (do - (when-not tooltip (reset! showing? false)) ;; To prevent tooltip from still showing after button drag/drop - (let [disabled? (deref-or-value disabled?) - cmerger (merge-css button-css-spec args) - the-button [:button - (merge - (flatten-attr (cmerger :main {:class class :disabled? disabled?})) - {:disabled disabled? - :on-click (handler-fn - (when (and on-click (not disabled?)) - (on-click event)))} - (when tooltip - {:on-mouse-over (handler-fn (reset! showing? true)) - :on-mouse-out (handler-fn (reset! showing? false))})) - label]] - (when disabled? - (reset! showing? false)) - (add-map-to-hiccup-call - (cmerger :wrapper) - [box - :src src - :debug-as (or debug-as (reflect-current-component)) - :align :start - :child (if tooltip - (add-map-to-hiccup-call - (cmerger :tooltip) - [popover-tooltip - :src (at) - :label tooltip - :position (or tooltip-position :below-center) - :showing? showing? - :anchor the-button]) - the-button)]))))))) - + (validate-args-macro button-args-desc args) + (do + (when-not tooltip (reset! showing? false)) ;; To prevent tooltip from still showing after button drag/drop + (let [disabled? (deref-or-value disabled?) + cmerger (merge-css button-css-spec args) + the-button [:button + (merge + (flatten-attr (cmerger :main {:class class :disabled? disabled?})) + {:disabled disabled? + :on-click (handler-fn + (when (and on-click (not disabled?)) + (on-click event)))} + (when tooltip + {:on-mouse-over (handler-fn (reset! showing? true)) + :on-mouse-out (handler-fn (reset! showing? false))})) + label]] + (when disabled? + (reset! showing? false)) + (add-map-to-hiccup-call + (cmerger :wrapper) + [box + :src src + :debug-as (or debug-as (reflect-current-component)) + :align :start + :child (if tooltip + (add-map-to-hiccup-call + (cmerger :tooltip) + [popover-tooltip + :src (at) + :label tooltip + :position (or tooltip-position :below-center) + :showing? showing? + :anchor the-button]) + the-button)]))))))) ;;-------------------------------------------------------------------------------------------------- ;; Component: md-circle-icon-button @@ -113,15 +112,14 @@ (when disabled? "rc-circle-disabled")]) :style (fn [{:keys [disabled?]}] - (if disabled? - {} - {:cursor "pointer"}))} + (if disabled? + {} + {:cursor "pointer"}))} :wrapper {:class ["display-inline-flex" "rc-md-circle-icon-button-wrapper"]} :tooltip {:class ["rc-md-circle-icon-button-tooltip"]} :icon {:class (fn [{:keys [md-icon-name]}] - ["zmdi" "zmdi-hc-fw-rc" md-icon-name "rc-md-circle-icon-button-icon"])} - }) + ["zmdi" "zmdi-hc-fw-rc" md-icon-name "rc-md-circle-icon-button-icon"])}}) (def md-circle-icon-button-parts (when include-args-desc? @@ -152,38 +150,37 @@ :or {md-icon-name "zmdi-plus"} :as args}] (or - (validate-args-macro md-circle-icon-button-args-desc args) - (do - (when-not tooltip (reset! showing? false)) ;; To prevent tooltip from still showing after button drag/drop - (let [cmerger (merge-css md-circle-icon-button-css-spec args) - the-button [:div - (merge - (flatten-attr - (cmerger :main {:emphasise? emphasise? :disabled? disabled? :size size})) - {:on-click (handler-fn - (when (and on-click (not disabled?)) - (on-click event)))} - (when tooltip - {:on-mouse-over (handler-fn (reset! showing? true)) - :on-mouse-out (handler-fn (reset! showing? false))})) - [:i (flatten-attr (cmerger :icon {:md-icon-name md-icon-name}))]]] - (add-map-to-hiccup-call - (cmerger :wrapper) - [box - :src src - :debug-as (or debug-as (reflect-current-component)) - :align :start - :child (if tooltip - (add-map-to-hiccup-call - (cmerger :tooltip) - [popover-tooltip - :src (at) - :label tooltip - :position (or tooltip-position :below-center) - :showing? showing? - :anchor the-button]) - the-button)]))))))) - + (validate-args-macro md-circle-icon-button-args-desc args) + (do + (when-not tooltip (reset! showing? false)) ;; To prevent tooltip from still showing after button drag/drop + (let [cmerger (merge-css md-circle-icon-button-css-spec args) + the-button [:div + (merge + (flatten-attr + (cmerger :main {:emphasise? emphasise? :disabled? disabled? :size size})) + {:on-click (handler-fn + (when (and on-click (not disabled?)) + (on-click event)))} + (when tooltip + {:on-mouse-over (handler-fn (reset! showing? true)) + :on-mouse-out (handler-fn (reset! showing? false))})) + [:i (flatten-attr (cmerger :icon {:md-icon-name md-icon-name}))]]] + (add-map-to-hiccup-call + (cmerger :wrapper) + [box + :src src + :debug-as (or debug-as (reflect-current-component)) + :align :start + :child (if tooltip + (add-map-to-hiccup-call + (cmerger :tooltip) + [popover-tooltip + :src (at) + :label tooltip + :position (or tooltip-position :below-center) + :showing? showing? + :anchor the-button]) + the-button)]))))))) ;;-------------------------------------------------------------------------------------------------- ;; Component: md-icon-button @@ -208,15 +205,14 @@ (when disabled? "rc-icon-disabled")]) :style (fn [{:keys [disabled?]}] - (if disabled? - {} - {:cursor "pointer"}))} + (if disabled? + {} + {:cursor "pointer"}))} :wrapper {:class ["display-inline-flex" "rc-md-icon-button-wrapper"]} :tooltip {:class ["rc-md-icon-button-tooltip"]} :icon {:class (fn [{:keys [md-icon-name]}] - ["zmdi" "zmdi-hc-fw-rc" md-icon-name "rc-md-icon-button-icon"])} - }) + ["zmdi" "zmdi-hc-fw-rc" md-icon-name "rc-md-icon-button-icon"])}}) (def md-icon-button-parts (when include-args-desc? @@ -247,38 +243,37 @@ :or {md-icon-name "zmdi-plus"} :as args}] (or - (validate-args-macro md-icon-button-args-desc args) - (do - (when-not tooltip (reset! showing? false)) ;; To prevent tooltip from still showing after button drag/drop - (let [cmerger (merge-css md-icon-button-css-spec args) - the-button [:div - (merge - (flatten-attr - (cmerger :main {:size size :emphasise? emphasise? :disabled? disabled?})) - {:on-click (handler-fn - (when (and on-click (not disabled?)) - (on-click event)))} - (when tooltip - {:on-mouse-over (handler-fn (reset! showing? true)) - :on-mouse-out (handler-fn (reset! showing? false))})) - [:i (cmerger :icon {:md-icon-name md-icon-name})]]] - (add-map-to-hiccup-call - (cmerger :wrapper) - [box - :src src - :debug-as (or debug-as (reflect-current-component)) - :align :start - :child (if tooltip - (add-map-to-hiccup-call - (cmerger :tooltip) - [popover-tooltip - :src (at) - :label tooltip - :position (or tooltip-position :below-center) - :showing? showing? - :anchor the-button]) - the-button)]))))))) - + (validate-args-macro md-icon-button-args-desc args) + (do + (when-not tooltip (reset! showing? false)) ;; To prevent tooltip from still showing after button drag/drop + (let [cmerger (merge-css md-icon-button-css-spec args) + the-button [:div + (merge + (flatten-attr + (cmerger :main {:size size :emphasise? emphasise? :disabled? disabled?})) + {:on-click (handler-fn + (when (and on-click (not disabled?)) + (on-click event)))} + (when tooltip + {:on-mouse-over (handler-fn (reset! showing? true)) + :on-mouse-out (handler-fn (reset! showing? false))})) + [:i (cmerger :icon {:md-icon-name md-icon-name})]]] + (add-map-to-hiccup-call + (cmerger :wrapper) + [box + :src src + :debug-as (or debug-as (reflect-current-component)) + :align :start + :child (if tooltip + (add-map-to-hiccup-call + (cmerger :tooltip) + [popover-tooltip + :src (at) + :label tooltip + :position (or tooltip-position :below-center) + :showing? showing? + :anchor the-button]) + the-button)]))))))) ;;-------------------------------------------------------------------------------------------------- ;; Component: info-button @@ -326,35 +321,34 @@ (fn info-button-render [& {:keys [info position width disabled? class style attr parts src debug-as] :as args}] (or - (validate-args-macro info-button-args-desc args) - (let [cmerger (merge-css info-button-css-spec args)] - (add-map-to-hiccup-call - (cmerger :tooltip) - [popover-tooltip - :src src - :debug-as (or debug-as (reflect-current-component)) - :label info - :status :info - :position (or position :right-below) - :width (or width "250px") - :showing? showing? - :on-cancel #(swap! showing? not) - :anchor [:div + (validate-args-macro info-button-args-desc args) + (let [cmerger (merge-css info-button-css-spec args)] + (add-map-to-hiccup-call + (cmerger :tooltip) + [popover-tooltip + :src src + :debug-as (or debug-as (reflect-current-component)) + :label info + :status :info + :position (or position :right-below) + :width (or width "250px") + :showing? showing? + :on-cancel #(swap! showing? not) + :anchor [:div + (merge + (flatten-attr + (cmerger :main {:disabled? disabled?})) + {:on-click (handler-fn + (when (not disabled?) + (swap! showing? not)))}) + [:svg (merge - (flatten-attr - (cmerger :main {:disabled? disabled?})) - {:on-click (handler-fn - (when (not disabled?) - (swap! showing? not)))}) - [:svg - (merge - (flatten-attr (cmerger :icon)) - {:width "11" - :height "11"}) - [:circle {:cx "5.5" :cy "5.5" :r "5.5"}] - [:circle {:cx "5.5" :cy "2.5" :r "1.4" :fill "white"}] - [:line {:x1 "5.5" :y1 "5.2" :x2 "5.5" :y2 "9.7" :stroke "white" :stroke-width "2.5"}]]]])))))) - + (flatten-attr (cmerger :icon)) + {:width "11" + :height "11"}) + [:circle {:cx "5.5" :cy "5.5" :r "5.5"}] + [:circle {:cx "5.5" :cy "2.5" :r "1.4" :fill "white"}] + [:line {:x1 "5.5" :y1 "5.2" :x2 "5.5" :y2 "9.7" :stroke "white" :stroke-width "2.5"}]]]])))))) ;;-------------------------------------------------------------------------------------------------- ;; Component: row-button @@ -405,38 +399,37 @@ :or {md-icon-name "zmdi-plus"} :as args}] (or - (validate-args-macro row-button-args-desc args) - (do - (when-not tooltip (reset! showing? false)) ;; To prevent tooltip from still showing after button drag/drop - (let [cmerger (merge-css row-button-css-spec args) - the-button [:div - (merge - (flatten-attr - (cmerger :main {:mouse-over-row? mouse-over-row? :disabled? disabled?})) - {:on-click (handler-fn - (when (and on-click (not disabled?)) - (on-click event)))} - (when tooltip - {:on-mouse-over (handler-fn (reset! showing? true)) - :on-mouse-out (handler-fn (reset! showing? false))})) ;; Need to return true to ALLOW default events to be performed - [:i (flatten-attr (cmerger :icon {:md-icon-name md-icon-name}))]]] - (add-map-to-hiccup-call - (cmerger :wrapper) - [box - :src src - :debug-as (reflect-current-component) - :align :start - :child (if tooltip - (add-map-to-hiccup-call - (cmerger :tooltip) - [popover-tooltip - :src (at) - :label tooltip - :position (or tooltip-position :below-center) - :showing? showing? - :anchor the-button]) - the-button)]))))))) - + (validate-args-macro row-button-args-desc args) + (do + (when-not tooltip (reset! showing? false)) ;; To prevent tooltip from still showing after button drag/drop + (let [cmerger (merge-css row-button-css-spec args) + the-button [:div + (merge + (flatten-attr + (cmerger :main {:mouse-over-row? mouse-over-row? :disabled? disabled?})) + {:on-click (handler-fn + (when (and on-click (not disabled?)) + (on-click event)))} + (when tooltip + {:on-mouse-over (handler-fn (reset! showing? true)) + :on-mouse-out (handler-fn (reset! showing? false))})) ;; Need to return true to ALLOW default events to be performed + [:i (flatten-attr (cmerger :icon {:md-icon-name md-icon-name}))]]] + (add-map-to-hiccup-call + (cmerger :wrapper) + [box + :src src + :debug-as (reflect-current-component) + :align :start + :child (if tooltip + (add-map-to-hiccup-call + (cmerger :tooltip) + [popover-tooltip + :src (at) + :label tooltip + :position (or tooltip-position :below-center) + :showing? showing? + :anchor the-button]) + the-button)]))))))) ;;-------------------------------------------------------------------------------------------------- ;; Component: hyperlink @@ -490,44 +483,43 @@ (fn hyperlink-render [& {:keys [label on-click tooltip tooltip-position disabled? class style attr parts src debug-as] :as args}] (or - (validate-args-macro hyperlink-args-desc args) - (do - (when-not tooltip (reset! showing? false)) ;; To prevent tooltip from still showing after button drag/drop - (let [label (deref-or-value label) - disabled? (deref-or-value disabled?) - cmerger (merge-css hyperlink-css-spec args) - the-button (add-map-to-hiccup-call - (cmerger :container) - [box - :src (at) - :align :start - :child [:a - (merge - (flatten-attr (cmerger :main {:disabled? disabled?})) - {:on-click (handler-fn - (when (and on-click (not disabled?)) - (on-click event)))} - (when tooltip - {:on-mouse-over (handler-fn (reset! showing? true)) - :on-mouse-out (handler-fn (reset! showing? false))})) - label]])] - (add-map-to-hiccup-call - (cmerger :wrapper) - [box - :src src - :debug-as (or debug-as (reflect-current-component)) - :align :start - :child (if tooltip - (add-map-to-hiccup-call - (cmerger :tooltip) - [popover-tooltip - :src (at) - :label tooltip - :position (or tooltip-position :below-center) - :showing? showing? - :anchor the-button]) - the-button)]))))))) - + (validate-args-macro hyperlink-args-desc args) + (do + (when-not tooltip (reset! showing? false)) ;; To prevent tooltip from still showing after button drag/drop + (let [label (deref-or-value label) + disabled? (deref-or-value disabled?) + cmerger (merge-css hyperlink-css-spec args) + the-button (add-map-to-hiccup-call + (cmerger :container) + [box + :src (at) + :align :start + :child [:a + (merge + (flatten-attr (cmerger :main {:disabled? disabled?})) + {:on-click (handler-fn + (when (and on-click (not disabled?)) + (on-click event)))} + (when tooltip + {:on-mouse-over (handler-fn (reset! showing? true)) + :on-mouse-out (handler-fn (reset! showing? false))})) + label]])] + (add-map-to-hiccup-call + (cmerger :wrapper) + [box + :src src + :debug-as (or debug-as (reflect-current-component)) + :align :start + :child (if tooltip + (add-map-to-hiccup-call + (cmerger :tooltip) + [popover-tooltip + :src (at) + :label tooltip + :position (or tooltip-position :below-center) + :showing? showing? + :anchor the-button]) + the-button)]))))))) ;;-------------------------------------------------------------------------------------------------- ;; Component: hyperlink-href @@ -580,42 +572,42 @@ (fn hyperlink-href-render [& {:keys [label href target tooltip tooltip-position disabled? class style attr parts src debug-as] :as args}] (or - (validate-args-macro hyperlink-href-args-desc args) - (do - (when-not tooltip (reset! showing? false)) ;; To prevent tooltip from still showing after button drag/drop - (let [label (deref-or-value label) - href (deref-or-value href) - target (deref-or-value target) - disabled? (deref-or-value disabled?) - cmerger (merge-css hyperlink-href-css-spec args) - the-button [:a - (merge (flatten-attr (cmerger :main {:disabled? disabled?})) - {:target target} + (validate-args-macro hyperlink-href-args-desc args) + (do + (when-not tooltip (reset! showing? false)) ;; To prevent tooltip from still showing after button drag/drop + (let [label (deref-or-value label) + href (deref-or-value href) + target (deref-or-value target) + disabled? (deref-or-value disabled?) + cmerger (merge-css hyperlink-href-css-spec args) + the-button [:a + (merge (flatten-attr (cmerger :main {:disabled? disabled?})) + {:target target} ;; As of HTML5 the href attribute on a elements is not required; when those elements do ;; not have href attributes they do not create hyperlinks. These are also known as a ;; 'placeholder link'. A placeholder link resembles a traditional hyperlink, but does not ;; lead anywhere; i.e. it is disabled. ;; Ref: https://www.w3.org/TR/html5/links.html#attr-hyperlink-href - (when (not disabled?) - {:href href}) - (when tooltip - {:on-mouse-over (handler-fn (reset! showing? true)) - :on-mouse-out (handler-fn (reset! showing? false))})) - label]] - - (add-map-to-hiccup-call - (cmerger :wrapper) - [box - :src src - :debug-as (or debug-as (reflect-current-component)) - :align :start - :child (if tooltip - (add-map-to-hiccup-call - (cmerger :tooltip) - [popover-tooltip - :src (at) - :label tooltip - :position (or tooltip-position :below-center) - :showing? showing? - :anchor the-button]) - the-button)]))))))) + (when (not disabled?) + {:href href}) + (when tooltip + {:on-mouse-over (handler-fn (reset! showing? true)) + :on-mouse-out (handler-fn (reset! showing? false))})) + label]] + + (add-map-to-hiccup-call + (cmerger :wrapper) + [box + :src src + :debug-as (or debug-as (reflect-current-component)) + :align :start + :child (if tooltip + (add-map-to-hiccup-call + (cmerger :tooltip) + [popover-tooltip + :src (at) + :label tooltip + :position (or tooltip-position :below-center) + :showing? showing? + :anchor the-button]) + the-button)]))))))) diff --git a/src/re_com/checkbox.cljs b/src/re_com/checkbox.cljs index 213fb69b..530e4ccd 100644 --- a/src/re_com/checkbox.cljs +++ b/src/re_com/checkbox.cljs @@ -3,14 +3,14 @@ [re-com.core :refer [handler-fn at reflect-current-component]] [re-com.validate :refer [validate-args-macro]]) (:require - [re-com.debug :refer [->attr]] - [re-com.config :refer [include-args-desc?]] - [re-com.util :refer [deref-or-value px add-map-to-hiccup-call merge-css flatten-attr]] - [re-com.popover :refer [popover-tooltip]] - [re-com.box :refer [h-box v-box box gap line flex-child-style align-style]] - [re-com.validate :refer [input-status-type? input-status-types-list regex? string-or-hiccup? css-style? html-attr? parts? - number-or-string? string-or-atom? nillable-string-or-atom? throbber-size? throbber-sizes-list]] - [reagent.core :as reagent])) + [re-com.debug :refer [->attr]] + [re-com.config :refer [include-args-desc?]] + [re-com.util :refer [deref-or-value px add-map-to-hiccup-call merge-css flatten-attr]] + [re-com.popover :refer [popover-tooltip]] + [re-com.box :refer [h-box v-box box gap line flex-child-style align-style]] + [re-com.validate :refer [input-status-type? input-status-types-list regex? string-or-hiccup? css-style? html-attr? parts? + number-or-string? string-or-atom? nillable-string-or-atom? throbber-size? throbber-sizes-list]] + [reagent.core :as reagent])) ;; ------------------------------------------------------------------------------------ ;; Component: checkbox @@ -56,31 +56,31 @@ [& {:keys [model on-change label disabled? label-class label-style class style attr parts src debug-as] :as args}] (or - (validate-args-macro checkbox-args-desc args) - (let [cursor "default" - model (deref-or-value model) - disabled? (deref-or-value disabled?) - callback-fn #(when (and on-change (not disabled?)) - (on-change (not model))) ;; call on-change with either true or false - cmerger (merge-css checkbox-css-spec args)] - (add-map-to-hiccup-call - (cmerger :wrapper) - [h-box - :src src - :debug-as (or debug-as (reflect-current-component)) - :align :start - :children [[:input - (merge - (flatten-attr (cmerger :main)) - {:type "checkbox" - :disabled disabled? - :checked (boolean model) - :on-change (handler-fn (callback-fn))})] - (when label - [:span - (flatten-attr - (cmerger :label - {:class label-class - :style label-style - :attr {:on-click (handler-fn (callback-fn))}})) - label])]])))) + (validate-args-macro checkbox-args-desc args) + (let [cursor "default" + model (deref-or-value model) + disabled? (deref-or-value disabled?) + callback-fn #(when (and on-change (not disabled?)) + (on-change (not model))) ;; call on-change with either true or false + cmerger (merge-css checkbox-css-spec args)] + (add-map-to-hiccup-call + (cmerger :wrapper) + [h-box + :src src + :debug-as (or debug-as (reflect-current-component)) + :align :start + :children [[:input + (merge + (flatten-attr (cmerger :main)) + {:type "checkbox" + :disabled disabled? + :checked (boolean model) + :on-change (handler-fn (callback-fn))})] + (when label + [:span + (flatten-attr + (cmerger :label + {:class label-class + :style label-style + :attr {:on-click (handler-fn (callback-fn))}})) + label])]])))) diff --git a/src/re_com/close_button.cljs b/src/re_com/close_button.cljs index 70159554..425168d3 100644 --- a/src/re_com/close_button.cljs +++ b/src/re_com/close_button.cljs @@ -2,13 +2,12 @@ (:require-macros [re-com.core :refer [handler-fn at reflect-current-component]]) (:require - [re-com.config :refer [include-args-desc?]] - [re-com.debug :refer [->attr]] - [re-com.util :refer [deref-or-value px add-map-to-hiccup-call merge-css flatten-attr]] - [re-com.validate :refer [string-or-hiccup? css-style? html-attr? parts?] :refer-macros [validate-args-macro]] - [re-com.box :refer [box]] - [reagent.core :as reagent])) - + [re-com.config :refer [include-args-desc?]] + [re-com.debug :refer [->attr]] + [re-com.util :refer [deref-or-value px add-map-to-hiccup-call merge-css flatten-attr]] + [re-com.validate :refer [string-or-hiccup? css-style? html-attr? parts?] :refer-macros [validate-args-macro]] + [re-com.box :refer [box]] + [reagent.core :as reagent])) ;; ------------------------------------------------------------------------------------ ;; Component: close-button @@ -72,32 +71,32 @@ (fn close-button-render [& {:keys [on-click div-size font-size color hover-color tooltip top-offset left-offset disabled? class style attr parts src debug-as] :as args}] (or - (validate-args-macro close-button-args-desc args) - (let [disabled? (deref-or-value disabled?) - cmerger (merge-css close-button-css-spec args)] - (add-map-to-hiccup-call - (cmerger :wrapper {:div-size div-size :disabled? disabled?}) - [box - :src src - :debug-as (or debug-as (reflect-current-component)) - :child (add-map-to-hiccup-call - (cmerger :main - {:disabled? disabled? - :over? @over? - :font-size font-size - :div-size div-size - :top-offset top-offset - :left-offset left-offset - :color color - :hover-color hover-color - :attr {:title tooltip - :on-click (handler-fn - (when (and on-click (not disabled?)) - (on-click event) - (.stopPropagation event))) - :on-mouse-enter (handler-fn (reset! over? true)) - :on-mouse-leave (handler-fn (reset! over? false))}}) - [box - :src (at) - :child [:i - (flatten-attr (cmerger :icon))]])])))))) + (validate-args-macro close-button-args-desc args) + (let [disabled? (deref-or-value disabled?) + cmerger (merge-css close-button-css-spec args)] + (add-map-to-hiccup-call + (cmerger :wrapper {:div-size div-size :disabled? disabled?}) + [box + :src src + :debug-as (or debug-as (reflect-current-component)) + :child (add-map-to-hiccup-call + (cmerger :main + {:disabled? disabled? + :over? @over? + :font-size font-size + :div-size div-size + :top-offset top-offset + :left-offset left-offset + :color color + :hover-color hover-color + :attr {:title tooltip + :on-click (handler-fn + (when (and on-click (not disabled?)) + (on-click event) + (.stopPropagation event))) + :on-mouse-enter (handler-fn (reset! over? true)) + :on-mouse-leave (handler-fn (reset! over? false))}}) + [box + :src (at) + :child [:i + (flatten-attr (cmerger :icon))]])])))))) diff --git a/src/re_com/datepicker.cljs b/src/re_com/datepicker.cljs index 055abdea..2ca4c59c 100644 --- a/src/re_com/datepicker.cljs +++ b/src/re_com/datepicker.cljs @@ -2,16 +2,16 @@ (:require-macros [re-com.core :refer [handler-fn at reflect-current-component]]) (:require - [reagent.core :as reagent] - [cljs-time.core :as cljs-time] - [re-com.config :refer [include-args-desc?]] - [re-com.validate :refer [date-like? css-style? html-attr? parts? position? position-options-list] :refer-macros [validate-args-macro]] - [cljs-time.predicates :refer [sunday?]] - [cljs-time.format :refer [parse unparse formatters formatter]] - [re-com.box :refer [border gap box line h-box flex-child-style]] - [re-com.util :refer [deref-or-value now->utc add-map-to-hiccup-call merge-css flatten-attr]] - [re-com.popover :refer [popover-anchor-wrapper popover-content-wrapper]] - [clojure.string :as string]) + [reagent.core :as reagent] + [cljs-time.core :as cljs-time] + [re-com.config :refer [include-args-desc?]] + [re-com.validate :refer [date-like? css-style? html-attr? parts? position? position-options-list] :refer-macros [validate-args-macro]] + [cljs-time.predicates :refer [sunday?]] + [cljs-time.format :refer [parse unparse formatters formatter]] + [re-com.box :refer [border gap box line h-box flex-child-style]] + [re-com.util :refer [deref-or-value now->utc add-map-to-hiccup-call merge-css flatten-attr]] + [re-com.popover :refer [popover-anchor-wrapper popover-content-wrapper]] + [clojure.string :as string]) (:import [goog.i18n DateTimeFormat])) @@ -301,39 +301,39 @@ (let [minimum (deref-or-value minimum) maximum (deref-or-value maximum) cmerger (merge-css datepicker-css-spec {:parts parts})] - [:th - (flatten-attr (cmerger :nav)) - [h-box - :src (at) - :height "100%" - :children [[prev-year-nav - :display-month display-month - :minimum minimum - :disabled? disabled? - :parts parts] - [prev-month-nav - :display-month display-month - :minimum minimum - :disabled? disabled? - :parts parts] - (add-map-to-hiccup-call - (cmerger :month) - [box - :src (at) - :size "1" - :align :center - :justify :center - :child (month-label @display-month i18n)]) - [next-month-nav - :display-month display-month - :maximum maximum - :disabled? disabled? - :parts parts] - [next-year-nav - :display-month display-month - :maximum maximum - :disabled? disabled? - :parts parts]]]])) + [:th + (flatten-attr (cmerger :nav)) + [h-box + :src (at) + :height "100%" + :children [[prev-year-nav + :display-month display-month + :minimum minimum + :disabled? disabled? + :parts parts] + [prev-month-nav + :display-month display-month + :minimum minimum + :disabled? disabled? + :parts parts] + (add-map-to-hiccup-call + (cmerger :month) + [box + :src (at) + :size "1" + :align :center + :justify :center + :child (month-label @display-month i18n)]) + [next-month-nav + :display-month display-month + :maximum maximum + :disabled? disabled? + :parts parts] + [next-year-nav + :display-month display-month + :maximum maximum + :disabled? disabled? + :parts parts]]]])) (defn- week-days [& {:keys [start-of-week i18n parts]}] @@ -396,7 +396,6 @@ :attr {:on-click (handler-fn (on-click))}})) (cljs-time/day date)])) - (defn- week-td [start-of-week date cmerger] [:td (flatten-attr (cmerger :week)) @@ -466,7 +465,6 @@ (when include-args-desc? (-> (map :name datepicker-parts-desc) set))) - (def datepicker-css-spec {:main {:class ["datepicker" "noselect" "rc-datepicker"] :style {:font-size "13px" @@ -526,7 +524,6 @@ ["rc-datepicker-today"] :else []))))}}) - (def datepicker-args-desc (when include-args-desc? [{:name :model :required false :type "satisfies DateTimeProtocol | r/atom" :validate-fn date-like? :description [:span "the selected date. If provided, should pass pred " [:code ":selectable-fn"] ". If not provided, (now->utc) will be used and the returned date will be a " [:code "goog.date.UtcDateTime"]]} @@ -550,36 +547,35 @@ (defn datepicker [& {:keys [model] :as args}] (or - (validate-args-macro datepicker-args-desc args) - (let [external-model (reagent/atom (deref-or-value model)) ;; Set model type in stone on creation of this datepicker instance - internal-model (reagent/atom @external-model) ;; Holds the last known external value of model, to detect external model changes - display-month (reagent/atom (cljs-time/first-day-of-the-month (or @internal-model (now->utc))))] - (fn datepicker-render - [& {:keys [model on-change disabled? start-of-week hide-border? class style attr parts src debug-as] - :or {start-of-week 6} ;; Default to Sunday - :as args}] - (or - (validate-args-macro datepicker-args-desc args) - (let [latest-ext-model (deref-or-value model) - disabled? (deref-or-value disabled?) - props-with-defaults (merge args {:start-of-week start-of-week}) - configuration (configure props-with-defaults) - cmerger (merge-css datepicker-css-spec args)] - (when (not= @external-model latest-ext-model) ;; Has model changed externally? - (reset! external-model latest-ext-model) - (reset! internal-model latest-ext-model) - (reset! display-month (cljs-time/first-day-of-the-month (or @internal-model (now->utc))))) - [main-div-with - [:table - (flatten-attr (cmerger :table)) - [table-thead display-month configuration disabled? parts] - [table-tbody @display-month @internal-model configuration disabled? on-change parts]] - hide-border? - cmerger - attr - src - (or debug-as (reflect-current-component))])))))) - + (validate-args-macro datepicker-args-desc args) + (let [external-model (reagent/atom (deref-or-value model)) ;; Set model type in stone on creation of this datepicker instance + internal-model (reagent/atom @external-model) ;; Holds the last known external value of model, to detect external model changes + display-month (reagent/atom (cljs-time/first-day-of-the-month (or @internal-model (now->utc))))] + (fn datepicker-render + [& {:keys [model on-change disabled? start-of-week hide-border? class style attr parts src debug-as] + :or {start-of-week 6} ;; Default to Sunday + :as args}] + (or + (validate-args-macro datepicker-args-desc args) + (let [latest-ext-model (deref-or-value model) + disabled? (deref-or-value disabled?) + props-with-defaults (merge args {:start-of-week start-of-week}) + configuration (configure props-with-defaults) + cmerger (merge-css datepicker-css-spec args)] + (when (not= @external-model latest-ext-model) ;; Has model changed externally? + (reset! external-model latest-ext-model) + (reset! internal-model latest-ext-model) + (reset! display-month (cljs-time/first-day-of-the-month (or @internal-model (now->utc))))) + [main-div-with + [:table + (flatten-attr (cmerger :table)) + [table-thead display-month configuration disabled? parts] + [table-tbody @display-month @internal-model configuration disabled? on-change parts]] + hide-border? + cmerger + attr + src + (or debug-as (reflect-current-component))])))))) (defn- anchor-button "Provide clickable field with current date label and dropdown button e.g. [ 2014 Sep 17 | # ]" diff --git a/src/re_com/dropdown.cljs b/src/re_com/dropdown.cljs index 771dd0f0..90b3a810 100644 --- a/src/re_com/dropdown.cljs +++ b/src/re_com/dropdown.cljs @@ -2,18 +2,18 @@ (:require-macros [re-com.core :refer [handler-fn at]]) (:require - [re-com.config :refer [include-args-desc?]] - [re-com.debug :refer [->attr]] - [re-com.util :refer [deref-or-value position-for-id item-for-id add-map-to-hiccup-call merge-css flatten-attr]] - [re-com.box :refer [align-style flex-child-style]] - [re-com.validate :refer [vector-of-maps? css-style? html-attr? parts? number-or-string? log-warning - string-or-hiccup? position? position-options-list] :refer-macros [validate-args-macro]] - [re-com.popover :refer [popover-tooltip]] - [clojure.string :as string] - [react :as react] - [reagent.core :as reagent] - [goog.string :as gstring] - [goog.string.format])) + [re-com.config :refer [include-args-desc?]] + [re-com.debug :refer [->attr]] + [re-com.util :refer [deref-or-value position-for-id item-for-id add-map-to-hiccup-call merge-css flatten-attr]] + [re-com.box :refer [align-style flex-child-style]] + [re-com.validate :refer [vector-of-maps? css-style? html-attr? parts? number-or-string? log-warning + string-or-hiccup? position? position-options-list] :refer-macros [validate-args-macro]] + [re-com.popover :refer [popover-tooltip]] + [clojure.string :as string] + [react :as react] + [reagent.core :as reagent] + [goog.string :as gstring] + [goog.string.format])) ;; Inspiration: http://alxlit.name/bootstrap-chosen ;; Alternative: http://silviomoreto.github.io/bootstrap-select @@ -131,32 +131,31 @@ ref! (partial reset! !ref) show! #(when (= @internal-model id) (show-selected-item @!ref))] (reagent/create-class - {:component-did-mount show! - :component-did-update show! - - :display-name "choice-item" - - :reagent-render - (fn - [id label on-click internal-model] - (let [selected (= @internal-model id) - cmerger (merge-css single-dropdown-css-spec {}) - class (if selected - "highlighted" - (when @mouse-over? "mouseover"))] - [:li - (flatten-attr - (cmerger :choice-item - {:selected selected - :mouse-over? @mouse-over? - :attr {:ref ref! - :on-mouse-over (handler-fn (reset! mouse-over? true)) - :on-mouse-out (handler-fn (reset! mouse-over? false)) - :on-mouse-down (handler-fn - (on-click id) - (.preventDefault event))}})) ;; Prevent free-text input as well as the normal dropdown from loosing focus - label]))}))) - + {:component-did-mount show! + :component-did-update show! + + :display-name "choice-item" + + :reagent-render + (fn + [id label on-click internal-model] + (let [selected (= @internal-model id) + cmerger (merge-css single-dropdown-css-spec {}) + class (if selected + "highlighted" + (when @mouse-over? "mouseover"))] + [:li + (flatten-attr + (cmerger :choice-item + {:selected selected + :mouse-over? @mouse-over? + :attr {:ref ref! + :on-mouse-over (handler-fn (reset! mouse-over? true)) + :on-mouse-out (handler-fn (reset! mouse-over? false)) + :on-mouse-down (handler-fn + (on-click id) + (.preventDefault event))}})) ;; Prevent free-text input as well as the normal dropdown from loosing focus + label]))}))) (defn make-choice-item [id-fn render-fn callback internal-model opt] @@ -259,46 +258,46 @@ [free-text-input select-free-text? free-text-focused? free-text-sel-range internal-model tab-index placeholder dropdown-click key-handler filter-box? drop-showing? cancel width free-text-change auto-complete? choices capitalize? disabled?] (let [cmerger (merge-css single-dropdown-css-spec {})] [:ul - (flatten-attr (cmerger :choices)) + (flatten-attr (cmerger :choices)) [:li (flatten-attr (cmerger :choices-search)) [:div (flatten-attr (cmerger :free-text-wrapper {:disabled? disabled?})) - [:input - (flatten-attr - (cmerger - :free-text - {:width width - :attr - {:type "text" - :auto-complete "off" - :tab-index tab-index - :placeholder placeholder - :value @internal-model - :disabled disabled? - :on-change (handler-fn (let [value (-> event .-target .-value)] - (free-text-change (cond-> value capitalize? capitalize-first-letter)))) - :on-key-down (handler-fn (when-not (key-handler event) - (.stopPropagation event) - (.preventDefault event))) ;; When key-handler returns false, preventDefault - :on-key-press (handler-fn - (let [ins (.-key event)] - (when (= (count ins) 1) ;; Filter out special keys (e.g. enter) - (handle-free-text-insertion event ins auto-complete? capitalize? choices internal-model free-text-sel-range free-text-change)))) - :on-paste (handler-fn - (let [ins (.getData (.-clipboardData event) "Text")] - (handle-free-text-insertion event ins auto-complete? capitalize? choices internal-model free-text-sel-range free-text-change))) - :on-focus (handler-fn - (reset! free-text-focused? true) - (reset! select-free-text? true)) - :on-blur (handler-fn - (when-not filter-box? - (cancel)) - (reset! free-text-focused? false)) ;; Set free-text-focused? after calling cancel to prevent re-focusing - :on-mouse-down (handler-fn (when @drop-showing? - (cancel) - (.preventDefault event))) ;; Prevent text selection flicker (esp. with filter-box) - :ref #(reset! free-text-input %)}}))] + [:input + (flatten-attr + (cmerger + :free-text + {:width width + :attr + {:type "text" + :auto-complete "off" + :tab-index tab-index + :placeholder placeholder + :value @internal-model + :disabled disabled? + :on-change (handler-fn (let [value (-> event .-target .-value)] + (free-text-change (cond-> value capitalize? capitalize-first-letter)))) + :on-key-down (handler-fn (when-not (key-handler event) + (.stopPropagation event) + (.preventDefault event))) ;; When key-handler returns false, preventDefault + :on-key-press (handler-fn + (let [ins (.-key event)] + (when (= (count ins) 1) ;; Filter out special keys (e.g. enter) + (handle-free-text-insertion event ins auto-complete? capitalize? choices internal-model free-text-sel-range free-text-change)))) + :on-paste (handler-fn + (let [ins (.getData (.-clipboardData event) "Text")] + (handle-free-text-insertion event ins auto-complete? capitalize? choices internal-model free-text-sel-range free-text-change))) + :on-focus (handler-fn + (reset! free-text-focused? true) + (reset! select-free-text? true)) + :on-blur (handler-fn + (when-not filter-box? + (cancel)) + (reset! free-text-focused? false)) ;; Set free-text-focused? after calling cancel to prevent re-focusing + :on-mouse-down (handler-fn (when @drop-showing? + (cancel) + (.preventDefault event))) ;; Prevent text selection flicker (esp. with filter-box) + :ref #(reset! free-text-input %)}}))] [:span (flatten-attr (cmerger @@ -400,15 +399,15 @@ :choices-no-results {:class ["no-results" "rc-dropdown-choices-no-results"]} :group-heading {:class ["group-result"]} :choice-item {:class (fn [{:keys [selected mouse-over?]}] - ["active-result" "group-option" (if selected - "highlighted" - (when mouse-over? "mouseover"))])} + ["active-result" "group-option" (if selected + "highlighted" + (when mouse-over? "mouseover"))])} :filter-wrapper {:class ["chosen-search"]} :filter-input-box {:style (fn [{:keys [visible?]}] - (when-not visible? {:position "absolute" ;; When no filter box required, use it but hide it off screen - :width "0px" ;; The rest of these styles make the textbox invisible - :padding "0px" - :border "none"}))} + (when-not visible? {:position "absolute" ;; When no filter box required, use it but hide it off screen + :width "0px" ;; The rest of these styles make the textbox invisible + :padding "0px" + :border "none"}))} :dropdown-top {:class ["chosen-single" "chosen-default"] :style (fn [{:keys [disabled?]}] (when disabled? @@ -489,157 +488,156 @@ ; request id to ignore handling response when new request was already made :id 0 ; to debounce requests - :timer nil}) - load-choices (partial load-choices choices-state choices debounce-delay) - set-filter-text (fn [text {:keys [regex-filter?] :as args} debounce?] - (load-choices text regex-filter? debounce?) - (reset! filter-text text)) - over? (reagent/atom false) - showing? (reagent/track #(and (not @drop-showing?) @over?)) - free-text-focused? (reagent/atom false) - free-text-input (reagent/atom nil) - select-free-text? (reagent/atom false) - free-text-sel-range (reagent/atom nil) - focus-free-text #(when @free-text-input (.focus @free-text-input)) - node (reagent/atom nil) - focus-anchor #(some-> @node (.getElementsByClassName "chosen-single") (.item 0) (.focus)) - ] - (load-choices "" regex-filter? false) - (fn single-dropdown-render - [& {:keys [choices model on-change id-fn label-fn group-fn render-fn disabled? filter-box? regex-filter? placeholder title? free-text? auto-complete? capitalize? enter-drop? cancelable? set-to-filter filter-placeholder can-drop-above? est-item-height repeat-change? i18n on-drop width max-height tab-index debounce-delay tooltip tooltip-position class style attr parts] - :or {id-fn :id label-fn :label group-fn :group render-fn label-fn enter-drop? true cancelable? true est-item-height 30} - :as args}] - (or - (validate-args-macro single-dropdown-args-desc args) - (let [cmerger (merge-css single-dropdown-css-spec args) - choices (if choices-fn? (:choices @choices-state) (deref-or-value choices)) - id-fn (if free-text? identity id-fn) - label-fn (if free-text? identity label-fn) - render-fn (if free-text? identity render-fn) - disabled? (deref-or-value disabled?) - regex-filter? (deref-or-value regex-filter?) - latest-ext-model (reagent/atom (deref-or-value model)) - _ (when (not= @external-model @latest-ext-model) ;; Has model changed externally? - (reset! external-model @latest-ext-model) - (reset! internal-model @latest-ext-model)) - changeable? (and on-change (not disabled?)) - call-on-change #(when (and changeable? (or (not= @internal-model @latest-ext-model) - repeat-change?)) - (reset! external-model @internal-model) - (on-change @internal-model)) - callback #(do - (reset! internal-model (cond-> % (and free-text? capitalize?) capitalize-first-letter)) - (reset! select-free-text? true) - (call-on-change) - (let [current-drop-showing? @drop-showing?] - (when current-drop-showing? - (focus-free-text)) - (when-not just-drop? - (reset! drop-showing? (not current-drop-showing?))) ;; toggle to allow opening dropdown on Enter key - (when current-drop-showing? - (focus-anchor))) - (set-filter-text "" args false)) - free-text-change #(do - (reset! internal-model %) - (reset! select-free-text? false) - (call-on-change)) - cancel #(do - (when-not @free-text-focused? ;; Prevent re-focusing free-text input on free-text input blur - (focus-free-text)) - (reset! drop-showing? false) - (set-filter-text "" args false) - (reset! internal-model @external-model)) - dropdown-click #(when-not disabled? - (if @drop-showing? - (cancel) - (do - (reset! drop-showing? true) - (focus-free-text) ;; After drop-showing? reset so hiding dropdown in filter-box blur will not be overwritten - (reset! select-free-text? true)))) - filtered-choices (if choices-fn? - choices - (if regex-filter? - (filter-choices-regex choices group-fn label-fn @filter-text) - (filter-choices choices group-fn label-fn @filter-text))) - visible-count #(let [results-node (and @node - (.item (.getElementsByClassName @node "chosen-results") 0))] - (if (and results-node (.-firstChild results-node)) - (quot (.-clientHeight results-node) - (.-offsetHeight (.-firstChild results-node))) - 0)) - est-drop-height #(let [items-height (* (count filtered-choices) est-item-height) - drop-margin 12 - filter-height 32 - maxh (cond - (not max-height) 240 - (string/ends-with? max-height "px") (js/parseInt max-height 10) - :else (do (log-warning "max-height is not in pxs, using 240px for estimation") - 240))] - (min (+ items-height drop-margin (if filter-box? filter-height 0)) - maxh)) - drop-height (reagent/track - #(if-let [drop-node (and @node - (.item (.getElementsByClassName @node "chosen-drop") 0))] - (-> drop-node .getBoundingClientRect .-height) - (est-drop-height))) - top-height 34 - drop-above? (reagent/track - #(when (and can-drop-above? @node) - (let [node-top (-> @node .getBoundingClientRect .-top) - window-height (-> js/document .-documentElement .-clientHeight)] - (> (+ node-top top-height @drop-height) - window-height)))) - press-enter (fn [] - (let [drop-was-showing? @drop-showing?] - (cond - disabled? (cancel) - (and (:on-enter-press set-to-filter) - (seq @filter-text) - (empty? filtered-choices) - free-text? - @drop-showing?) (callback @filter-text) - (or @drop-showing? enter-drop?) (callback @internal-model)) - (not drop-was-showing?))) - press-escape (fn [] - (let [drop-was-showing? @drop-showing?] - (cancel) - (when drop-was-showing? - (focus-anchor)) - (not drop-was-showing?))) - press-tab (fn [shift-key?] - (if disabled? - (cancel) - (let [drop-was-showing? @drop-showing?] ;; Was (callback @internal-model) but needed a customised version - (call-on-change) - (reset! drop-showing? false) - (set-filter-text "" args false) - (when (and drop-was-showing? shift-key?) - (focus-anchor)))) - (reset! drop-showing? false) - true) - press-arrow (fn [offset] - (when (and @drop-showing? (seq filtered-choices)) - (reset! internal-model (move-to-new-choice filtered-choices id-fn @internal-model offset)) - (when-not cancelable? - (call-on-change))) - (reset! drop-showing? true) - (reset! select-free-text? true) - true) - press-up #(press-arrow -1) ;; Up arrow - press-down #(press-arrow 1) ;; Down arrow - press-page-up #(press-arrow (- (dec (visible-count)))) - press-page-down #(press-arrow (dec (visible-count))) - press-home-or-end (fn [offset] - (when (and (not @free-text-focused?) - (seq filtered-choices)) - (reset! internal-model (move-to-new-choice filtered-choices id-fn @internal-model offset)) - (reset! select-free-text? true)) - true) - press-home #(press-home-or-end :start) - press-end #(press-home-or-end :end) - key-handler #(if disabled? - false - (case (.-key %) + :timer nil}) + load-choices (partial load-choices choices-state choices debounce-delay) + set-filter-text (fn [text {:keys [regex-filter?] :as args} debounce?] + (load-choices text regex-filter? debounce?) + (reset! filter-text text)) + over? (reagent/atom false) + showing? (reagent/track #(and (not @drop-showing?) @over?)) + free-text-focused? (reagent/atom false) + free-text-input (reagent/atom nil) + select-free-text? (reagent/atom false) + free-text-sel-range (reagent/atom nil) + focus-free-text #(when @free-text-input (.focus @free-text-input)) + node (reagent/atom nil) + focus-anchor #(some-> @node (.getElementsByClassName "chosen-single") (.item 0) (.focus))] + (load-choices "" regex-filter? false) + (fn single-dropdown-render + [& {:keys [choices model on-change id-fn label-fn group-fn render-fn disabled? filter-box? regex-filter? placeholder title? free-text? auto-complete? capitalize? enter-drop? cancelable? set-to-filter filter-placeholder can-drop-above? est-item-height repeat-change? i18n on-drop width max-height tab-index debounce-delay tooltip tooltip-position class style attr parts] + :or {id-fn :id label-fn :label group-fn :group render-fn label-fn enter-drop? true cancelable? true est-item-height 30} + :as args}] + (or + (validate-args-macro single-dropdown-args-desc args) + (let [cmerger (merge-css single-dropdown-css-spec args) + choices (if choices-fn? (:choices @choices-state) (deref-or-value choices)) + id-fn (if free-text? identity id-fn) + label-fn (if free-text? identity label-fn) + render-fn (if free-text? identity render-fn) + disabled? (deref-or-value disabled?) + regex-filter? (deref-or-value regex-filter?) + latest-ext-model (reagent/atom (deref-or-value model)) + _ (when (not= @external-model @latest-ext-model) ;; Has model changed externally? + (reset! external-model @latest-ext-model) + (reset! internal-model @latest-ext-model)) + changeable? (and on-change (not disabled?)) + call-on-change #(when (and changeable? (or (not= @internal-model @latest-ext-model) + repeat-change?)) + (reset! external-model @internal-model) + (on-change @internal-model)) + callback #(do + (reset! internal-model (cond-> % (and free-text? capitalize?) capitalize-first-letter)) + (reset! select-free-text? true) + (call-on-change) + (let [current-drop-showing? @drop-showing?] + (when current-drop-showing? + (focus-free-text)) + (when-not just-drop? + (reset! drop-showing? (not current-drop-showing?))) ;; toggle to allow opening dropdown on Enter key + (when current-drop-showing? + (focus-anchor))) + (set-filter-text "" args false)) + free-text-change #(do + (reset! internal-model %) + (reset! select-free-text? false) + (call-on-change)) + cancel #(do + (when-not @free-text-focused? ;; Prevent re-focusing free-text input on free-text input blur + (focus-free-text)) + (reset! drop-showing? false) + (set-filter-text "" args false) + (reset! internal-model @external-model)) + dropdown-click #(when-not disabled? + (if @drop-showing? + (cancel) + (do + (reset! drop-showing? true) + (focus-free-text) ;; After drop-showing? reset so hiding dropdown in filter-box blur will not be overwritten + (reset! select-free-text? true)))) + filtered-choices (if choices-fn? + choices + (if regex-filter? + (filter-choices-regex choices group-fn label-fn @filter-text) + (filter-choices choices group-fn label-fn @filter-text))) + visible-count #(let [results-node (and @node + (.item (.getElementsByClassName @node "chosen-results") 0))] + (if (and results-node (.-firstChild results-node)) + (quot (.-clientHeight results-node) + (.-offsetHeight (.-firstChild results-node))) + 0)) + est-drop-height #(let [items-height (* (count filtered-choices) est-item-height) + drop-margin 12 + filter-height 32 + maxh (cond + (not max-height) 240 + (string/ends-with? max-height "px") (js/parseInt max-height 10) + :else (do (log-warning "max-height is not in pxs, using 240px for estimation") + 240))] + (min (+ items-height drop-margin (if filter-box? filter-height 0)) + maxh)) + drop-height (reagent/track + #(if-let [drop-node (and @node + (.item (.getElementsByClassName @node "chosen-drop") 0))] + (-> drop-node .getBoundingClientRect .-height) + (est-drop-height))) + top-height 34 + drop-above? (reagent/track + #(when (and can-drop-above? @node) + (let [node-top (-> @node .getBoundingClientRect .-top) + window-height (-> js/document .-documentElement .-clientHeight)] + (> (+ node-top top-height @drop-height) + window-height)))) + press-enter (fn [] + (let [drop-was-showing? @drop-showing?] + (cond + disabled? (cancel) + (and (:on-enter-press set-to-filter) + (seq @filter-text) + (empty? filtered-choices) + free-text? + @drop-showing?) (callback @filter-text) + (or @drop-showing? enter-drop?) (callback @internal-model)) + (not drop-was-showing?))) + press-escape (fn [] + (let [drop-was-showing? @drop-showing?] + (cancel) + (when drop-was-showing? + (focus-anchor)) + (not drop-was-showing?))) + press-tab (fn [shift-key?] + (if disabled? + (cancel) + (let [drop-was-showing? @drop-showing?] ;; Was (callback @internal-model) but needed a customised version + (call-on-change) + (reset! drop-showing? false) + (set-filter-text "" args false) + (when (and drop-was-showing? shift-key?) + (focus-anchor)))) + (reset! drop-showing? false) + true) + press-arrow (fn [offset] + (when (and @drop-showing? (seq filtered-choices)) + (reset! internal-model (move-to-new-choice filtered-choices id-fn @internal-model offset)) + (when-not cancelable? + (call-on-change))) + (reset! drop-showing? true) + (reset! select-free-text? true) + true) + press-up #(press-arrow -1) ;; Up arrow + press-down #(press-arrow 1) ;; Down arrow + press-page-up #(press-arrow (- (dec (visible-count)))) + press-page-down #(press-arrow (dec (visible-count))) + press-home-or-end (fn [offset] + (when (and (not @free-text-focused?) + (seq filtered-choices)) + (reset! internal-model (move-to-new-choice filtered-choices id-fn @internal-model offset)) + (reset! select-free-text? true)) + true) + press-home #(press-home-or-end :start) + press-end #(press-home-or-end :end) + key-handler #(if disabled? + false + (case (.-key %) "Enter" (press-enter) "Escape" (press-escape) "Tab" (press-tab (.-shiftKey %)) @@ -650,78 +648,78 @@ "Home" (press-home) "End" (press-end) (or filter-box? free-text?))) ;; Use this boolean to allow/prevent the key from being processed by the text box - dropdown [:div - (merge + dropdown [:div + (merge + (flatten-attr + (cmerger :main {:free-text? free-text? + :drop-showing? @drop-showing? + :free-text-focused? @free-text-focused? + :width width})) + {:ref #(reset! node %)} + (when tooltip + {:on-mouse-over (handler-fn (reset! over? true)) + :on-mouse-out (handler-fn (reset! over? false))}) + (->attr args)) + (cond + just-drop? nil + free-text? [free-text-dropdown-top free-text-input select-free-text? free-text-focused? free-text-sel-range internal-model tab-index placeholder dropdown-click key-handler filter-box? drop-showing? cancel width free-text-change auto-complete? choices capitalize? disabled?] + :else [dropdown-top internal-model choices id-fn label-fn tab-index placeholder dropdown-click key-handler filter-box? drop-showing? title? disabled?]) + (when (and @drop-showing? (not disabled?)) + [:div (flatten-attr - (cmerger :main {:free-text? free-text? - :drop-showing? @drop-showing? - :free-text-focused? @free-text-focused? - :width width})) - {:ref #(reset! node %)} - (when tooltip - {:on-mouse-over (handler-fn (reset! over? true)) - :on-mouse-out (handler-fn (reset! over? false))}) - (->attr args)) - (cond - just-drop? nil - free-text? [free-text-dropdown-top free-text-input select-free-text? free-text-focused? free-text-sel-range internal-model tab-index placeholder dropdown-click key-handler filter-box? drop-showing? cancel width free-text-change auto-complete? choices capitalize? disabled?] - :else [dropdown-top internal-model choices id-fn label-fn tab-index placeholder dropdown-click key-handler filter-box? drop-showing? title? disabled?]) - (when (and @drop-showing? (not disabled?)) - [:div - (flatten-attr - (cmerger :chosen-drop {:drop-above? @drop-above? - :top-height top-height - :drop-height @drop-height})) - (when (and (or filter-box? (not free-text?)) - (not just-drop?)) - [filter-text-box filter-box? filter-text key-handler drop-showing? #(set-filter-text % args true) filter-placeholder]) - [:ul - (flatten-attr - (cmerger :chosen-results {:max-height max-height})) - (cond - (and choices-fn? (:loading? @choices-state)) - [:li - (flatten-attr (cmerger :choices-loading)) - (get i18n :loading "Loading...")] - (and choices-fn? (:error @choices-state)) - [:li - (flatten-attr (cmerger :choices-error)) - (:error @choices-state)] - (-> filtered-choices count pos?) - (let [[group-names group-opt-lists] (choices-with-group-headings filtered-choices group-fn) - make-a-choice (partial make-choice-item id-fn render-fn callback internal-model) - make-choices #(map make-a-choice %1) - make-h-then-choices (fn [h opts] - (cons (make-group-heading h) - (make-choices opts))) - has-no-group-names? (nil? (:group (first group-names)))] - (if (and (= 1 (count group-opt-lists)) has-no-group-names?) - (make-choices (first group-opt-lists)) ;; one group means no headings - (apply concat (map make-h-then-choices group-names group-opt-lists)))) - :else - [:li - (flatten-attr - (cmerger :choices-no-results - {:attr - {:on-mouse-down (handler-fn - (when (and (:on-no-results-match-click set-to-filter) - (seq @filter-text) - free-text?) - (callback @filter-text)))}})) - (gstring/format (or (and (seq @filter-text) (:no-results-match i18n)) - (and (empty? @filter-text) (:no-results i18n)) - (:no-results-match i18n) - "No results match \"%s\"") - @filter-text)])]])] - _ (when tooltip (add-watch drop-showing? :tooltip #(reset! over? false))) - _ (when on-drop (add-watch drop-showing? :on-drop #(when (and (not %3) %4) (on-drop))))] - (if tooltip - (add-map-to-hiccup-call - (cmerger :tooltip) - [popover-tooltip - :src (at) - :label tooltip - :position (or tooltip-position :below-center) - :showing? showing? - :anchor dropdown]) - dropdown))))))) + (cmerger :chosen-drop {:drop-above? @drop-above? + :top-height top-height + :drop-height @drop-height})) + (when (and (or filter-box? (not free-text?)) + (not just-drop?)) + [filter-text-box filter-box? filter-text key-handler drop-showing? #(set-filter-text % args true) filter-placeholder]) + [:ul + (flatten-attr + (cmerger :chosen-results {:max-height max-height})) + (cond + (and choices-fn? (:loading? @choices-state)) + [:li + (flatten-attr (cmerger :choices-loading)) + (get i18n :loading "Loading...")] + (and choices-fn? (:error @choices-state)) + [:li + (flatten-attr (cmerger :choices-error)) + (:error @choices-state)] + (-> filtered-choices count pos?) + (let [[group-names group-opt-lists] (choices-with-group-headings filtered-choices group-fn) + make-a-choice (partial make-choice-item id-fn render-fn callback internal-model) + make-choices #(map make-a-choice %1) + make-h-then-choices (fn [h opts] + (cons (make-group-heading h) + (make-choices opts))) + has-no-group-names? (nil? (:group (first group-names)))] + (if (and (= 1 (count group-opt-lists)) has-no-group-names?) + (make-choices (first group-opt-lists)) ;; one group means no headings + (apply concat (map make-h-then-choices group-names group-opt-lists)))) + :else + [:li + (flatten-attr + (cmerger :choices-no-results + {:attr + {:on-mouse-down (handler-fn + (when (and (:on-no-results-match-click set-to-filter) + (seq @filter-text) + free-text?) + (callback @filter-text)))}})) + (gstring/format (or (and (seq @filter-text) (:no-results-match i18n)) + (and (empty? @filter-text) (:no-results i18n)) + (:no-results-match i18n) + "No results match \"%s\"") + @filter-text)])]])] + _ (when tooltip (add-watch drop-showing? :tooltip #(reset! over? false))) + _ (when on-drop (add-watch drop-showing? :on-drop #(when (and (not %3) %4) (on-drop))))] + (if tooltip + (add-map-to-hiccup-call + (cmerger :tooltip) + [popover-tooltip + :src (at) + :label tooltip + :position (or tooltip-position :below-center) + :showing? showing? + :anchor dropdown]) + dropdown))))))) diff --git a/src/re_com/input_text.cljs b/src/re_com/input_text.cljs index d237ca8b..4915e9ce 100644 --- a/src/re_com/input_text.cljs +++ b/src/re_com/input_text.cljs @@ -3,15 +3,15 @@ [re-com.core :refer [handler-fn at reflect-current-component]] [re-com.validate :refer [validate-args-macro]]) (:require - [re-com.config :refer [include-args-desc?]] - [re-com.debug :refer [->attr]] - [re-com.util :refer [deref-or-value px add-map-to-hiccup-call merge-css flatten-attr]] - [re-com.popover :refer [popover-tooltip]] - [re-com.throbber :refer [throbber]] - [re-com.box :refer [h-box v-box box gap line flex-child-style align-style]] - [re-com.validate :refer [input-status-type? input-status-types-list regex? string-or-hiccup? css-style? html-attr? parts? - number-or-string? string-or-atom? nillable-string-or-atom? throbber-size? throbber-sizes-list]] - [reagent.core :as reagent])) + [re-com.config :refer [include-args-desc?]] + [re-com.debug :refer [->attr]] + [re-com.util :refer [deref-or-value px add-map-to-hiccup-call merge-css flatten-attr]] + [re-com.popover :refer [popover-tooltip]] + [re-com.throbber :refer [throbber]] + [re-com.box :refer [h-box v-box box gap line flex-child-style align-style]] + [re-com.validate :refer [input-status-type? input-status-types-list regex? string-or-hiccup? css-style? html-attr? parts? + number-or-string? string-or-atom? nillable-string-or-atom? throbber-size? throbber-sizes-list]] + [reagent.core :as reagent])) ;; ------------------------------------------------------------------------------------ ;; Component: input-text @@ -52,7 +52,7 @@ :tooltip-icon {:class (fn [{:keys [status]}] ["zmdi" "zmdi-hc-fw" (case - status + status :success "zmdi-check-circle" :warning "zmdi-alert-triangle" :error "zmdi-alert-circle" @@ -65,7 +65,7 @@ :tooltip-icon2 {:class (fn [{:keys [status]}] ["zmdi" "zmdi-hc-fw" (case - status + status :success "zmdi-check-circle" :warning "zmdi-alert-triangle" :error "zmdi-alert-circle" @@ -136,100 +136,99 @@ ;; fix this problem there is an optional 2-arity version of on-change that receives a function as the second ;; arg that when called signals that :model has reached a 'steady state' and the reset! of external-model ;; can be done thus avoiding the flicker. - on-change-handler (fn [] - (when (fn? on-change) - (let [has-done-fn? (= 2 (.-length ^js/Function on-change)) - reset-fn #(reset! external-model @internal-model)] - (if has-done-fn? - (on-change @internal-model reset-fn) - (do - (on-change @internal-model) - (reset-fn)))))) - cmerger (merge-css input-text-css-spec args)] - (when (not= @external-model latest-ext-model) ;; Has model changed externally? - (reset! external-model latest-ext-model) - (reset! internal-model latest-ext-model)) - (add-map-to-hiccup-call - (cmerger :wrapper) - [h-box - :src src - :debug-as (or debug-as (reflect-current-component)) - :align :start - :width (if width width "250px") - :children [[:div + on-change-handler (fn [] + (when (fn? on-change) + (let [has-done-fn? (= 2 (.-length ^js/Function on-change)) + reset-fn #(reset! external-model @internal-model)] + (if has-done-fn? + (on-change @internal-model reset-fn) + (do + (on-change @internal-model) + (reset-fn)))))) + cmerger (merge-css input-text-css-spec args)] + (when (not= @external-model latest-ext-model) ;; Has model changed externally? + (reset! external-model latest-ext-model) + (reset! internal-model latest-ext-model)) + (add-map-to-hiccup-call + (cmerger :wrapper) + [h-box + :src src + :debug-as (or debug-as (reflect-current-component)) + :align :start + :width (if width width "250px") + :children [[:div + (flatten-attr + (cmerger :inner {:status status :status-icon? status-icon?})) + [(if (= input-type :password) :input input-type) + (merge (flatten-attr - (cmerger :inner {:status status :status-icon? status-icon?})) - [(if (= input-type :password) :input input-type) - (merge - (flatten-attr - (cmerger :main {:height height})) - {:type (case input-type - :input "text" - :password "password" - nil) - :rows (when (= input-type :textarea) (or rows 3)) - :placeholder placeholder - :value @internal-model - :disabled disabled? - :on-change (handler-fn - (let [new-val-orig (-> event .-target .-value) - new-val (on-alter new-val-orig)] - (when (not= new-val new-val-orig) - (set! (-> event .-target .-value) new-val)) - (when (and - on-change - (not disabled?) - (if validation-regex (re-find validation-regex new-val) true)) - (reset! internal-model new-val) - (when-not change-on-blur? - (on-change-handler))))) - :on-blur (handler-fn + (cmerger :main {:height height})) + {:type (case input-type + :input "text" + :password "password" + nil) + :rows (when (= input-type :textarea) (or rows 3)) + :placeholder placeholder + :value @internal-model + :disabled disabled? + :on-change (handler-fn + (let [new-val-orig (-> event .-target .-value) + new-val (on-alter new-val-orig)] + (when (not= new-val new-val-orig) + (set! (-> event .-target .-value) new-val)) (when (and - change-on-blur? - (not= @internal-model @external-model)) - (on-change-handler))) - :on-key-up (handler-fn - (if disabled? - (.preventDefault event) - (case (.-key event) - "Enter" (on-change-handler) - "Escape" (reset! internal-model @external-model) - true)))} - attr)]] - (when (and status-icon? status) - (if status-tooltip - (add-map-to-hiccup-call - (cmerger :popover) - [popover-tooltip - :src (at) - :label status-tooltip - :position :right-center - :status status + on-change + (not disabled?) + (if validation-regex (re-find validation-regex new-val) true)) + (reset! internal-model new-val) + (when-not change-on-blur? + (on-change-handler))))) + :on-blur (handler-fn + (when (and + change-on-blur? + (not= @internal-model @external-model)) + (on-change-handler))) + :on-key-up (handler-fn + (if disabled? + (.preventDefault event) + (case (.-key event) + "Enter" (on-change-handler) + "Escape" (reset! internal-model @external-model) + true)))} + attr)]] + (when (and status-icon? status) + (if status-tooltip + (add-map-to-hiccup-call + (cmerger :popover) + [popover-tooltip + :src (at) + :label status-tooltip + :position :right-center + :status status ;:width "200px" - :showing? showing? - :anchor (if (= :validating status) - (add-map-to-hiccup-call - (cmerger :throbber - {:attr {:on-mouse-over (handler-fn (when (and status-icon? status) (reset! showing? true))) - :on-mouse-out (handler-fn (reset! showing? false))}}) - [throbber - :size :regular]) - [:i (merge - (flatten-attr - (cmerger :tooltip-icon {:status status})) - {:on-mouse-over (handler-fn (when (and status-icon? status) (reset! showing? true))) - :on-mouse-out (handler-fn (reset! showing? false))})])]) - (if (= :validating status) - (add-map-to-hiccup-call - (cmerger :throbber) - [throbber - :src (at) - :size :regular]) - [:i (flatten-attr - (cmerger :tooltip-icon2 - {:status status - :attr {:title status-tooltip}}))])))]]))))))) - + :showing? showing? + :anchor (if (= :validating status) + (add-map-to-hiccup-call + (cmerger :throbber + {:attr {:on-mouse-over (handler-fn (when (and status-icon? status) (reset! showing? true))) + :on-mouse-out (handler-fn (reset! showing? false))}}) + [throbber + :size :regular]) + [:i (merge + (flatten-attr + (cmerger :tooltip-icon {:status status})) + {:on-mouse-over (handler-fn (when (and status-icon? status) (reset! showing? true))) + :on-mouse-out (handler-fn (reset! showing? false))})])]) + (if (= :validating status) + (add-map-to-hiccup-call + (cmerger :throbber) + [throbber + :src (at) + :size :regular]) + [:i (flatten-attr + (cmerger :tooltip-icon2 + {:status status + :attr {:title status-tooltip}}))])))]]))))))) (defn input-text [& args] diff --git a/src/re_com/input_time.cljs b/src/re_com/input_time.cljs index 2787a513..09711214 100644 --- a/src/re_com/input_time.cljs +++ b/src/re_com/input_time.cljs @@ -3,13 +3,13 @@ [re-com.core :refer [handler-fn at reflect-current-component]] [re-com.validate :refer [validate-args-macro]]) (:require - [reagent.core :as reagent] - [re-com.config :refer [include-args-desc?]] - [re-com.debug :as debug :refer [->attr]] - [re-com.validate :refer [css-style? html-attr? parts? number-or-string?]] - [re-com.text :refer [label]] - [re-com.box :refer [h-box gap]] - [re-com.util :refer [pad-zero-number deref-or-value add-map-to-hiccup-call merge-css flatten-attr]])) + [reagent.core :as reagent] + [re-com.config :refer [include-args-desc?]] + [re-com.debug :as debug :refer [->attr]] + [re-com.validate :refer [css-style? html-attr? parts? number-or-string?]] + [re-com.text :refer [label]] + [re-com.box :refer [h-box gap]] + [re-com.util :refer [pad-zero-number deref-or-value add-map-to-hiccup-call merge-css flatten-attr]])) (defn- time->mins [time] @@ -185,47 +185,47 @@ [& {:keys [model minimum maximum] :as args :or {minimum 0 maximum 2359}}] (or - (validate-args-macro input-time-args-desc args) - (validate-arg-times (deref-or-value model) minimum maximum args) - (let [deref-model (deref-or-value model) - text-model (reagent/atom (time->text deref-model)) - previous-model (reagent/atom deref-model)] - (fn input-time-render - [& {:keys [model on-change minimum maximum disabled? show-icon? hide-border? width height class style attr parts src debug-as] :as args - :or {minimum 0 maximum 2359}}] - (or - (validate-args-macro input-time-args-desc args) - (validate-arg-times (deref-or-value model) minimum maximum args) - (let [style (merge (when hide-border? {:border "none"}) - style) - new-val (deref-or-value model) - new-val (if (< new-val minimum) minimum new-val) - new-val (if (> new-val maximum) maximum new-val) - cmerger (merge-css input-time-css-spec args)] + (validate-args-macro input-time-args-desc args) + (validate-arg-times (deref-or-value model) minimum maximum args) + (let [deref-model (deref-or-value model) + text-model (reagent/atom (time->text deref-model)) + previous-model (reagent/atom deref-model)] + (fn input-time-render + [& {:keys [model on-change minimum maximum disabled? show-icon? hide-border? width height class style attr parts src debug-as] :as args + :or {minimum 0 maximum 2359}}] + (or + (validate-args-macro input-time-args-desc args) + (validate-arg-times (deref-or-value model) minimum maximum args) + (let [style (merge (when hide-border? {:border "none"}) + style) + new-val (deref-or-value model) + new-val (if (< new-val minimum) minimum new-val) + new-val (if (> new-val maximum) maximum new-val) + cmerger (merge-css input-time-css-spec args)] ;; if the model is different to that currently shown in text, then reset the text to match ;; other than that we want to keep the current text, because the user is probably typing - (when (not= @previous-model new-val) - (reset! text-model (time->text new-val)) - (reset! previous-model new-val)) - - (add-map-to-hiccup-call - (cmerger :wrapper) - [h-box - :src src - :debug-as (or debug-as (reflect-current-component)) - :children [[:input - (merge - (flatten-attr - (cmerger :main {:width width})) - {:type "text" - :value @text-model - :disabled (deref-or-value disabled?) - :on-change (handler-fn (on-new-keypress event text-model)) - :on-blur (handler-fn (on-defocus text-model minimum maximum on-change @previous-model)) - :on-key-up (handler-fn (lose-focus-if-enter event))})] - (when show-icon? + (when (not= @previous-model new-val) + (reset! text-model (time->text new-val)) + (reset! previous-model new-val)) + + (add-map-to-hiccup-call + (cmerger :wrapper) + [h-box + :src src + :debug-as (or debug-as (reflect-current-component)) + :children [[:input + (merge + (flatten-attr + (cmerger :main {:width width})) + {:type "text" + :value @text-model + :disabled (deref-or-value disabled?) + :on-change (handler-fn (on-new-keypress event text-model)) + :on-blur (handler-fn (on-defocus text-model minimum maximum on-change @previous-model)) + :on-key-up (handler-fn (lose-focus-if-enter event))})] + (when show-icon? ;; Leaving time-icon class (below) for backwards compatibility only. - [:div - (flatten-attr (cmerger :time-icon-container)) - [:i - (flatten-attr (cmerger :time-icon))]])]]))))))) + [:div + (flatten-attr (cmerger :time-icon-container)) + [:i + (flatten-attr (cmerger :time-icon))]])]]))))))) diff --git a/src/re_com/modal_panel.cljs b/src/re_com/modal_panel.cljs index e068f77a..edd463c5 100644 --- a/src/re_com/modal_panel.cljs +++ b/src/re_com/modal_panel.cljs @@ -3,10 +3,10 @@ [re-com.core :refer [handler-fn at]] [re-com.validate :refer [validate-args-macro]]) (:require - [re-com.config :refer [include-args-desc?]] - [re-com.debug :refer [->attr]] - [re-com.util :refer [merge-css add-map-to-hiccup-call flatten-attr]] - [re-com.validate :refer [string-or-hiccup? number-or-string? css-style? html-attr? parts?]])) + [re-com.config :refer [include-args-desc?]] + [re-com.debug :refer [->attr]] + [re-com.util :refer [merge-css add-map-to-hiccup-call flatten-attr]] + [re-com.validate :refer [string-or-hiccup? number-or-string? css-style? html-attr? parts?]])) ;; ------------------------------------------------------------------------------------ ;; modal-panel @@ -71,10 +71,10 @@ :or {wrap-nicely? true} :as args}] (or - (validate-args-macro modal-panel-args-desc args) - (let [cmerger (merge-css modal-panel-css-spec args)] - [:div ;; Containing div - (merge (flatten-attr (cmerger :main)) + (validate-args-macro modal-panel-args-desc args) + (let [cmerger (merge-css modal-panel-css-spec args)] + [:div ;; Containing div + (merge (flatten-attr (cmerger :main)) (->attr args)) [:div ;; Backdrop (flatten-attr @@ -83,5 +83,5 @@ (.preventDefault event) (.stopPropagation event))}}))] [:div ;; Child container - (cmerger :child-container {:wrap-nicely? wrap-nicely?}) + (cmerger :child-container {:wrap-nicely? wrap-nicely?}) child]]))) diff --git a/src/re_com/multi_select.cljs b/src/re_com/multi_select.cljs index 5745d04a..1ae6b31e 100644 --- a/src/re_com/multi_select.cljs +++ b/src/re_com/multi_select.cljs @@ -3,19 +3,19 @@ [re-com.core :refer [handler-fn at]] [re-com.validate :refer [validate-args-macro]]) (:require - [clojure.set :as set] - [clojure.string :as string] - [goog.string :as gstring] - [re-com.config :refer [include-args-desc?]] - [re-com.debug :refer [->attr]] - [re-com.input-text :refer [input-text]] - [re-com.box :as box] - [re-com.text :as text] - [re-com.buttons :as buttons] - [re-com.close-button :as close-button] - [re-com.util :as rc.util :refer [deref-or-value add-map-to-hiccup-call flatten-attr merge-css]] - [re-com.validate :as validate :refer [string-or-hiccup? parts?]] - [reagent.core :as reagent])) + [clojure.set :as set] + [clojure.string :as string] + [goog.string :as gstring] + [re-com.config :refer [include-args-desc?]] + [re-com.debug :refer [->attr]] + [re-com.input-text :refer [input-text]] + [re-com.box :as box] + [re-com.text :as text] + [re-com.buttons :as buttons] + [re-com.close-button :as close-button] + [re-com.util :as rc.util :refer [deref-or-value add-map-to-hiccup-call flatten-attr merge-css]] + [re-com.validate :as validate :refer [string-or-hiccup? parts?]] + [reagent.core :as reagent])) (declare multi-select-css-spec) @@ -306,9 +306,9 @@ :right-filter-result-count {:class ["rc-multi-select-right-filter-result-count"] :style {:font-size "smaller"}} :group-heading-item {:class (fn [{:keys [selected? mouse-over?]}] - ["group-result" (if selected? - "highlighted" - (when mouse-over? "mouseover"))]) + ["group-result" (if selected? + "highlighted" + (when mouse-over? "mouseover"))]) :style (fn [{:keys [disabled? selected?]}] (merge {:padding-left "6px" :cursor (when-not disabled? "pointer") @@ -380,84 +380,84 @@ RHS - selections - comes from model => internal-model - the selected items from choices collection " (or - (validate-args-macro multi-select-args-desc args) - (let [*external-model (reagent/atom (deref-or-value model)) ;; Holds the last known external value of model, to detect external model changes - *internal-model (reagent/atom @*external-model) ;; Create a new atom from the model to be used internally - *current-choice-id (reagent/atom nil) - *current-selection-id (reagent/atom nil) - *choice-group-heading-selected? (reagent/atom false) - *selection-group-heading-selected? (reagent/atom false) - *warning-message (reagent/atom nil) - *filter-choices-text (reagent/atom "") - *filter-selections-text (reagent/atom "")] - (fn multi-select-render - [& {:keys [choices model required? max-selected-items left-label right-label on-change disabled? filter-box? regex-filter? - placeholder width height max-height tab-index id-fn label-fn group-fn sort-fn filter-fn class style attr parts src] - :or {id-fn :id - label-fn :label - group-fn :group - sort-fn compare - required? false} - :as args}] - (or - (validate-args-macro multi-select-args-desc args) - (let [required? (deref-or-value required?) - filter-box? (deref-or-value filter-box?) - regex-filter? (deref-or-value regex-filter?) - min-msg "Must have at least one" - max-msg (str "Max items allowed is " max-selected-items) - group-fn (or group-fn ::$$$) ;; TODO: If nil is passed because of a when, this will prevent exceptions...smelly! - choices (set (deref-or-value choices)) - disabled? (deref-or-value disabled?) - regex-filter? (deref-or-value regex-filter?) - *latest-ext-model (reagent/atom (deref-or-value model)) - _ (when (not= @*external-model @*latest-ext-model) ;; Has model changed externally? - (reset! *external-model @*latest-ext-model) - (reset! *internal-model @*latest-ext-model)) - changeable? (and on-change (not disabled?)) - excludable? (and @*current-selection-id (> (count @*internal-model) (if required? 1 0))) - filter-fn (or filter-fn (comp str label-fn)) - choices-filter-fn (if regex-filter? - (filter-items-regex group-fn filter-fn @*filter-choices-text) - (filter-items group-fn filter-fn @*filter-choices-text)) - filtered-choices (into [] - (->> choices - (remove #(contains? @*internal-model (id-fn %))) - (filter choices-filter-fn) - (sort-by sort-fn))) - selections (into [] - (->> @*internal-model - (map #(rc.util/item-for-id % choices :id-fn id-fn)) - (sort-by sort-fn))) - selections-filter-fn (if regex-filter? - (filter-items-regex group-fn filter-fn @*filter-selections-text) - (filter-items group-fn filter-fn @*filter-selections-text)) - filtered-selections (into [] - (->> selections - (filter selections-filter-fn) - (sort-by sort-fn))) - potential-count (->> @*internal-model - (set/difference (set (map id-fn choices))) - count) - chosen-count (count selections) - choice-click (fn [id group-heading-selected?] - (reset! *current-choice-id id) - (reset! *choice-group-heading-selected? group-heading-selected?) - (reset! *warning-message nil)) - selection-click (fn [id group-heading-selected?] - (reset! *current-selection-id id) - (reset! *selection-group-heading-selected? group-heading-selected?) - (reset! *warning-message nil)) - include-filtered-click #(do (if (and (some? max-selected-items) (> (+ (count @*internal-model) (count filtered-choices)) max-selected-items)) - (reset! *warning-message max-msg) - (do - (swap! *internal-model conj @*current-choice-id) - (reset! *warning-message nil))) + (validate-args-macro multi-select-args-desc args) + (let [*external-model (reagent/atom (deref-or-value model)) ;; Holds the last known external value of model, to detect external model changes + *internal-model (reagent/atom @*external-model) ;; Create a new atom from the model to be used internally + *current-choice-id (reagent/atom nil) + *current-selection-id (reagent/atom nil) + *choice-group-heading-selected? (reagent/atom false) + *selection-group-heading-selected? (reagent/atom false) + *warning-message (reagent/atom nil) + *filter-choices-text (reagent/atom "") + *filter-selections-text (reagent/atom "")] + (fn multi-select-render + [& {:keys [choices model required? max-selected-items left-label right-label on-change disabled? filter-box? regex-filter? + placeholder width height max-height tab-index id-fn label-fn group-fn sort-fn filter-fn class style attr parts src] + :or {id-fn :id + label-fn :label + group-fn :group + sort-fn compare + required? false} + :as args}] + (or + (validate-args-macro multi-select-args-desc args) + (let [required? (deref-or-value required?) + filter-box? (deref-or-value filter-box?) + regex-filter? (deref-or-value regex-filter?) + min-msg "Must have at least one" + max-msg (str "Max items allowed is " max-selected-items) + group-fn (or group-fn ::$$$) ;; TODO: If nil is passed because of a when, this will prevent exceptions...smelly! + choices (set (deref-or-value choices)) + disabled? (deref-or-value disabled?) + regex-filter? (deref-or-value regex-filter?) + *latest-ext-model (reagent/atom (deref-or-value model)) + _ (when (not= @*external-model @*latest-ext-model) ;; Has model changed externally? + (reset! *external-model @*latest-ext-model) + (reset! *internal-model @*latest-ext-model)) + changeable? (and on-change (not disabled?)) + excludable? (and @*current-selection-id (> (count @*internal-model) (if required? 1 0))) + filter-fn (or filter-fn (comp str label-fn)) + choices-filter-fn (if regex-filter? + (filter-items-regex group-fn filter-fn @*filter-choices-text) + (filter-items group-fn filter-fn @*filter-choices-text)) + filtered-choices (into [] + (->> choices + (remove #(contains? @*internal-model (id-fn %))) + (filter choices-filter-fn) + (sort-by sort-fn))) + selections (into [] + (->> @*internal-model + (map #(rc.util/item-for-id % choices :id-fn id-fn)) + (sort-by sort-fn))) + selections-filter-fn (if regex-filter? + (filter-items-regex group-fn filter-fn @*filter-selections-text) + (filter-items group-fn filter-fn @*filter-selections-text)) + filtered-selections (into [] + (->> selections + (filter selections-filter-fn) + (sort-by sort-fn))) + potential-count (->> @*internal-model + (set/difference (set (map id-fn choices))) + count) + chosen-count (count selections) + choice-click (fn [id group-heading-selected?] + (reset! *current-choice-id id) + (reset! *choice-group-heading-selected? group-heading-selected?) + (reset! *warning-message nil)) + selection-click (fn [id group-heading-selected?] + (reset! *current-selection-id id) + (reset! *selection-group-heading-selected? group-heading-selected?) + (reset! *warning-message nil)) + include-filtered-click #(do (if (and (some? max-selected-items) (> (+ (count @*internal-model) (count filtered-choices)) max-selected-items)) + (reset! *warning-message max-msg) + (do + (swap! *internal-model conj @*current-choice-id) + (reset! *warning-message nil))) (when (and changeable? (not= @*internal-model @*latest-ext-model)) (reset! *external-model @*internal-model) (on-change @*internal-model)) (reset! *current-choice-id nil)) - include-click #(do (if @*choice-group-heading-selected? + include-click #(do (if @*choice-group-heading-selected? (let [choices-to-include (->> filtered-choices (filter (fn [item] (= (first @*current-choice-id) (group-fn item)))) (map id-fn) ;; TODO: Need to realise map output for prod build (dev doesn't need it). Why? @@ -512,193 +512,193 @@ (reset! *external-model @*internal-model) (on-change @*internal-model)) (reset! *current-selection-id nil)) - cmerger (merge-css multi-select-css-spec args)] - [:div - (merge - (flatten-attr (cmerger :main {:width width})) - (->attr args)) ;; Prevent user text selection - (add-map-to-hiccup-call - (cmerger :container) - [box/h-box - :src (at) - :height height - :max-height max-height - :gap "4px" - :children [(add-map-to-hiccup-call - (cmerger :left) - [box/v-box - :src (at) - :size "50%" - :gap "4px" - :children [(when left-label - (if (string? left-label) - (add-map-to-hiccup-call - (cmerger :left-label-container) - [box/h-box - :src (at) - :justify :between - :children [[:span - (flatten-attr (cmerger :left-label)) - left-label] - [:span - (flatten-attr - (cmerger :left-label-item-count)) - (if (string/blank? @*filter-choices-text) - (rc.util/pluralize potential-count "item") - (str "showing " (count filtered-choices) " of " potential-count))]]]) - left-label)) - (add-map-to-hiccup-call - (cmerger :left-list-box) - [list-box - :src (at) - :items filtered-choices - :id-fn id-fn - :label-fn label-fn - :group-fn group-fn - :disabled? disabled? - :*current-item-id *current-choice-id - :group-heading-selected? @*choice-group-heading-selected? - :click-callback choice-click - :double-click-callback include-click - :filter-choices-text @*filter-choices-text]) - (when filter-box? - [:<> - [box/gap - :src (at) - :size "4px"] - [filter-text-box *filter-choices-text placeholder *warning-message disabled? parts] - [box/gap - :src (at) - :size "4px"] - (if (string/blank? @*filter-choices-text) - [text/label - :src (at) - :label (gstring/unescapeEntities " ") - :style {:font-size "smaller"}] - (add-map-to-hiccup-call - (cmerger :left-filter-result-count) - [text/label - :src (at) - :label [:span "Found " (rc.util/pluralize (count filtered-choices) "match" "matches") " containing " [:strong @*filter-choices-text]]]))])]]) + cmerger (merge-css multi-select-css-spec args)] + [:div + (merge + (flatten-attr (cmerger :main {:width width})) + (->attr args)) ;; Prevent user text selection + (add-map-to-hiccup-call + (cmerger :container) + [box/h-box + :src (at) + :height height + :max-height max-height + :gap "4px" + :children [(add-map-to-hiccup-call + (cmerger :left) + [box/v-box + :src (at) + :size "50%" + :gap "4px" + :children [(when left-label + (if (string? left-label) + (add-map-to-hiccup-call + (cmerger :left-label-container) + [box/h-box + :src (at) + :justify :between + :children [[:span + (flatten-attr (cmerger :left-label)) + left-label] + [:span + (flatten-attr + (cmerger :left-label-item-count)) + (if (string/blank? @*filter-choices-text) + (rc.util/pluralize potential-count "item") + (str "showing " (count filtered-choices) " of " potential-count))]]]) + left-label)) + (add-map-to-hiccup-call + (cmerger :left-list-box) + [list-box + :src (at) + :items filtered-choices + :id-fn id-fn + :label-fn label-fn + :group-fn group-fn + :disabled? disabled? + :*current-item-id *current-choice-id + :group-heading-selected? @*choice-group-heading-selected? + :click-callback choice-click + :double-click-callback include-click + :filter-choices-text @*filter-choices-text]) + (when filter-box? + [:<> + [box/gap + :src (at) + :size "4px"] + [filter-text-box *filter-choices-text placeholder *warning-message disabled? parts] + [box/gap + :src (at) + :size "4px"] + (if (string/blank? @*filter-choices-text) + [text/label + :src (at) + :label (gstring/unescapeEntities " ") + :style {:font-size "smaller"}] + (add-map-to-hiccup-call + (cmerger :left-filter-result-count) + [text/label + :src (at) + :label [:span "Found " (rc.util/pluralize (count filtered-choices) "match" "matches") " containing " [:strong @*filter-choices-text]]]))])]]) - (add-map-to-hiccup-call - (cmerger :middle-container) - [box/v-box - :src (at) - :justify :between - :children [(add-map-to-hiccup-call - (cmerger :middle-spacer) - [box/box - :src (at) - :size "0 1 22px" ;; 22 = (+ 18 4) - height of the top components - :child ""]) - (add-map-to-hiccup-call - (cmerger :middle) - [box/v-box - :src (at) - :justify :center - :children [(add-map-to-hiccup-call - (cmerger :include-all-button) - [multi-button - :src (at) - :label (str " include " (if (string/blank? @*filter-choices-text) potential-count (count filtered-choices))) - :icon "fast-forward" - :disabled? (or disabled? (zero? (count filtered-choices))) - :on-click include-filtered-click]) - (add-map-to-hiccup-call - (cmerger :include-selected-button) - [multi-button - :src (at) - :label (str " include " (when @*choice-group-heading-selected? - (->> filtered-choices ;; TODO: Inefficient - (filter (fn [item] (= (first @*current-choice-id) (group-fn item)))) - count))) - :icon "play" - :disabled? (or disabled? (not @*current-choice-id)) - :on-click include-click]) - (add-map-to-hiccup-call - (cmerger :exclude-selected-button) - [multi-button - :src (at) - :label (str " exclude " (when @*selection-group-heading-selected? - (->> filtered-selections ;; TODO: Inefficient - (filter (fn [item] (= (first @*current-selection-id) (group-fn item)))) - count))) + (add-map-to-hiccup-call + (cmerger :middle-container) + [box/v-box + :src (at) + :justify :between + :children [(add-map-to-hiccup-call + (cmerger :middle-spacer) + [box/box + :src (at) + :size "0 1 22px" ;; 22 = (+ 18 4) - height of the top components + :child ""]) + (add-map-to-hiccup-call + (cmerger :middle) + [box/v-box + :src (at) + :justify :center + :children [(add-map-to-hiccup-call + (cmerger :include-all-button) + [multi-button + :src (at) + :label (str " include " (if (string/blank? @*filter-choices-text) potential-count (count filtered-choices))) + :icon "fast-forward" + :disabled? (or disabled? (zero? (count filtered-choices))) + :on-click include-filtered-click]) + (add-map-to-hiccup-call + (cmerger :include-selected-button) + [multi-button + :src (at) + :label (str " include " (when @*choice-group-heading-selected? + (->> filtered-choices ;; TODO: Inefficient + (filter (fn [item] (= (first @*current-choice-id) (group-fn item)))) + count))) + :icon "play" + :disabled? (or disabled? (not @*current-choice-id)) + :on-click include-click]) + (add-map-to-hiccup-call + (cmerger :exclude-selected-button) + [multi-button + :src (at) + :label (str " exclude " (when @*selection-group-heading-selected? + (->> filtered-selections ;; TODO: Inefficient + (filter (fn [item] (= (first @*current-selection-id) (group-fn item)))) + count))) ;;TODO: Zmdi reference should be in -css-spec - :icon "play zmdi-hc-rotate-180" - :disabled? (or disabled? (not excludable?)) - :on-click exclude-click]) - (add-map-to-hiccup-call - (cmerger :exclude-all-button) - [multi-button - :src (at) - :label (str " exclude " (if (string/blank? @*filter-selections-text) chosen-count (count filtered-selections))) - :icon "fast-rewind" - :disabled? (or disabled? (zero? (count filtered-selections)) (not (> (count @*internal-model) (if required? 1 0)))) - :on-click exclude-filtered-click])]]) - [box/box - :src (at) - :size (str "0 2 " (if filter-box? "55px" "0px")) ;; 55 = (+ 4 4 28 4 15) - height of the bottom components + :icon "play zmdi-hc-rotate-180" + :disabled? (or disabled? (not excludable?)) + :on-click exclude-click]) + (add-map-to-hiccup-call + (cmerger :exclude-all-button) + [multi-button + :src (at) + :label (str " exclude " (if (string/blank? @*filter-selections-text) chosen-count (count filtered-selections))) + :icon "fast-rewind" + :disabled? (or disabled? (zero? (count filtered-selections)) (not (> (count @*internal-model) (if required? 1 0)))) + :on-click exclude-filtered-click])]]) + [box/box + :src (at) + :size (str "0 2 " (if filter-box? "55px" "0px")) ;; 55 = (+ 4 4 28 4 15) - height of the bottom components ;:style {:background-color "lightblue"} - :child ""]]]) - (add-map-to-hiccup-call - (cmerger :right) - [box/v-box - :src (at) - :size "50%" - :gap "4px" - :children [^{:key (gensym)} - (add-map-to-hiccup-call - (cmerger :warning-message {:warning-message @*warning-message}) - [text/label - :src (at) - :label @*warning-message]) - (when right-label - (if (string? right-label) - (add-map-to-hiccup-call - (cmerger :right-label-container) - [box/h-box - :src (at) - :justify :between - :children [[:span - (flatten-attr (cmerger :right-label)) - right-label] - [:span - (flatten-attr (cmerger :right-label-item-count)) - (if (string/blank? @*filter-selections-text) - (rc.util/pluralize chosen-count "item") - (str "showing " (count filtered-selections) " of " chosen-count))]]]) - right-label)) - (add-map-to-hiccup-call - (cmerger :right-list-box) - [list-box - :src (at) - :items filtered-selections - :id-fn id-fn - :label-fn label-fn - :group-fn group-fn - :disabled? disabled? - :*current-item-id *current-selection-id - :group-heading-selected? @*selection-group-heading-selected? - :click-callback selection-click - :double-click-callback exclude-click - :filter-choices-text @*filter-selections-text]) - (when filter-box? - [:<> - [box/gap - :src (at) - :size "4px"] - [filter-text-box *filter-selections-text placeholder *warning-message disabled? parts] - [box/gap - :src (at) - :size "4px"] - (if (string/blank? @*filter-selections-text) - [text/label - :src (at) - :label (gstring/unescapeEntities " ") - :style {:font-size "smaller"}] - (add-map-to-hiccup-call - (cmerger :right-filter-result-count) - [text/label - :src (at) - :label [:span "Found " (rc.util/pluralize (count filtered-selections) "match" "matches") " containing " [:strong @*filter-selections-text]]]))])]])]])])))))) + :child ""]]]) + (add-map-to-hiccup-call + (cmerger :right) + [box/v-box + :src (at) + :size "50%" + :gap "4px" + :children [^{:key (gensym)} + (add-map-to-hiccup-call + (cmerger :warning-message {:warning-message @*warning-message}) + [text/label + :src (at) + :label @*warning-message]) + (when right-label + (if (string? right-label) + (add-map-to-hiccup-call + (cmerger :right-label-container) + [box/h-box + :src (at) + :justify :between + :children [[:span + (flatten-attr (cmerger :right-label)) + right-label] + [:span + (flatten-attr (cmerger :right-label-item-count)) + (if (string/blank? @*filter-selections-text) + (rc.util/pluralize chosen-count "item") + (str "showing " (count filtered-selections) " of " chosen-count))]]]) + right-label)) + (add-map-to-hiccup-call + (cmerger :right-list-box) + [list-box + :src (at) + :items filtered-selections + :id-fn id-fn + :label-fn label-fn + :group-fn group-fn + :disabled? disabled? + :*current-item-id *current-selection-id + :group-heading-selected? @*selection-group-heading-selected? + :click-callback selection-click + :double-click-callback exclude-click + :filter-choices-text @*filter-selections-text]) + (when filter-box? + [:<> + [box/gap + :src (at) + :size "4px"] + [filter-text-box *filter-selections-text placeholder *warning-message disabled? parts] + [box/gap + :src (at) + :size "4px"] + (if (string/blank? @*filter-selections-text) + [text/label + :src (at) + :label (gstring/unescapeEntities " ") + :style {:font-size "smaller"}] + (add-map-to-hiccup-call + (cmerger :right-filter-result-count) + [text/label + :src (at) + :label [:span "Found " (rc.util/pluralize (count filtered-selections) "match" "matches") " containing " [:strong @*filter-selections-text]]]))])]])]])])))))) diff --git a/src/re_com/popover.cljs b/src/re_com/popover.cljs index 889f8103..beaa8188 100644 --- a/src/re_com/popover.cljs +++ b/src/re_com/popover.cljs @@ -3,18 +3,17 @@ [re-com.core :refer [handler-fn at reflect-current-component]] [re-com.validate :refer [validate-args-macro]]) (:require - [re-com.config :refer [include-args-desc?]] - [re-com.debug :refer [->attr]] - [re-com.util :refer [get-element-by-id px deref-or-value sum-scroll-offsets merge-css add-map-to-hiccup-call flatten-attr]] - [re-com.box :refer [box h-box v-box flex-child-style flex-flow-style align-style]] - [re-com.close-button :refer [close-button]] - [re-com.validate :refer [position? position-options-list popover-status-type? popover-status-types-list number-or-string? - string-or-hiccup? string-or-atom? vector-of-maps? css-style? html-attr? parts?]] - [clojure.string :as string] - [react :as react] - [reagent.core :as reagent] - [reagent.ratom :refer-macros [reaction]])) - + [re-com.config :refer [include-args-desc?]] + [re-com.debug :refer [->attr]] + [re-com.util :refer [get-element-by-id px deref-or-value sum-scroll-offsets merge-css add-map-to-hiccup-call flatten-attr]] + [re-com.box :refer [box h-box v-box flex-child-style flex-flow-style align-style]] + [re-com.close-button :refer [close-button]] + [re-com.validate :refer [position? position-options-list popover-status-type? popover-status-types-list number-or-string? + string-or-hiccup? string-or-atom? vector-of-maps? css-style? html-attr? parts?]] + [clojure.string :as string] + [react :as react] + [reagent.core :as reagent] + [reagent.ratom :refer-macros [reaction]])) (defn point [x y] @@ -84,29 +83,29 @@ (def popover-arrow-css-spec {:arrow {:class ["popover-arrow" "rc-popover-arrow"] - :style (fn [{:keys [orientation pop-offset arrow-length arrow-width]}] - {:position "absolute" - (case orientation ;; Connect arrow to edge of popover - :left :right - :right :left - :above :bottom - :below :top) (px arrow-length :negative) - - (case orientation ;; Position the arrow at the top/left, center or bottom/right of the popover - (:left :right) :top - (:above :below) :left) (if (nil? pop-offset) "50%" (px pop-offset)) - - (case orientation ;; Adjust the arrow position so it's center is attached to the desired position set above - (:left :right) :margin-top - (:above :below) :margin-left) (px (/ arrow-width 2) :negative) - - :width (px (case orientation ;; Arrow is rendered in a rectangle so choose the correct edge length - (:left :right) arrow-length - (:above :below) arrow-width)) - - :height (px (case orientation ;; Same as :width comment above - (:left :right) arrow-width - (:above :below) arrow-length))})} + :style (fn [{:keys [orientation pop-offset arrow-length arrow-width]}] + {:position "absolute" + (case orientation ;; Connect arrow to edge of popover + :left :right + :right :left + :above :bottom + :below :top) (px arrow-length :negative) + + (case orientation ;; Position the arrow at the top/left, center or bottom/right of the popover + (:left :right) :top + (:above :below) :left) (if (nil? pop-offset) "50%" (px pop-offset)) + + (case orientation ;; Adjust the arrow position so it's center is attached to the desired position set above + (:left :right) :margin-top + (:above :below) :margin-left) (px (/ arrow-width 2) :negative) + + :width (px (case orientation ;; Arrow is rendered in a rectangle so choose the correct edge length + (:left :right) arrow-length + (:above :below) arrow-width)) + + :height (px (case orientation ;; Same as :width comment above + (:left :right) arrow-width + (:above :below) arrow-length))})} :arrow-drawing {:style (fn [{:keys [popover-color grey-arrow? popover-border-color no-border?]}] {:fill (if popover-color @@ -161,15 +160,14 @@ "Renders a backdrop div which fills the entire page and responds to clicks on it. Can also specify how tranparent it should be" [& {:keys [opacity on-click class style attr] :as args}] (or - (validate-args-macro backdrop-args-desc args) - (let [cmerger (merge-css backdrop-css-spec args)] - [:div - (merge - (flatten-attr - (cmerger :main - {:attr {:on-click (handler-fn (on-click))}})) - (->attr args))]))) - + (validate-args-macro backdrop-args-desc args) + (let [cmerger (merge-css backdrop-css-spec args)] + [:div + (merge + (flatten-attr + (cmerger :main + {:attr {:on-click (handler-fn (on-click))}})) + (->attr args))]))) ;;-------------------------------------------------------------------------------------------------- ;; Component: popover-title @@ -210,34 +208,33 @@ [& {:keys [showing? title close-button? close-callback class style attr parts] :as args}] (or - (validate-args-macro popover-title-args-desc args) - #_(assert (or ((complement nil?) showing?) ((complement nil?) close-callback)) "Must specify either showing? OR close-callback") ;; IJ: TODO re-refactor - (let [close-button? (if (nil? close-button?) true close-button?) - cmerger (merge-css popover-title-css-spec args)] - [:h3 - (merge - (flatten-attr (cmerger :main)) - (->attr args)) - (add-map-to-hiccup-call - (cmerger :container) - [h-box - :src (at) - :justify :between - :align :center - :children [title - (when close-button? - (add-map-to-hiccup-call - (cmerger :close-button) - [close-button - :src (at) - :on-click #(if close-callback - (close-callback) - (reset! showing? false)) - :div-size 0 - :font-size 26 - :top-offset -1 - :left-offset -5]))]])]))) - + (validate-args-macro popover-title-args-desc args) + #_(assert (or ((complement nil?) showing?) ((complement nil?) close-callback)) "Must specify either showing? OR close-callback") ;; IJ: TODO re-refactor + (let [close-button? (if (nil? close-button?) true close-button?) + cmerger (merge-css popover-title-css-spec args)] + [:h3 + (merge + (flatten-attr (cmerger :main)) + (->attr args)) + (add-map-to-hiccup-call + (cmerger :container) + [h-box + :src (at) + :justify :between + :align :center + :children [title + (when close-button? + (add-map-to-hiccup-call + (cmerger :close-button) + [close-button + :src (at) + :on-click #(if close-callback + (close-callback) + (reset! showing? false)) + :div-size 0 + :font-size 26 + :top-offset -1 + :left-offset -5]))]])]))) ;;-------------------------------------------------------------------------------------------------- ;; Component: popover-border @@ -347,84 +344,83 @@ "Renders an element or control along with a Bootstrap popover" [& {:keys [position position-offset title src] :as args}] (or - (validate-args-macro popover-border-args-desc args) - (let [pop-id (gensym "popover-") - rendered-once (reagent/atom false) ;; The initial render is off screen because rendering it in place does not render at final width, and we need to measure it to be able to place it properly - ready-to-show? (reagent/atom false) ;; This is used by the optimal position code to avoid briefly seeing it in its intended position before quickly moving to the optimal position - p-width (reagent/atom 0) - p-height (reagent/atom 0) - pop-offset (reagent/atom 0) - found-optimal (reagent/atom false) - !pop-border (atom nil) - ref! (partial reset! !pop-border) - calc-metrics (fn [pos] - (let [popover-elem (get-element-by-id pop-id) - [orientation arrow-pos] (split-keyword pos "-") - grey-arrow? (and title (or (= orientation :below) (= arrow-pos :below)))] - (reset! p-width (if popover-elem (next-even-integer (.-clientWidth popover-elem)) 0)) ;; next-even-integer required to avoid wiggling popovers (width/height appears to prefer being even and toggles without this call) - (reset! p-height (if popover-elem (next-even-integer (.-clientHeight popover-elem)) 0)) - (reset! pop-offset (calc-pop-offset arrow-pos position-offset @p-width @p-height)) - [orientation grey-arrow?]))] - (reagent/create-class - {:display-name "popover-border" - - :component-did-mount - (fn [] - (reset! rendered-once true)) - - :component-did-update - (fn [this] - (let [clipped? (popover-clipping @!pop-border) - anchor-node (-> @!pop-border .-parentNode .-parentNode .-parentNode)] ;; Get reference to rc-point-wrapper node - (when (and clipped? (not @found-optimal)) - (reset! position (calculate-optimal-position (calc-element-midpoint anchor-node))) - (reset! found-optimal true)) - (calc-metrics @position) - (reset! ready-to-show? true))) - - :reagent-render - (fn popover-border-render - [& {:keys [children position position-offset width height popover-color popover-border-color arrow-length - arrow-width arrow-gap padding margin-left margin-top tooltip-style? arrow-renderer - title class style attr parts src] - :or {arrow-length 11 arrow-width 22 arrow-gap -1} - :as args}] - (or - (validate-args-macro popover-border-args-desc args) - (let [[orientation grey-arrow?] (calc-metrics @position) - cmerger (merge-css popover-border-css-spec args)] - [:div + (validate-args-macro popover-border-args-desc args) + (let [pop-id (gensym "popover-") + rendered-once (reagent/atom false) ;; The initial render is off screen because rendering it in place does not render at final width, and we need to measure it to be able to place it properly + ready-to-show? (reagent/atom false) ;; This is used by the optimal position code to avoid briefly seeing it in its intended position before quickly moving to the optimal position + p-width (reagent/atom 0) + p-height (reagent/atom 0) + pop-offset (reagent/atom 0) + found-optimal (reagent/atom false) + !pop-border (atom nil) + ref! (partial reset! !pop-border) + calc-metrics (fn [pos] + (let [popover-elem (get-element-by-id pop-id) + [orientation arrow-pos] (split-keyword pos "-") + grey-arrow? (and title (or (= orientation :below) (= arrow-pos :below)))] + (reset! p-width (if popover-elem (next-even-integer (.-clientWidth popover-elem)) 0)) ;; next-even-integer required to avoid wiggling popovers (width/height appears to prefer being even and toggles without this call) + (reset! p-height (if popover-elem (next-even-integer (.-clientHeight popover-elem)) 0)) + (reset! pop-offset (calc-pop-offset arrow-pos position-offset @p-width @p-height)) + [orientation grey-arrow?]))] + (reagent/create-class + {:display-name "popover-border" + + :component-did-mount + (fn [] + (reset! rendered-once true)) + + :component-did-update + (fn [this] + (let [clipped? (popover-clipping @!pop-border) + anchor-node (-> @!pop-border .-parentNode .-parentNode .-parentNode)] ;; Get reference to rc-point-wrapper node + (when (and clipped? (not @found-optimal)) + (reset! position (calculate-optimal-position (calc-element-midpoint anchor-node))) + (reset! found-optimal true)) + (calc-metrics @position) + (reset! ready-to-show? true))) + + :reagent-render + (fn popover-border-render + [& {:keys [children position position-offset width height popover-color popover-border-color arrow-length + arrow-width arrow-gap padding margin-left margin-top tooltip-style? arrow-renderer + title class style attr parts src] + :or {arrow-length 11 arrow-width 22 arrow-gap -1} + :as args}] + (or + (validate-args-macro popover-border-args-desc args) + (let [[orientation grey-arrow?] (calc-metrics @position) + cmerger (merge-css popover-border-css-spec args)] + [:div + (merge + {:id pop-id} + (flatten-attr + (cmerger + :main (merge - {:id pop-id} - (flatten-attr - (cmerger - :main - (merge - (if @rendered-once - (when pop-id (calc-popover-pos orientation @p-width @p-height @pop-offset arrow-length arrow-gap)) - {:top "-10000px" :left "-10000px"}) - {:width width :height height :background-color popover-color - :border-color popover-border-color :tooltip-style? tooltip-style? - :orientation orientation :margin-left margin-left :margin-top margin-top - :ready-to-show? @ready-to-show?}))) - (->attr args) - {:ref ref!}) - [(or arrow-renderer popover-arrow) - :orientation orientation - :pop-offset @pop-offset - :arrow-length arrow-length - :arrow-width arrow-width - :grey-arrow? grey-arrow? - :tooltip-style? tooltip-style? - :popover-color popover-color - :popover-border-color popover-border-color - :parts parts] - (when title title) - (into [:div - (flatten-attr - (cmerger :content {:padding padding}))] - children)])))})))) - + (if @rendered-once + (when pop-id (calc-popover-pos orientation @p-width @p-height @pop-offset arrow-length arrow-gap)) + {:top "-10000px" :left "-10000px"}) + {:width width :height height :background-color popover-color + :border-color popover-border-color :tooltip-style? tooltip-style? + :orientation orientation :margin-left margin-left :margin-top margin-top + :ready-to-show? @ready-to-show?}))) + (->attr args) + {:ref ref!}) + [(or arrow-renderer popover-arrow) + :orientation orientation + :pop-offset @pop-offset + :arrow-length arrow-length + :arrow-width arrow-width + :grey-arrow? grey-arrow? + :tooltip-style? tooltip-style? + :popover-color popover-color + :popover-border-color popover-border-color + :parts parts] + (when title title) + (into [:div + (flatten-attr + (cmerger :content {:padding padding}))] + children)])))})))) ;;-------------------------------------------------------------------------------------------------- ;; Component: popover-content-wrapper @@ -485,80 +481,79 @@ "Abstracts several components to handle the 90% of cases for general popovers and dialog boxes" [& {:keys [no-clip?] :as args}] (or - (validate-args-macro popover-content-wrapper-args-desc args) - (let [left-offset (reagent/atom 0) - top-offset (reagent/atom 0) - !ref (atom nil) - ref! (partial reset! !ref) - position-no-clip-popover (fn position-no-clip-popover - [this] - (when no-clip? - (let [popover-point-node (.-parentNode @!ref) ;; Get reference to rc-popover-point node - bounding-rect (.getBoundingClientRect popover-point-node)] ;; The modern magical way of getting offsetLeft and offsetTop. Returns this: https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIDOMClientRect - (reset! left-offset (.-left bounding-rect)) - (reset! top-offset (.-top bounding-rect)))))] - (reagent/create-class - {:display-name "popover-content-wrapper" - - :component-did-mount - (fn [this] - (position-no-clip-popover this)) - - :component-did-update - (fn [this] - (position-no-clip-popover this)) - - :reagent-render - (fn popover-content-wrapper-render - [& {:keys [showing-injected? position-injected position-offset no-clip? width height backdrop-opacity on-cancel - title close-button? body tooltip-style? popover-color popover-border-color arrow-length arrow-width - arrow-gap arrow-renderer padding class style attr parts] - :or {arrow-length 11 arrow-width 22 arrow-gap -1} - :as args}] - (or - (validate-args-macro popover-content-wrapper-args-desc args) - (let [cmerger (merge-css popover-content-wrapper-css-spec args)] - @position-injected ;; Dereference this atom. Although nothing here needs its value explicitly, the calculation of left-offset and top-offset are affected by it for :no-clip? true - [:div - (merge (flatten-attr - (cmerger :main {:no-clip? no-clip? - :left-offset @left-offset - :top-offset @top-offset})) - (->attr args) - {:ref ref!}) - (when (and (deref-or-value showing-injected?) on-cancel) - (add-map-to-hiccup-call - (cmerger :backdrop) - [backdrop - :src (at) - :opacity backdrop-opacity - :on-click on-cancel])) - (add-map-to-hiccup-call - (cmerger :border) - [popover-border - :src (at) - :position position-injected - :position-offset position-offset - :width width - :height height - :tooltip-style? tooltip-style? - :arrow-renderer arrow-renderer - :popover-color popover-color - :popover-border-color popover-border-color - :arrow-length arrow-length - :arrow-width arrow-width - :arrow-gap arrow-gap - :padding padding - :title (when title (add-map-to-hiccup-call - (cmerger :title) - [popover-title - :src (at) - :title title - :showing? showing-injected? - :close-button? close-button? - :close-callback on-cancel])) - :children [body]])])))})))) - + (validate-args-macro popover-content-wrapper-args-desc args) + (let [left-offset (reagent/atom 0) + top-offset (reagent/atom 0) + !ref (atom nil) + ref! (partial reset! !ref) + position-no-clip-popover (fn position-no-clip-popover + [this] + (when no-clip? + (let [popover-point-node (.-parentNode @!ref) ;; Get reference to rc-popover-point node + bounding-rect (.getBoundingClientRect popover-point-node)] ;; The modern magical way of getting offsetLeft and offsetTop. Returns this: https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIDOMClientRect + (reset! left-offset (.-left bounding-rect)) + (reset! top-offset (.-top bounding-rect)))))] + (reagent/create-class + {:display-name "popover-content-wrapper" + + :component-did-mount + (fn [this] + (position-no-clip-popover this)) + + :component-did-update + (fn [this] + (position-no-clip-popover this)) + + :reagent-render + (fn popover-content-wrapper-render + [& {:keys [showing-injected? position-injected position-offset no-clip? width height backdrop-opacity on-cancel + title close-button? body tooltip-style? popover-color popover-border-color arrow-length arrow-width + arrow-gap arrow-renderer padding class style attr parts] + :or {arrow-length 11 arrow-width 22 arrow-gap -1} + :as args}] + (or + (validate-args-macro popover-content-wrapper-args-desc args) + (let [cmerger (merge-css popover-content-wrapper-css-spec args)] + @position-injected ;; Dereference this atom. Although nothing here needs its value explicitly, the calculation of left-offset and top-offset are affected by it for :no-clip? true + [:div + (merge (flatten-attr + (cmerger :main {:no-clip? no-clip? + :left-offset @left-offset + :top-offset @top-offset})) + (->attr args) + {:ref ref!}) + (when (and (deref-or-value showing-injected?) on-cancel) + (add-map-to-hiccup-call + (cmerger :backdrop) + [backdrop + :src (at) + :opacity backdrop-opacity + :on-click on-cancel])) + (add-map-to-hiccup-call + (cmerger :border) + [popover-border + :src (at) + :position position-injected + :position-offset position-offset + :width width + :height height + :tooltip-style? tooltip-style? + :arrow-renderer arrow-renderer + :popover-color popover-color + :popover-border-color popover-border-color + :arrow-length arrow-length + :arrow-width arrow-width + :arrow-gap arrow-gap + :padding padding + :title (when title (add-map-to-hiccup-call + (cmerger :title) + [popover-title + :src (at) + :title title + :showing? showing-injected? + :close-button? close-button? + :close-callback on-cancel])) + :children [body]])])))})))) ;;-------------------------------------------------------------------------------------------------- ;; Component: popover-anchor-wrapper @@ -605,41 +600,40 @@ "Renders an element or control along with a Bootstrap popover" [& {:keys [showing? position src] :as args}] (or - (validate-args-macro popover-anchor-wrapper-args-desc args) - (let [external-position (reagent/atom position) - internal-position (reagent/atom @external-position) - reset-on-hide (reaction (when-not (deref-or-value showing?) - (reset! internal-position @external-position)))] - (reagent/create-class - {:display-name "popover-anchor-wrapper" - - :reagent-render - (fn popover-anchor-wrapper-render - [& {:keys [showing? position anchor popover class style attr parts] :as args}] - (or - (validate-args-macro popover-anchor-wrapper-args-desc args) - (do - @reset-on-hide ;; Dereference this reaction, otherwise it won't be set up. The reaction is set to run whenever the popover closes - (when (not= @external-position position) ;; Has position changed externally? - (reset! external-position position) - (reset! internal-position @external-position)) - (let [[orientation _arrow-pos] (split-keyword @internal-position "-") ;; only need orientation here - place-anchor-before? (case orientation (:left :above) false true) - flex-flow (case orientation (:left :right) "row" "column") - cmerger (merge-css popover-anchor-wrapper-css-spec args)] - [:div - (merge (flatten-attr (cmerger :main)) - (->attr args)) - [:div ;; Wrapper around the anchor and the "point" - (flatten-attr - (cmerger :point-wrapper {:flex-flow flex-flow})) - (when place-anchor-before? anchor) - (when (deref-or-value showing?) - [:div ;; The "point" that connects the anchor to the popover - (flatten-attr (cmerger :point)) - (into popover [:showing-injected? showing? :position-injected internal-position])]) ;; NOTE: Inject showing? and position to the popover - (when-not place-anchor-before? anchor)]]))))})))) - + (validate-args-macro popover-anchor-wrapper-args-desc args) + (let [external-position (reagent/atom position) + internal-position (reagent/atom @external-position) + reset-on-hide (reaction (when-not (deref-or-value showing?) + (reset! internal-position @external-position)))] + (reagent/create-class + {:display-name "popover-anchor-wrapper" + + :reagent-render + (fn popover-anchor-wrapper-render + [& {:keys [showing? position anchor popover class style attr parts] :as args}] + (or + (validate-args-macro popover-anchor-wrapper-args-desc args) + (do + @reset-on-hide ;; Dereference this reaction, otherwise it won't be set up. The reaction is set to run whenever the popover closes + (when (not= @external-position position) ;; Has position changed externally? + (reset! external-position position) + (reset! internal-position @external-position)) + (let [[orientation _arrow-pos] (split-keyword @internal-position "-") ;; only need orientation here + place-anchor-before? (case orientation (:left :above) false true) + flex-flow (case orientation (:left :right) "row" "column") + cmerger (merge-css popover-anchor-wrapper-css-spec args)] + [:div + (merge (flatten-attr (cmerger :main)) + (->attr args)) + [:div ;; Wrapper around the anchor and the "point" + (flatten-attr + (cmerger :point-wrapper {:flex-flow flex-flow})) + (when place-anchor-before? anchor) + (when (deref-or-value showing?) + [:div ;; The "point" that connects the anchor to the popover + (flatten-attr (cmerger :point)) + (into popover [:showing-injected? showing? :position-injected internal-position])]) ;; NOTE: Inject showing? and position to the popover + (when-not place-anchor-before? anchor)]]))))})))) ;;-------------------------------------------------------------------------------------------------- ;; Component: popover-tooltip @@ -696,53 +690,53 @@ :or {no-clip? true} :as args}] (or - (validate-args-macro popover-tooltip-args-desc args) - (let [label (deref-or-value label) - cmerger (merge-css popover-tooltip-css-spec args)] - (add-map-to-hiccup-call - (cmerger :main) - [popover-anchor-wrapper - :src src - :debug-as (or debug-as (reflect-current-component)) - :showing? showing? - :position (or position :below-center) - :anchor anchor - :popover (add-map-to-hiccup-call - (cmerger :content-wrapper) - [popover-content-wrapper - :src (at) - :no-clip? no-clip? - :on-cancel on-cancel - :width width - :popover-color (case status - :warning "#f57c00" - :error "#d50000" - :info "#333333" - :success "#13C200" - "black") - :padding "3px 8px" - :arrow-length 6 - :arrow-width 12 - :arrow-gap 4 - :tooltip-style? true - :body (add-map-to-hiccup-call - (cmerger :v-box {:status status}) - [v-box - :src (at) - :children [(when close-button? - (add-map-to-hiccup-call - (cmerger :close-button-container) - [box - :src (at) - :align-self :end - :child (add-map-to-hiccup-call - (cmerger :close-button) - [close-button - :src (at) - :on-click #(if on-cancel - (on-cancel) - (reset! showing? false)) - :div-size 15 - :font-size 20 - :left-offset 5])])) - label]])])])))) + (validate-args-macro popover-tooltip-args-desc args) + (let [label (deref-or-value label) + cmerger (merge-css popover-tooltip-css-spec args)] + (add-map-to-hiccup-call + (cmerger :main) + [popover-anchor-wrapper + :src src + :debug-as (or debug-as (reflect-current-component)) + :showing? showing? + :position (or position :below-center) + :anchor anchor + :popover (add-map-to-hiccup-call + (cmerger :content-wrapper) + [popover-content-wrapper + :src (at) + :no-clip? no-clip? + :on-cancel on-cancel + :width width + :popover-color (case status + :warning "#f57c00" + :error "#d50000" + :info "#333333" + :success "#13C200" + "black") + :padding "3px 8px" + :arrow-length 6 + :arrow-width 12 + :arrow-gap 4 + :tooltip-style? true + :body (add-map-to-hiccup-call + (cmerger :v-box {:status status}) + [v-box + :src (at) + :children [(when close-button? + (add-map-to-hiccup-call + (cmerger :close-button-container) + [box + :src (at) + :align-self :end + :child (add-map-to-hiccup-call + (cmerger :close-button) + [close-button + :src (at) + :on-click #(if on-cancel + (on-cancel) + (reset! showing? false)) + :div-size 15 + :font-size 20 + :left-offset 5])])) + label]])])])))) diff --git a/src/re_com/progress_bar.cljs b/src/re_com/progress_bar.cljs index 73c83cd6..dd4bcb44 100644 --- a/src/re_com/progress_bar.cljs +++ b/src/re_com/progress_bar.cljs @@ -3,13 +3,13 @@ [re-com.core :refer [handler-fn at reflect-current-component]] [re-com.validate :refer [validate-args-macro]]) (:require - [re-com.config :refer [include-args-desc?]] - [re-com.debug :refer [->attr]] - [re-com.util :refer [deref-or-value px add-map-to-hiccup-call merge-css flatten-attr]] - [re-com.popover :refer [popover-tooltip]] - [re-com.box :refer [h-box v-box box gap line flex-child-style align-style]] - [re-com.validate :refer [input-status-type? input-status-types-list regex? string-or-hiccup? css-style? html-attr? parts? - number-or-string? string-or-atom? nillable-string-or-atom? throbber-size? throbber-sizes-list]])) + [re-com.config :refer [include-args-desc?]] + [re-com.debug :refer [->attr]] + [re-com.util :refer [deref-or-value px add-map-to-hiccup-call merge-css flatten-attr]] + [re-com.popover :refer [popover-tooltip]] + [re-com.box :refer [h-box v-box box gap line flex-child-style align-style]] + [re-com.validate :refer [input-status-type? input-status-types-list regex? string-or-hiccup? css-style? html-attr? parts? + number-or-string? string-or-atom? nillable-string-or-atom? throbber-size? throbber-sizes-list]])) ;; ------------------------------------------------------------------------------------ ;; Component: progress-bar @@ -57,22 +57,22 @@ [& {:keys [model width striped? class bar-class style attr parts src debug-as] :as args}] (or - (validate-args-macro progress-bar-args-desc args) - (let [model (deref-or-value model) - cmerger (merge-css progress-bar-css-spec args)] - (add-map-to-hiccup-call - (cmerger :wrapper) - [box - :src src - :debug-as (or debug-as (reflect-current-component)) - :align :start - :child [:div + (validate-args-macro progress-bar-args-desc args) + (let [model (deref-or-value model) + cmerger (merge-css progress-bar-css-spec args)] + (add-map-to-hiccup-call + (cmerger :wrapper) + [box + :src src + :debug-as (or debug-as (reflect-current-component)) + :align :start + :child [:div + (flatten-attr + (cmerger :main {:width width})) + [:div (flatten-attr - (cmerger :main {:width width})) - [:div - (flatten-attr - (cmerger :portion - {:striped? striped? - :percent model - :attr {:role "progressbar"}})) - (str model "%")]]])))) + (cmerger :portion + {:striped? striped? + :percent model + :attr {:role "progressbar"}})) + (str model "%")]]])))) diff --git a/src/re_com/radio_button.cljs b/src/re_com/radio_button.cljs index f61058ee..ea0b66b6 100644 --- a/src/re_com/radio_button.cljs +++ b/src/re_com/radio_button.cljs @@ -3,13 +3,13 @@ [re-com.core :refer [handler-fn at reflect-current-component]] [re-com.validate :refer [validate-args-macro]]) (:require - [re-com.config :refer [include-args-desc?]] - [re-com.debug :refer [->attr]] - [re-com.util :refer [deref-or-value px add-map-to-hiccup-call merge-css flatten-attr]] - [re-com.popover :refer [popover-tooltip]] - [re-com.box :refer [h-box v-box box gap line flex-child-style align-style]] - [re-com.validate :refer [input-status-type? input-status-types-list regex? string-or-hiccup? css-style? html-attr? parts? - number-or-string? string-or-atom? nillable-string-or-atom? throbber-size? throbber-sizes-list]])) + [re-com.config :refer [include-args-desc?]] + [re-com.debug :refer [->attr]] + [re-com.util :refer [deref-or-value px add-map-to-hiccup-call merge-css flatten-attr]] + [re-com.popover :refer [popover-tooltip]] + [re-com.box :refer [h-box v-box box gap line flex-child-style align-style]] + [re-com.validate :refer [input-status-type? input-status-types-list regex? string-or-hiccup? css-style? html-attr? parts? + number-or-string? string-or-atom? nillable-string-or-atom? throbber-size? throbber-sizes-list]])) ;; ------------------------------------------------------------------------------------ ;; Component: radio-button @@ -58,30 +58,30 @@ [& {:keys [model value on-change label disabled? label-class label-style class style attr parts src debug-as] :as args}] (or - (validate-args-macro radio-button-args-desc args) - (let [model (deref-or-value model) - disabled? (deref-or-value disabled?) - checked? (= model value) - callback-fn #(when (and on-change (not disabled?)) - (on-change value)) ;; call on-change with the :value arg - cmerger (merge-css radio-button-css-spec args)] - (add-map-to-hiccup-call - (cmerger :wrapper) - [h-box - :src src - :debug-as (or debug-as (reflect-current-component)) - :align :start - :children [[:input - (merge - (flatten-attr (cmerger :main)) - {:type "radio" - :disabled disabled? - :checked checked? - :on-change (handler-fn (callback-fn))})] - (when label - [:span - (flatten-attr - (cmerger :label {:class label-class - :style label-style - :attr {:on-click (handler-fn (callback-fn))}})) - label])]])))) + (validate-args-macro radio-button-args-desc args) + (let [model (deref-or-value model) + disabled? (deref-or-value disabled?) + checked? (= model value) + callback-fn #(when (and on-change (not disabled?)) + (on-change value)) ;; call on-change with the :value arg + cmerger (merge-css radio-button-css-spec args)] + (add-map-to-hiccup-call + (cmerger :wrapper) + [h-box + :src src + :debug-as (or debug-as (reflect-current-component)) + :align :start + :children [[:input + (merge + (flatten-attr (cmerger :main)) + {:type "radio" + :disabled disabled? + :checked checked? + :on-change (handler-fn (callback-fn))})] + (when label + [:span + (flatten-attr + (cmerger :label {:class label-class + :style label-style + :attr {:on-click (handler-fn (callback-fn))}})) + label])]])))) diff --git a/src/re_com/selection_list.cljs b/src/re_com/selection_list.cljs index 6b6f77c1..86086d74 100644 --- a/src/re_com/selection_list.cljs +++ b/src/re_com/selection_list.cljs @@ -3,14 +3,14 @@ [re-com.core :refer [handler-fn at reflect-current-component]] [re-com.validate :refer [validate-args-macro]]) (:require - [re-com.config :refer [include-args-desc?]] - [re-com.debug :refer [->attr]] - [re-com.text :refer [label]] - [re-com.checkbox :refer [checkbox]] - [re-com.radio-button :refer [radio-button]] - [re-com.box :refer [box border h-box v-box]] - [re-com.validate :refer [vector-of-maps? string-or-atom? set-or-atom? css-style? html-attr? parts?]] - [re-com.util :refer [fmap deref-or-value merge-css add-map-to-hiccup-call flatten-attr]])) + [re-com.config :refer [include-args-desc?]] + [re-com.debug :refer [->attr]] + [re-com.text :refer [label]] + [re-com.checkbox :refer [checkbox]] + [re-com.radio-button :refer [radio-button]] + [re-com.box :refer [box border h-box v-box]] + [re-com.validate :refer [vector-of-maps? string-or-atom? set-or-atom? css-style? html-attr? parts?]] + [re-com.util :refer [fmap deref-or-value merge-css add-map-to-hiccup-call flatten-attr]])) ;; ---------------------------------------------------------------------------- (defn label-style @@ -171,41 +171,41 @@ label-fn :label} :as args}] (or - (validate-args-macro selection-list-args-desc args) - (let [choices (deref-or-value choices) - model (deref-or-value model) - on-change (deref-or-value on-change) - multi-select? (deref-or-value multi-select?) - as-exclusions? (deref-or-value as-exclusions?) - required? (deref-or-value required?) - width (deref-or-value width) - height (deref-or-value height) - max-height (deref-or-value max-height) - disabled? (deref-or-value disabled?) - hide-border? (deref-or-value hide-border?) - item-renderer (deref-or-value item-renderer) - selected (if multi-select? model (-> model first vector set)) - items (map (if item-renderer - #(item-renderer % id-fn selected on-change disabled? label-fn required? as-exclusions?) ;; TODO do we need to pass id-fn? - (if multi-select? - #(as-checked % id-fn selected on-change disabled? label-fn required? as-exclusions? parts) - #(as-radio % id-fn selected on-change disabled? label-fn required? as-exclusions? parts))) - choices) - cmerger (merge-css selection-list-css-spec args)] + (validate-args-macro selection-list-args-desc args) + (let [choices (deref-or-value choices) + model (deref-or-value model) + on-change (deref-or-value on-change) + multi-select? (deref-or-value multi-select?) + as-exclusions? (deref-or-value as-exclusions?) + required? (deref-or-value required?) + width (deref-or-value width) + height (deref-or-value height) + max-height (deref-or-value max-height) + disabled? (deref-or-value disabled?) + hide-border? (deref-or-value hide-border?) + item-renderer (deref-or-value item-renderer) + selected (if multi-select? model (-> model first vector set)) + items (map (if item-renderer + #(item-renderer % id-fn selected on-change disabled? label-fn required? as-exclusions?) ;; TODO do we need to pass id-fn? + (if multi-select? + #(as-checked % id-fn selected on-change disabled? label-fn required? as-exclusions? parts) + #(as-radio % id-fn selected on-change disabled? label-fn required? as-exclusions? parts))) + choices) + cmerger (merge-css selection-list-css-spec args)] ;; In single select mode force selections to one. This causes a second render ;; TODO: GR commented this out to fix the bug where #{nil} was being returned for an empty list. Remove when we're sure there are no ill effects. - #_(when-not (= selected model) (on-change selected)) - (add-map-to-hiccup-call - (cmerger :main {:disabled? (deref-or-value disabled?)}) - [border - :src src - :debug-as (or debug-as (reflect-current-component)) - :radius "4px" - :border (when hide-border? "none") - :child (into [:div - (flatten-attr - (cmerger :list-group {:width width :height height :max-height max-height - :hide-border? hide-border?}))] - items)])))) + #_(when-not (= selected model) (on-change selected)) + (add-map-to-hiccup-call + (cmerger :main {:disabled? (deref-or-value disabled?)}) + [border + :src src + :debug-as (or debug-as (reflect-current-component)) + :radius "4px" + :border (when hide-border? "none") + :child (into [:div + (flatten-attr + (cmerger :list-group {:width width :height height :max-height max-height + :hide-border? hide-border?}))] + items)])))) diff --git a/src/re_com/simple_v_table.cljs b/src/re_com/simple_v_table.cljs index 3f310fd2..31cef83d 100644 --- a/src/re_com/simple_v_table.cljs +++ b/src/re_com/simple_v_table.cljs @@ -3,15 +3,14 @@ [re-com.core :refer [handler-fn at reflect-current-component]] [re-com.validate :refer [validate-args-macro]]) (:require - [reagent.core :as reagent] - [re-com.buttons :refer [hyperlink row-button]] - [re-com.config :refer [include-args-desc?]] - [re-com.box :refer [box h-box gap]] - [re-com.util :refer [px deref-or-value assoc-in-if-empty merge-css add-map-to-hiccup-call flatten-attr ->v position-for-id item-for-id remove-id-item clipboard-write! table->tsv]] - [re-com.text :refer [label]] - [re-com.validate :refer [vector-of-maps? vector-atom? parts?]] - [re-com.v-table :as v-table])) - + [reagent.core :as reagent] + [re-com.buttons :refer [hyperlink row-button]] + [re-com.config :refer [include-args-desc?]] + [re-com.box :refer [box h-box gap]] + [re-com.util :refer [px deref-or-value assoc-in-if-empty merge-css add-map-to-hiccup-call flatten-attr ->v position-for-id item-for-id remove-id-item clipboard-write! table->tsv]] + [re-com.text :refer [label]] + [re-com.validate :refer [vector-of-maps? vector-atom? parts?]] + [re-com.v-table :as v-table])) (def default-sort-criterion {:keyfn :label :order :asc}) @@ -72,19 +71,19 @@ (let [hover? (reagent/atom false)] (fn [{:keys [id row-label-fn width height align header-label sort-by]} parts sort-by-column] (let - [sort-by (cond (true? sort-by) {} :else sort-by) - default-sort-by {:key-fn row-label-fn :comp compare :id id :order :asc} - ps (position-for-id id @sort-by-column) - {current-order :order} (item-for-id id @sort-by-column) - add-criteria! #(swap! sort-by-column update-sort-criteria (merge default-sort-by sort-by)) - replace-criteria! #(reset! sort-by-column [(merge default-sort-by sort-by)]) - on-click #(if (or (.-shiftKey %) (empty? (remove (clojure.core/comp #{id} :id) @sort-by-column))) - (add-criteria!) - (replace-criteria!)) - justify (get align->justify (keyword align) :start) - multiple-columns-sorted? (> (count @sort-by-column) 1) - cmerger (merge-css simple-v-table-css-spec {:parts parts})] - (add-map-to-hiccup-call + [sort-by (cond (true? sort-by) {} :else sort-by) + default-sort-by {:key-fn row-label-fn :comp compare :id id :order :asc} + ps (position-for-id id @sort-by-column) + {current-order :order} (item-for-id id @sort-by-column) + add-criteria! #(swap! sort-by-column update-sort-criteria (merge default-sort-by sort-by)) + replace-criteria! #(reset! sort-by-column [(merge default-sort-by sort-by)]) + on-click #(if (or (.-shiftKey %) (empty? (remove (clojure.core/comp #{id} :id) @sort-by-column))) + (add-criteria!) + (replace-criteria!)) + justify (get align->justify (keyword align) :start) + multiple-columns-sorted? (> (count @sort-by-column) 1) + cmerger (merge-css simple-v-table-css-spec {:parts parts})] + (add-map-to-hiccup-call (cmerger :simple-column-header-item {:height height ;:align align @@ -99,22 +98,22 @@ :align :center :children [header-label (when sort-by - (add-map-to-hiccup-call - (cmerger :simple-column-header-sort - {:current-order current-order}) - [h-box - :class (str "rc-simple-v-table-column-header-sort-label " (when current-order "rc-simple-v-table-column-header-sort-active")) - :min-width "35px" - :style (when current-order {:opacity 0.3}) - :justify :center - :align :center - :children - [(case current-order - :asc [arrow-up-icon] - :desc [arrow-down-icon] - [sort-icon]) - (when ps - [label :style {:visibility (when-not multiple-columns-sorted? "hidden")} :label (inc ps)])]]))]]))))) + (add-map-to-hiccup-call + (cmerger :simple-column-header-sort + {:current-order current-order}) + [h-box + :class (str "rc-simple-v-table-column-header-sort-label " (when current-order "rc-simple-v-table-column-header-sort-active")) + :min-width "35px" + :style (when current-order {:opacity 0.3}) + :justify :center + :align :center + :children + [(case current-order + :asc [arrow-up-icon] + :desc [arrow-down-icon] + [sort-icon]) + (when ps + [label :style {:visibility (when-not multiple-columns-sorted? "hidden")} :label (inc ps)])]]))]]))))) (defn column-header-renderer ":column-header-renderer AND :top-left-renderer - Render the table header" @@ -140,28 +139,27 @@ :cell-style cell-style :row row :column column}) - (row-label-fn row)])) + (row-label-fn row)])) (defn row-renderer ":row-renderer AND :row-header-renderer: Render a single row of the table data" [columns on-click-row on-enter-row on-leave-row striped? row-height row-style cell-style parts table-row-line-color row-index row] (let [cmerger (merge-css simple-v-table-css-spec {:parts parts})] (into - [:div - (cmerger :simple-row - {:row-height row-height - :row-style row-style - :table-row-line-color table-row-line-color - :on-click-row on-click-row - :odd-row? (and striped? (odd? row-index)) - :row row - :attr {:on-click (handler-fn (do (v-table/show-row-data-on-alt-click row row-index event) - (when on-click-row (on-click-row row-index)))) - :on-mouse-enter (when on-enter-row (handler-fn (on-enter-row row-index))) - :on-mouse-leave (when on-leave-row (handler-fn (on-leave-row row-index)))}})] - (for [column columns] - [row-item row column cell-style parts])))) - + [:div + (cmerger :simple-row + {:row-height row-height + :row-style row-style + :table-row-line-color table-row-line-color + :on-click-row on-click-row + :odd-row? (and striped? (odd? row-index)) + :row row + :attr {:on-click (handler-fn (do (v-table/show-row-data-on-alt-click row row-index event) + (when on-click-row (on-click-row row-index)))) + :on-mouse-enter (when on-enter-row (handler-fn (on-enter-row row-index))) + :on-mouse-leave (when on-leave-row (handler-fn (on-leave-row row-index)))}})] + (for [column columns] + [row-item row column cell-style parts])))) (def simple-v-table-exclusive-parts-desc (when include-args-desc? @@ -178,8 +176,8 @@ (def simple-v-table-parts-desc (when include-args-desc? (into - simple-v-table-exclusive-parts-desc - (map #(update % :level inc) v-table/v-table-parts-desc)))) + simple-v-table-exclusive-parts-desc + (map #(update % :level inc) v-table/v-table-parts-desc)))) (def simple-v-table-css-spec {:simple-wrapper {:class ["rc-simple-v-table-wrapper"] diff --git a/src/re_com/slider.cljs b/src/re_com/slider.cljs index 108c693c..6512c723 100644 --- a/src/re_com/slider.cljs +++ b/src/re_com/slider.cljs @@ -3,13 +3,13 @@ [re-com.core :refer [handler-fn at reflect-current-component]] [re-com.validate :refer [validate-args-macro]]) (:require - [re-com.config :refer [include-args-desc?]] - [re-com.debug :refer [->attr]] - [re-com.util :refer [deref-or-value px add-map-to-hiccup-call merge-css flatten-attr]] - [re-com.popover :refer [popover-tooltip]] - [re-com.box :refer [h-box v-box box gap line flex-child-style align-style]] - [re-com.validate :refer [input-status-type? input-status-types-list regex? string-or-hiccup? css-style? html-attr? parts? - number-or-string? string-or-atom? nillable-string-or-atom? throbber-size? throbber-sizes-list]])) + [re-com.config :refer [include-args-desc?]] + [re-com.debug :refer [->attr]] + [re-com.util :refer [deref-or-value px add-map-to-hiccup-call merge-css flatten-attr]] + [re-com.popover :refer [popover-tooltip]] + [re-com.box :refer [h-box v-box box gap line flex-child-style align-style]] + [re-com.validate :refer [input-status-type? input-status-types-list regex? string-or-hiccup? css-style? html-attr? parts? + number-or-string? string-or-atom? nillable-string-or-atom? throbber-size? throbber-sizes-list]])) ;; ------------------------------------------------------------------------------------ ;; Component: slider @@ -57,28 +57,28 @@ :or {min 0 max 100} :as args}] (or - (validate-args-macro slider-args-desc args) - (let [model (deref-or-value model) - min (deref-or-value min) - max (deref-or-value max) - step (deref-or-value step) - disabled? (deref-or-value disabled?) - cmerger (merge-css slider-css-spec args)] - (add-map-to-hiccup-call - (cmerger :wrapper) - [box - :src src - :debug-as (or debug-as (reflect-current-component)) - :align :start - :child [:input - (merge - (flatten-attr - (cmerger :main {:width width :disabled? disabled?})) - {:type "range" + (validate-args-macro slider-args-desc args) + (let [model (deref-or-value model) + min (deref-or-value min) + max (deref-or-value max) + step (deref-or-value step) + disabled? (deref-or-value disabled?) + cmerger (merge-css slider-css-spec args)] + (add-map-to-hiccup-call + (cmerger :wrapper) + [box + :src src + :debug-as (or debug-as (reflect-current-component)) + :align :start + :child [:input + (merge + (flatten-attr + (cmerger :main {:width width :disabled? disabled?})) + {:type "range" ;:orient "vertical" ;; Make Firefox slider vertical (doesn't work because React ignores it, I think) - :min min - :max max - :step step - :value model - :disabled disabled? - :on-change (handler-fn (on-change (js/Number (-> event .-target .-value))))})]])))) + :min min + :max max + :step step + :value model + :disabled disabled? + :on-change (handler-fn (on-change (js/Number (-> event .-target .-value))))})]])))) diff --git a/src/re_com/splits.cljs b/src/re_com/splits.cljs index 1c1a1677..ea429c2a 100644 --- a/src/re_com/splits.cljs +++ b/src/re_com/splits.cljs @@ -2,12 +2,12 @@ (:require-macros [re-com.core :refer [handler-fn at reflect-current-component]]) (:require - [re-com.config :refer [include-args-desc?]] - [re-com.debug :refer [->attr]] - [re-com.util :refer [get-element-by-id sum-scroll-offsets merge-css flatten-attr]] - [re-com.box :refer [flex-child-style flex-flow-style]] - [re-com.validate :refer [string-or-hiccup? number-or-string? html-attr? css-style? parts?] :refer-macros [validate-args-macro]] - [reagent.core :as reagent])) + [re-com.config :refer [include-args-desc?]] + [re-com.debug :refer [->attr]] + [re-com.util :refer [get-element-by-id sum-scroll-offsets merge-css flatten-attr]] + [re-com.box :refer [flex-child-style flex-flow-style]] + [re-com.validate :refer [string-or-hiccup? number-or-string? html-attr? css-style? parts?] :refer-macros [validate-args-macro]] + [reagent.core :as reagent])) (declare hv-split-css-spec) @@ -108,13 +108,10 @@ (merge (flex-child-style flex) (when dragging? {:pointer-events "none"})))}}) - (def hv-split-parts (when include-args-desc? (-> (map :name hv-split-parts-desc) set))) - - (def hv-split-args-desc (when include-args-desc? [{:name :panel-1 :required true :type "hiccup" :validate-fn string-or-hiccup? :description "markup to go in the left (or top) panel"} @@ -140,75 +137,74 @@ :or {size "auto" initial-split 50 splitter-size "8px" margin "8px"} :as args}] (or - (validate-args-macro hv-split-args-desc args) - (let [container-id (gensym "h-split-") - split-perc (reagent/atom (js/parseInt initial-split)) ;; splitter position as a percentage of width - dragging? (reagent/atom false) ;; is the user dragging the splitter (mouse is down)? - over? (reagent/atom false) ;; is the mouse over the splitter, if so, highlight it - - stop-drag (fn [] - (when on-split-change (on-split-change @split-perc)) - (reset! dragging? false)) - - calc-perc (fn [mouse-x] ;; turn a mouse x coordinate into a percentage position - (let [container (get-element-by-id container-id) ;; the outside container - c-width (.-clientWidth container) ;; the container's width - c-left-x (+ (.-pageXOffset js/window) - (-> container .getBoundingClientRect .-left)) ;; the container's left X - relative-x (- mouse-x c-left-x)] ;; the X of the mouse, relative to container - (if split-is-px? - relative-x ;; return the left offset in px - (* 100.0 (/ relative-x c-width))))) ;; return the percentage panel-1 width against container width - - ? #(= % (.-documentElement js/document)) ;; test for the element - - mouseout (fn [event] - (if (? (.-relatedTarget event)) ;; stop drag if we leave the element - (stop-drag))) - - mousemove (fn [event] - (reset! split-perc (calc-perc (.-clientX event)))) - - mousedown (fn [event] - (.preventDefault event) ;; stop selection of text during drag - (reset! dragging? true)) - - mouseover-split #(reset! over? true) ;; true CANCELs mouse-over (false cancels all others) - mouseout-split #(reset! over? false)] - - (fn h-split-render - [& {:keys [panel-1 panel-2 _size _width _height _on-split-change _initial-split _splitter-size _margin class style attr parts src]}] - (let [cmerger (merge-css hv-split-css-spec args)] + (validate-args-macro hv-split-args-desc args) + (let [container-id (gensym "h-split-") + split-perc (reagent/atom (js/parseInt initial-split)) ;; splitter position as a percentage of width + dragging? (reagent/atom false) ;; is the user dragging the splitter (mouse is down)? + over? (reagent/atom false) ;; is the mouse over the splitter, if so, highlight it + + stop-drag (fn [] + (when on-split-change (on-split-change @split-perc)) + (reset! dragging? false)) + + calc-perc (fn [mouse-x] ;; turn a mouse x coordinate into a percentage position + (let [container (get-element-by-id container-id) ;; the outside container + c-width (.-clientWidth container) ;; the container's width + c-left-x (+ (.-pageXOffset js/window) + (-> container .getBoundingClientRect .-left)) ;; the container's left X + relative-x (- mouse-x c-left-x)] ;; the X of the mouse, relative to container + (if split-is-px? + relative-x ;; return the left offset in px + (* 100.0 (/ relative-x c-width))))) ;; return the percentage panel-1 width against container width + + ? #(= % (.-documentElement js/document)) ;; test for the element + + mouseout (fn [event] + (if (? (.-relatedTarget event)) ;; stop drag if we leave the element + (stop-drag))) + + mousemove (fn [event] + (reset! split-perc (calc-perc (.-clientX event)))) + + mousedown (fn [event] + (.preventDefault event) ;; stop selection of text during drag + (reset! dragging? true)) + + mouseover-split #(reset! over? true) ;; true CANCELs mouse-over (false cancels all others) + mouseout-split #(reset! over? false)] + + (fn h-split-render + [& {:keys [panel-1 panel-2 _size _width _height _on-split-change _initial-split _splitter-size _margin class style attr parts src]}] + (let [cmerger (merge-css hv-split-css-spec args)] + [:div + (flatten-attr + (cmerger :main {:vertical? false :size size :margin margin :width width :height height + :attr (merge + {:id container-id} + (when @dragging? ;; only listen when we are dragging + {:on-mouse-up (handler-fn (stop-drag)) + :on-mouse-move (handler-fn (mousemove event)) + :on-mouse-out (handler-fn (mouseout event))}))})) [:div (flatten-attr - (cmerger :main {:vertical? false :size size :margin margin :width width :height height - :attr (merge - {:id container-id} - (when @dragging? ;; only listen when we are dragging - {:on-mouse-up (handler-fn (stop-drag)) - :on-mouse-move (handler-fn (mousemove event)) - :on-mouse-out (handler-fn (mouseout event))}))})) - [:div - (flatten-attr - (cmerger :left {:dragging? @dragging? - :flex (calculate-split-flex-style @split-perc split-is-px?)})) - panel-1] - [:div - (flatten-attr - (cmerger :splitter {:vertical? false :size splitter-size :over? @over? - :attr {:on-mouse-down (handler-fn (mousedown event)) - :on-mouse-over (handler-fn (mouseover-split)) - :on-mouse-out (handler-fn (mouseout-split))}})) - [drag-handle :vertical @over? parts]] - [:div - (flatten-attr - (cmerger :right {:dragging? @dragging? - :flex (calculate-split-flex-style (if split-is-px? - (- @split-perc) ;; Negative value indicates this is for panel-2 - (- 100 @split-perc)) - split-is-px?)})) - panel-2]]))))) - + (cmerger :left {:dragging? @dragging? + :flex (calculate-split-flex-style @split-perc split-is-px?)})) + panel-1] + [:div + (flatten-attr + (cmerger :splitter {:vertical? false :size splitter-size :over? @over? + :attr {:on-mouse-down (handler-fn (mousedown event)) + :on-mouse-over (handler-fn (mouseover-split)) + :on-mouse-out (handler-fn (mouseout-split))}})) + [drag-handle :vertical @over? parts]] + [:div + (flatten-attr + (cmerger :right {:dragging? @dragging? + :flex (calculate-split-flex-style (if split-is-px? + (- @split-perc) ;; Negative value indicates this is for panel-2 + (- 100 @split-perc)) + split-is-px?)})) + panel-2]]))))) ;; ------------------------------------------------------------------------------------ ;; Component: v-split @@ -220,72 +216,72 @@ :or {size "auto" initial-split 50 splitter-size "8px" margin "8px"} :as args}] (or - (validate-args-macro hv-split-args-desc args) - (let [container-id (gensym "v-split-") - split-perc (reagent/atom (js/parseInt initial-split)) ;; splitter position as a percentage of height - dragging? (reagent/atom false) ;; is the user dragging the splitter (mouse is down)? - over? (reagent/atom false) ;; is the mouse over the splitter, if so, highlight it - - stop-drag (fn [] - (when on-split-change (on-split-change @split-perc)) - (reset! dragging? false)) - - calc-perc (fn [mouse-y] ;; turn a mouse y coordinate into a percentage position - (let [container (get-element-by-id container-id) ;; the outside container - c-height (.-clientHeight container) ;; the container's height - c-top-y (+ (.-pageYOffset js/window) - (-> container .getBoundingClientRect .-top)) ;; the container's top Y - relative-y (- mouse-y c-top-y)] ;; the Y of the mouse, relative to container - (if split-is-px? - relative-y ;; return the top offset in px - (* 100.0 (/ relative-y c-height))))) ;; return the percentage panel-1 height against container width - - ? #(= % (.-documentElement js/document)) ;; test for the element - - mouseout (fn [event] - (if (? (.-relatedTarget event)) ;; stop drag if we leave the element - (stop-drag))) - - mousemove (fn [event] - (reset! split-perc (calc-perc (.-clientY event)))) - - mousedown (fn [event] - (.preventDefault event) ;; stop selection of text during drag - (reset! dragging? true)) - - mouseover-split #(reset! over? true) - mouseout-split #(reset! over? false)] - - (fn v-split-render - [& {:keys [panel-1 panel-2 _size _width _height _on-split-change _initial-split _splitter-size _margin class style attr parts src]}] - - (let [cmerger (merge-css hv-split-css-spec args)] + (validate-args-macro hv-split-args-desc args) + (let [container-id (gensym "v-split-") + split-perc (reagent/atom (js/parseInt initial-split)) ;; splitter position as a percentage of height + dragging? (reagent/atom false) ;; is the user dragging the splitter (mouse is down)? + over? (reagent/atom false) ;; is the mouse over the splitter, if so, highlight it + + stop-drag (fn [] + (when on-split-change (on-split-change @split-perc)) + (reset! dragging? false)) + + calc-perc (fn [mouse-y] ;; turn a mouse y coordinate into a percentage position + (let [container (get-element-by-id container-id) ;; the outside container + c-height (.-clientHeight container) ;; the container's height + c-top-y (+ (.-pageYOffset js/window) + (-> container .getBoundingClientRect .-top)) ;; the container's top Y + relative-y (- mouse-y c-top-y)] ;; the Y of the mouse, relative to container + (if split-is-px? + relative-y ;; return the top offset in px + (* 100.0 (/ relative-y c-height))))) ;; return the percentage panel-1 height against container width + + ? #(= % (.-documentElement js/document)) ;; test for the element + + mouseout (fn [event] + (if (? (.-relatedTarget event)) ;; stop drag if we leave the element + (stop-drag))) + + mousemove (fn [event] + (reset! split-perc (calc-perc (.-clientY event)))) + + mousedown (fn [event] + (.preventDefault event) ;; stop selection of text during drag + (reset! dragging? true)) + + mouseover-split #(reset! over? true) + mouseout-split #(reset! over? false)] + + (fn v-split-render + [& {:keys [panel-1 panel-2 _size _width _height _on-split-change _initial-split _splitter-size _margin class style attr parts src]}] + + (let [cmerger (merge-css hv-split-css-spec args)] + [:div + (flatten-attr + (cmerger :main {:vertical? true :size size :margin margin :width width :height height + :attr (merge + {:id container-id} + (when @dragging? ;; only listen when we are dragging + {:on-mouse-up (handler-fn (stop-drag)) + :on-mouse-move (handler-fn (mousemove event)) + :on-mouse-out (handler-fn (mouseout event))}))})) [:div (flatten-attr - (cmerger :main {:vertical? true :size size :margin margin :width width :height height - :attr (merge - {:id container-id} - (when @dragging? ;; only listen when we are dragging - {:on-mouse-up (handler-fn (stop-drag)) - :on-mouse-move (handler-fn (mousemove event)) - :on-mouse-out (handler-fn (mouseout event))}))})) - [:div - (flatten-attr - (cmerger :top {:dragging? @dragging? - :flex (calculate-split-flex-style @split-perc split-is-px?)})) - panel-1] - [:div - (flatten-attr - (cmerger :splitter {:vertical? true :size splitter-size :over? @over? - :attr {:on-mouse-down (handler-fn (mousedown event)) - :on-mouse-over (handler-fn (mouseover-split)) - :on-mouse-out (handler-fn (mouseout-split))}})) - [drag-handle :horizontal @over? parts]] - [:div - (flatten-attr - (cmerger :bottom {:dragging? @dragging? - :flex (calculate-split-flex-style (if split-is-px? - (- @split-perc) ;; Negative value indicates this is for panel-2 - (- 100 @split-perc)) - split-is-px?)})) - panel-2]]))))) + (cmerger :top {:dragging? @dragging? + :flex (calculate-split-flex-style @split-perc split-is-px?)})) + panel-1] + [:div + (flatten-attr + (cmerger :splitter {:vertical? true :size splitter-size :over? @over? + :attr {:on-mouse-down (handler-fn (mousedown event)) + :on-mouse-over (handler-fn (mouseover-split)) + :on-mouse-out (handler-fn (mouseout-split))}})) + [drag-handle :horizontal @over? parts]] + [:div + (flatten-attr + (cmerger :bottom {:dragging? @dragging? + :flex (calculate-split-flex-style (if split-is-px? + (- @split-perc) ;; Negative value indicates this is for panel-2 + (- 100 @split-perc)) + split-is-px?)})) + panel-2]]))))) diff --git a/src/re_com/tabs.cljs b/src/re_com/tabs.cljs index af69f15d..b1121b14 100644 --- a/src/re_com/tabs.cljs +++ b/src/re_com/tabs.cljs @@ -3,14 +3,14 @@ [re-com.core :refer [handler-fn at]] [re-com.validate :refer [validate-args-macro]]) (:require - [re-com.config :refer [include-args-desc?]] - [re-com.debug :refer [->attr]] - [re-com.util :refer [deref-or-value add-map-to-hiccup-call merge-css flatten-attr]] - [re-com.box :refer [flex-child-style]] - [re-com.validate :refer [css-style? html-attr? parts? vector-of-maps? - position? position-options-list]] - [re-com.popover :refer [popover-tooltip]] - [reagent.core :as reagent])) + [re-com.config :refer [include-args-desc?]] + [re-com.debug :refer [->attr]] + [re-com.util :refer [deref-or-value add-map-to-hiccup-call merge-css flatten-attr]] + [re-com.box :refer [flex-child-style]] + [re-com.validate :refer [css-style? html-attr? parts? vector-of-maps? + position? position-options-list]] + [re-com.popover :refer [popover-tooltip]] + [reagent.core :as reagent])) ;;-------------------------------------------------------------------------------------------------- ;; Component: horizontal-tabs @@ -57,35 +57,34 @@ :or {id-fn :id label-fn :label} :as args}] (or - (validate-args-macro horizontal-tabs-args-desc args) - (let [current (deref-or-value model) - tabs (deref-or-value tabs) - disabled? (deref-or-value disabled?) - _ (assert (not-empty (filter #(= current (id-fn %)) tabs)) "model not found in tabs vector") - cmerger (merge-css horizontal-tabs-css-spec args)] - [:ul - (merge (flatten-attr - (cmerger :wrapper)) - (->attr args)) - (for [t tabs] - (let [id (id-fn t) - label (label-fn t) - disabled? (or disabled? (:disabled? t)) - selected? (= id current)] ;; must use current instead of @model to avoid reagent warnings - [:li + (validate-args-macro horizontal-tabs-args-desc args) + (let [current (deref-or-value model) + tabs (deref-or-value tabs) + disabled? (deref-or-value disabled?) + _ (assert (not-empty (filter #(= current (id-fn %)) tabs)) "model not found in tabs vector") + cmerger (merge-css horizontal-tabs-css-spec args)] + [:ul + (merge (flatten-attr + (cmerger :wrapper)) + (->attr args)) + (for [t tabs] + (let [id (id-fn t) + label (label-fn t) + disabled? (or disabled? (:disabled? t)) + selected? (= id current)] ;; must use current instead of @model to avoid reagent warnings + [:li + (flatten-attr + (cmerger :tab {:selected? selected? + :disabled? disabled? + :attr {:key (str id)}})) + [:a (flatten-attr - (cmerger :tab {:selected? selected? - :disabled? disabled? - :attr {:key (str id)}})) - [:a - (flatten-attr - (cmerger :anchor {:selected? selected? - :disabled? disabled? - :attr - {:on-click (when (and on-change (not selected?) (not disabled?)) - (handler-fn (on-change id)))}})) - label]]))]))) - + (cmerger :anchor {:selected? selected? + :disabled? disabled? + :attr + {:on-click (when (and on-change (not selected?) (not disabled?)) + (handler-fn (on-change id)))}})) + label]]))]))) ;;-------------------------------------------------------------------------------------------------- ;; Component: horizontal-bar-tabs @@ -135,37 +134,37 @@ (merge (flatten-attr (cmerger :wrapper {:vertical? vertical?})) - (->attr args) - attr)] - (for [t tabs] - (let [id (id-fn t) - label (label-fn t) - tooltip (when tooltip-fn (tooltip-fn t)) - selected? (= id current) - disabled? (or disabled? (:disabled? t)) - the-button [:button - (merge - (cmerger :button {:selected? selected? - :vertical? vertical? - :disabled? disabled?}) - {:type "button" - :key (str id) - :on-click (when (and on-change (not selected?) (not disabled?)) - (handler-fn (on-change id)))} - (when tooltip - {:on-mouse-over (handler-fn (reset! showing id)) - :on-mouse-out (handler-fn (swap! showing #(when-not (= id %) %)))})) - label]] - (if tooltip - (add-map-to-hiccup-call - (cmerger :tooltip) - [popover-tooltip - :src (at) - :label tooltip - :position (or tooltip-position :below-center) - :showing? (reagent/track #(= id @showing)) - :anchor the-button]) - the-button)))))))) + (->attr args) + attr)] + (for [t tabs] + (let [id (id-fn t) + label (label-fn t) + tooltip (when tooltip-fn (tooltip-fn t)) + selected? (= id current) + disabled? (or disabled? (:disabled? t)) + the-button [:button + (merge + (cmerger :button {:selected? selected? + :vertical? vertical? + :disabled? disabled?}) + {:type "button" + :key (str id) + :on-click (when (and on-change (not selected?) (not disabled?)) + (handler-fn (on-change id)))} + (when tooltip + {:on-mouse-over (handler-fn (reset! showing id)) + :on-mouse-out (handler-fn (swap! showing #(when-not (= id %) %)))})) + label]] + (if tooltip + (add-map-to-hiccup-call + (cmerger :tooltip) + [popover-tooltip + :src (at) + :label tooltip + :position (or tooltip-position :below-center) + :showing? (reagent/track #(= id @showing)) + :anchor the-button]) + the-button)))))))) (defn horizontal-bar-tabs [& {:keys [model tabs on-change id-fn label-fn tooltip-fn tooltip-position class style attr parts src debug-as validate? disabled?] @@ -256,9 +255,9 @@ [:ul (merge (flatten-attr (cmerger :wrapper {:vertical? vertical?})) - {:role "tabslist"} - (->attr args) - attr) + {:role "tabslist"} + (->attr args) + attr) (for [t tabs] (let [id (id-fn t) label (label-fn t) diff --git a/src/re_com/tag_dropdown.cljs b/src/re_com/tag_dropdown.cljs index c4934acf..339bd9d5 100644 --- a/src/re_com/tag_dropdown.cljs +++ b/src/re_com/tag_dropdown.cljs @@ -4,17 +4,17 @@ [re-com.core :refer [handler-fn at reflect-current-component]] [re-com.validate :refer [validate-args-macro]]) (:require - [goog.string :as gstring] - [reagent.core :as reagent] - [re-com.config :refer [include-args-desc?]] - [re-com.debug :refer [->attr]] - [re-com.util :refer [deref-or-value px-n assoc-in-if-empty merge-css add-map-to-hiccup-call flatten-attr]] - [re-com.validate :as validate :refer [parts?]] - [re-com.box :refer [box h-box v-box gap]] - [re-com.checkbox :refer [checkbox]] - [re-com.selection-list :as selection-list] - [re-com.close-button :refer [close-button]] - [re-com.popover :refer [popover-content-wrapper popover-anchor-wrapper]])) + [goog.string :as gstring] + [reagent.core :as reagent] + [re-com.config :refer [include-args-desc?]] + [re-com.debug :refer [->attr]] + [re-com.util :refer [deref-or-value px-n assoc-in-if-empty merge-css add-map-to-hiccup-call flatten-attr]] + [re-com.validate :as validate :refer [parts?]] + [re-com.box :refer [box h-box v-box gap]] + [re-com.checkbox :refer [checkbox]] + [re-com.selection-list :as selection-list] + [re-com.close-button :refer [close-button]] + [re-com.popover :refer [popover-content-wrapper popover-anchor-wrapper]])) ;; TODO: [GR] Ripped off from re-com.selection-list (defn label-style @@ -215,107 +215,107 @@ disabled? (deref-or-value disabled?) unselect-buttons? (deref-or-value unselect-buttons?) - choices-num-chars (reduce - (fn [n choice] - (if (contains? model (:id choice)) - (+ n (count (label-fn choice))) - n)) - 0 - choices) - abbrev? (and (>= choices-num-chars abbrev-threshold) - (number? abbrev-threshold) - (fn? abbrev-fn)) - cmerger (merge-css tag-dropdown-css-spec args) + choices-num-chars (reduce + (fn [n choice] + (if (contains? model (:id choice)) + (+ n (count (label-fn choice))) + n)) + 0 + choices) + abbrev? (and (>= choices-num-chars abbrev-threshold) + (number? abbrev-threshold) + (fn? abbrev-fn)) + cmerger (merge-css tag-dropdown-css-spec args) - placeholder-tag [text-tag - :tag-data {:id :$placeholder$ - :label "" - :background-color "white" - :width (if abbrev? "20px" "40px")} - :on-click #(reset! showing? true) - :tooltip "Click to select tags" - :hover-style {:background-color "#eee"}] - tag-list-body (add-map-to-hiccup-call - (cmerger :selection-list) - [selection-list/selection-list - :src (at) - :disabled? disabled? - :required? required? - :parts (-> - (select-keys parts selection-list/selection-list-parts) - (assoc-in-if-empty [:list-group-item :style :border] "1px solid #ddd") - (assoc-in-if-empty [:list-group-item :style :height] "auto") - (assoc-in-if-empty [:list-group-item :style :padding] "10px 15px")) - :choices choices - :hide-border? true - :label-fn (fn [tag] - [text-tag - :label-fn label-fn - :description-fn description-fn - :tag-data tag - :style style]) - :model model - :on-change #(on-change %) - :multi-select? true]) - tag-main (add-map-to-hiccup-call - (cmerger :main {:disabled? disabled? - :attr (when (not disabled?) - {:on-click (handler-fn (reset! showing? true))})}) - [h-box - :src (at) - :min-width min-width - :max-width max-width - :height height - :align :center - :padding "0px 6px" - :children [(add-map-to-hiccup-call - (cmerger :tags) - [h-box - :src (at) - :size "1" ;; This line will align the tag placeholder to the right - :children (conj - (mapv (fn [tag] - (when (contains? model (:id tag)) - [text-tag - :label-fn (if abbrev? abbrev-fn label-fn) - :tag-data tag - :tooltip (:label tag) - :disabled? disabled? - :on-click #(reset! showing? true) ;; Show dropdown - :on-unselect (when (and unselect-buttons? (not (and (= 1 (count model)) required?))) #(on-change (disj model %))) - :hover-style {:opacity "0.8"} - :style style - :parts parts])) - choices) - (when (not disabled?) - placeholder-tag) - [gap - :src (at) - :size "20px"] - (when (zero? (count model)) - [box - :src (at) - :child (if placeholder placeholder "")]))]) - (when (and (not-empty model) (not disabled?) - (not required?)) - [close-button - :src (at) - :parts {:wrapper (cmerger :close-button-wrapper)} - :on-click #(on-change #{})])]])] - (add-map-to-hiccup-call - (cmerger :popover-anchor-wrapper) - [popover-anchor-wrapper - :src src - :debug-as (or debug-as (reflect-current-component)) - :showing? showing? - :position :below-center - :anchor tag-main - :popover [popover-content-wrapper - :src (at) - :arrow-length 0 - :arrow-width 0 - :arrow-gap 1 - :no-clip? true - :on-cancel #(reset! showing? false) - :padding "19px 19px" - :body tag-list-body]]))))))) + placeholder-tag [text-tag + :tag-data {:id :$placeholder$ + :label "" + :background-color "white" + :width (if abbrev? "20px" "40px")} + :on-click #(reset! showing? true) + :tooltip "Click to select tags" + :hover-style {:background-color "#eee"}] + tag-list-body (add-map-to-hiccup-call + (cmerger :selection-list) + [selection-list/selection-list + :src (at) + :disabled? disabled? + :required? required? + :parts (-> + (select-keys parts selection-list/selection-list-parts) + (assoc-in-if-empty [:list-group-item :style :border] "1px solid #ddd") + (assoc-in-if-empty [:list-group-item :style :height] "auto") + (assoc-in-if-empty [:list-group-item :style :padding] "10px 15px")) + :choices choices + :hide-border? true + :label-fn (fn [tag] + [text-tag + :label-fn label-fn + :description-fn description-fn + :tag-data tag + :style style]) + :model model + :on-change #(on-change %) + :multi-select? true]) + tag-main (add-map-to-hiccup-call + (cmerger :main {:disabled? disabled? + :attr (when (not disabled?) + {:on-click (handler-fn (reset! showing? true))})}) + [h-box + :src (at) + :min-width min-width + :max-width max-width + :height height + :align :center + :padding "0px 6px" + :children [(add-map-to-hiccup-call + (cmerger :tags) + [h-box + :src (at) + :size "1" ;; This line will align the tag placeholder to the right + :children (conj + (mapv (fn [tag] + (when (contains? model (:id tag)) + [text-tag + :label-fn (if abbrev? abbrev-fn label-fn) + :tag-data tag + :tooltip (:label tag) + :disabled? disabled? + :on-click #(reset! showing? true) ;; Show dropdown + :on-unselect (when (and unselect-buttons? (not (and (= 1 (count model)) required?))) #(on-change (disj model %))) + :hover-style {:opacity "0.8"} + :style style + :parts parts])) + choices) + (when (not disabled?) + placeholder-tag) + [gap + :src (at) + :size "20px"] + (when (zero? (count model)) + [box + :src (at) + :child (if placeholder placeholder "")]))]) + (when (and (not-empty model) (not disabled?) + (not required?)) + [close-button + :src (at) + :parts {:wrapper (cmerger :close-button-wrapper)} + :on-click #(on-change #{})])]])] + (add-map-to-hiccup-call + (cmerger :popover-anchor-wrapper) + [popover-anchor-wrapper + :src src + :debug-as (or debug-as (reflect-current-component)) + :showing? showing? + :position :below-center + :anchor tag-main + :popover [popover-content-wrapper + :src (at) + :arrow-length 0 + :arrow-width 0 + :arrow-gap 1 + :no-clip? true + :on-cancel #(reset! showing? false) + :padding "19px 19px" + :body tag-list-body]]))))))) diff --git a/src/re_com/text.cljs b/src/re_com/text.cljs index abe237b1..752b11ed 100644 --- a/src/re_com/text.cljs +++ b/src/re_com/text.cljs @@ -3,11 +3,11 @@ [re-com.core :refer [handler-fn at reflect-current-component]] [re-com.validate :refer [validate-args-macro]]) (:require - [re-com.config :refer [include-args-desc?]] - [re-com.debug :refer [->attr]] - [re-com.box :refer [v-box box line flex-child-style]] - [re-com.util :refer [deep-merge add-map-to-hiccup-call merge-css flatten-attr]] - [re-com.validate :refer [title-levels-list title-level-type? css-style? html-attr? parts? string-or-hiccup?]])) + [re-com.config :refer [include-args-desc?]] + [re-com.debug :refer [->attr]] + [re-com.box :refer [v-box box line flex-child-style]] + [re-com.util :refer [deep-merge add-map-to-hiccup-call merge-css flatten-attr]] + [re-com.validate :refer [title-levels-list title-level-type? css-style? html-attr? parts? string-or-hiccup?]])) ;; ------------------------------------------------------------------------------------ ;; Component: label @@ -44,22 +44,21 @@ [& {:keys [label on-click width class style attr parts src debug-as] :as args}] (or - (validate-args-macro label-args-desc args) - (let [cmerger (merge-css label-css-spec args)] - (add-map-to-hiccup-call - (cmerger :wrapper) - [box - :debug-as (or debug-as (reflect-current-component)) - :src src - :width width - :align :start - :child [:span - (merge - (cmerger :main) - (when on-click - {:on-click (handler-fn (on-click))})) - label]])))) - + (validate-args-macro label-args-desc args) + (let [cmerger (merge-css label-css-spec args)] + (add-map-to-hiccup-call + (cmerger :wrapper) + [box + :debug-as (or debug-as (reflect-current-component)) + :src src + :width width + :align :start + :child [:span + (merge + (cmerger :main) + (when on-click + {:on-click (handler-fn (on-click))})) + label]])))) ;; ------------------------------------------------------------------------------------ ;; Component: title @@ -110,23 +109,22 @@ [& {:keys [label level underline? margin-top margin-bottom class style attr parts src debug-as] :as args}] (or - (validate-args-macro title-args-desc args) - (let [cmerger (merge-css title-css-spec args)] - (add-map-to-hiccup-call - (cmerger :wrapper {:level level}) - [v-box - :src src - :debug-as (or debug-as (reflect-current-component)) - :children [[:span (merge (flatten-attr - (cmerger :main {:level level :underline? underline? - :margin-top margin-top :margin-bottom margin-bottom}))) - label] - (when underline? (add-map-to-hiccup-call - (cmerger :underline {:margin-bottom margin-bottom}) - [line - :src (at) - :size "1px"]))]])))) - + (validate-args-macro title-args-desc args) + (let [cmerger (merge-css title-css-spec args)] + (add-map-to-hiccup-call + (cmerger :wrapper {:level level}) + [v-box + :src src + :debug-as (or debug-as (reflect-current-component)) + :children [[:span (merge (flatten-attr + (cmerger :main {:level level :underline? underline? + :margin-top margin-top :margin-bottom margin-bottom}))) + label] + (when underline? (add-map-to-hiccup-call + (cmerger :underline {:margin-bottom margin-bottom}) + [line + :src (at) + :size "1px"]))]])))) ;; ------------------------------------------------------------------------------------ ;; Component: p diff --git a/src/re_com/throbber.cljs b/src/re_com/throbber.cljs index 75e32db1..f5ec6448 100644 --- a/src/re_com/throbber.cljs +++ b/src/re_com/throbber.cljs @@ -3,13 +3,13 @@ [re-com.core :refer [handler-fn at reflect-current-component]] [re-com.validate :refer [validate-args-macro]]) (:require - [re-com.config :refer [include-args-desc?]] - [re-com.debug :refer [->attr]] - [re-com.util :refer [deref-or-value px add-map-to-hiccup-call merge-css flatten-attr]] - [re-com.popover :refer [popover-tooltip]] - [re-com.box :refer [h-box v-box box gap line flex-child-style align-style]] - [re-com.validate :refer [input-status-type? input-status-types-list regex? string-or-hiccup? css-style? html-attr? parts? - number-or-string? string-or-atom? nillable-string-or-atom? throbber-size? throbber-sizes-list]])) + [re-com.config :refer [include-args-desc?]] + [re-com.debug :refer [->attr]] + [re-com.util :refer [deref-or-value px add-map-to-hiccup-call merge-css flatten-attr]] + [re-com.popover :refer [popover-tooltip]] + [re-com.box :refer [h-box v-box box gap line flex-child-style align-style]] + [re-com.validate :refer [input-status-type? input-status-types-list regex? string-or-hiccup? css-style? html-attr? parts? + number-or-string? string-or-atom? nillable-string-or-atom? throbber-size? throbber-sizes-list]])) ;; ------------------------------------------------------------------------------------ ;; Component: throbber @@ -54,17 +54,17 @@ "Render an animated throbber using CSS" [& {:keys [size color class style attr parts src debug-as] :as args}] (or - (validate-args-macro throbber-args-desc args) - (let [cmerger (merge-css throbber-css-spec args) - seg (fn [] - [:li (cmerger :segment {:color color})])] - (add-map-to-hiccup-call - (cmerger :wrapper) - [box - :src src - :debug-as (or debug-as (reflect-current-component)) - :align :start - :child [:ul - (cmerger :main {:size size}) - [seg] [seg] [seg] [seg] - [seg] [seg] [seg] [seg]]])))) ;; Each :li element in [seg] represents one of the eight circles in the throbber + (validate-args-macro throbber-args-desc args) + (let [cmerger (merge-css throbber-css-spec args) + seg (fn [] + [:li (cmerger :segment {:color color})])] + (add-map-to-hiccup-call + (cmerger :wrapper) + [box + :src src + :debug-as (or debug-as (reflect-current-component)) + :align :start + :child [:ul + (cmerger :main {:size size}) + [seg] [seg] [seg] [seg] + [seg] [seg] [seg] [seg]]])))) ;; Each :li element in [seg] represents one of the eight circles in the throbber diff --git a/src/re_com/tour.cljs b/src/re_com/tour.cljs index 13f6f36b..e72d2fca 100644 --- a/src/re_com/tour.cljs +++ b/src/re_com/tour.cljs @@ -2,11 +2,10 @@ (:require-macros [re-com.core :refer [handler-fn at]]) (:require - [reagent.core :as reagent] - [re-com.box :refer [flex-child-style]] - [re-com.buttons :refer [button]] - [re-com.util :refer [add-map-to-hiccup-call merge-css flatten-attr]])) - + [reagent.core :as reagent] + [re-com.box :refer [flex-child-style]] + [re-com.buttons :refer [button]] + [re-com.util :refer [add-map-to-hiccup-call merge-css flatten-attr]])) ;;-------------------------------------------------------------------------------------------------- ;; Component: tour diff --git a/src/re_com/typeahead.cljs b/src/re_com/typeahead.cljs index 456abb4d..051d9984 100644 --- a/src/re_com/typeahead.cljs +++ b/src/re_com/typeahead.cljs @@ -4,18 +4,18 @@ [re-com.validate :refer [validate-args-macro]] [cljs.core.async.macros :refer [alt! go-loop]]) (:require - [cljs.core.async :refer [chan timeout attr]] - [re-com.throbber :refer [throbber]] - [re-com.input-text :refer [input-text]] - [re-com.util :refer [deref-or-value px add-map-to-hiccup-call merge-css]] - [re-com.popover :refer [popover-tooltip]] ;; need? - [re-com.box :refer [h-box v-box box gap line flex-child-style align-style]] ;; need? - [re-com.validate :refer [input-status-type? input-status-types-list regex? string-or-hiccup? css-style? html-attr? parts? number-or-string? - string-or-atom? throbber-size? throbber-sizes-list]] - [reagent.core :as reagent] - [goog.events.KeyCodes])) + [cljs.core.async :refer [chan timeout attr]] + [re-com.throbber :refer [throbber]] + [re-com.input-text :refer [input-text]] + [re-com.util :refer [deref-or-value px add-map-to-hiccup-call merge-css]] + [re-com.popover :refer [popover-tooltip]] ;; need? + [re-com.box :refer [h-box v-box box gap line flex-child-style align-style]] ;; need? + [re-com.validate :refer [input-status-type? input-status-types-list regex? string-or-hiccup? css-style? html-attr? parts? number-or-string? + string-or-atom? throbber-size? throbber-sizes-list]] + [reagent.core :as reagent] + [goog.events.KeyCodes])) ;; TODO ;; ability to focus & blur the input-text would be nice... this is also missing from input-text @@ -267,7 +267,6 @@ :suggestion {:class (fn [{:keys [selected?]}] ["rc-typeahead-suggestion" (when selected? "active")])}}) - (defn typeahead "typeahead reagent component" [& {:keys [] :as args}] @@ -281,72 +280,72 @@ [& {:as args :keys [data-source _on-change _change-on-blur? _immediate-model-update? model _debounce-delay render-suggestion _suggestion-to-string _rigid? ;; forwarded to wrapped `input-text`: - status status-icon? status-tooltip placeholder width height disabled? class style attr parts src debug-as]}] - (or - (validate-args-macro typeahead-args-desc args) - (let [{:as state :keys [suggestions waiting? suggestion-active-index external-model]} @state-atom - last-data-source (:data-source state) - latest-external-model (deref-or-value model) - width (or width "250px") - cmerger (merge-css typeahead-css-spec args)] - (when (not= last-data-source data-source) - (swap! state-atom change-data-source data-source)) - (when (not= latest-external-model external-model) - (swap! state-atom external-model-changed latest-external-model)) - (add-map-to-hiccup-call - (cmerger :wrapper) - [v-box - :src src - :debug-as (or debug-as (reflect-current-component)) - :width width - :children [(add-map-to-hiccup-call - (cmerger :main - {:attr {:on-key-down (partial input-text-on-key-down! state-atom) - :on-focus #() + status status-icon? status-tooltip placeholder width height disabled? class style attr parts src debug-as]}] + (or + (validate-args-macro typeahead-args-desc args) + (let [{:as state :keys [suggestions waiting? suggestion-active-index external-model]} @state-atom + last-data-source (:data-source state) + latest-external-model (deref-or-value model) + width (or width "250px") + cmerger (merge-css typeahead-css-spec args)] + (when (not= last-data-source data-source) + (swap! state-atom change-data-source data-source)) + (when (not= latest-external-model external-model) + (swap! state-atom external-model-changed latest-external-model)) + (add-map-to-hiccup-call + (cmerger :wrapper) + [v-box + :src src + :debug-as (or debug-as (reflect-current-component)) + :width width + :children [(add-map-to-hiccup-call + (cmerger :main + {:attr {:on-key-down (partial input-text-on-key-down! state-atom) + :on-focus #() ;; on-blur should behave the same as tabbing off - :on-blur #(swap! state-atom input-text-will-blur)}}) - [input-text - :src (at) - :model input-text-model - :disabled? disabled? - :status-icon? status-icon? - :status status - :status-tooltip status-tooltip - :width width - :height height - :placeholder placeholder - :on-change (partial input-text-on-change! state-atom) - :change-on-blur? false]) - (if (or (not-empty suggestions) waiting?) - (add-map-to-hiccup-call - (cmerger :suggestions-container-wrapper) - [box - :src (at) - :child (add-map-to-hiccup-call - (cmerger :suggestions-container) - [v-box - :src (at) - :children [(when waiting? - [box - :src (at) - :align :center - :child (add-map-to-hiccup-call - (cmerger :throbber) - [throbber - :src (at) - :size :small])]) - (for [[i s] (map vector (range) suggestions) - :let [selected? (= suggestion-active-index i)]] - (add-map-to-hiccup-call - (cmerger :suggestion {:selected? selected? - :attr {:on-mouse-over #(swap! state-atom activate-suggestion-by-index i) - :on-mouse-down #(do (.preventDefault %) (swap! state-atom choose-suggestion-by-index i))}}) - ^{:key i} - [box - :src (at) - :child (if render-suggestion - (render-suggestion s) - s)]))]])]))]]))))))) + :on-blur #(swap! state-atom input-text-will-blur)}}) + [input-text + :src (at) + :model input-text-model + :disabled? disabled? + :status-icon? status-icon? + :status status + :status-tooltip status-tooltip + :width width + :height height + :placeholder placeholder + :on-change (partial input-text-on-change! state-atom) + :change-on-blur? false]) + (if (or (not-empty suggestions) waiting?) + (add-map-to-hiccup-call + (cmerger :suggestions-container-wrapper) + [box + :src (at) + :child (add-map-to-hiccup-call + (cmerger :suggestions-container) + [v-box + :src (at) + :children [(when waiting? + [box + :src (at) + :align :center + :child (add-map-to-hiccup-call + (cmerger :throbber) + [throbber + :src (at) + :size :small])]) + (for [[i s] (map vector (range) suggestions) + :let [selected? (= suggestion-active-index i)]] + (add-map-to-hiccup-call + (cmerger :suggestion {:selected? selected? + :attr {:on-mouse-over #(swap! state-atom activate-suggestion-by-index i) + :on-mouse-down #(do (.preventDefault %) (swap! state-atom choose-suggestion-by-index i))}}) + ^{:key i} + [box + :src (at) + :child (if render-suggestion + (render-suggestion s) + s)]))]])]))]]))))))) (defn- debounce "Return a channel which will receive a value from the `in` channel only diff --git a/src/re_com/util.cljs b/src/re_com/util.cljs index f503f095..ea97a2fa 100644 --- a/src/re_com/util.cljs +++ b/src/re_com/util.cljs @@ -205,12 +205,10 @@ [] (let [local-date-time (js/goog.date.DateTime.)] (js/goog.date.UtcDateTime. - (.getYear local-date-time) - (.getMonth local-date-time) - (.getDate local-date-time) - 0 0 0 0))) - - + (.getYear local-date-time) + (.getMonth local-date-time) + (.getDate local-date-time) + 0 0 0 0))) ;; Merge-css - a tool for handling the css that comes into re-com components ;; @@ -283,7 +281,6 @@ ;; ;; The :main key is a special case. Most re-com components receive a :parts parameter for fine-grained css control. But they also receive a simpler set of :class, :style and :attr components. What happens to these? They are generally directed toward the element that the end user will perceive as being the central one. The :main one, in other words. So merge-css will take these toplevel CSS parameters and apply them to the request named :main. The :use-toplevel key, placed in the :main section of the -css-spec structure with a `false` value, will suppress this behavior. Placed in another section - say, :wrapper - with a `true` value, it will cause that element to receive the toplevel user CSS parameters. - (defn merge-css [css-desc {:as params :keys [class style attr parts]}] (for [[k v] css-desc :when (not (and (keyword? k) (map? v)))] diff --git a/src/re_com/v_table.cljs b/src/re_com/v_table.cljs index 906f8a15..4c781c6d 100644 --- a/src/re_com/v_table.cljs +++ b/src/re_com/v_table.cljs @@ -4,13 +4,13 @@ [re-com.core :refer [handler-fn at reflect-current-component]] [re-com.validate :refer [validate-args-macro]]) (:require - [reagent.core :as reagent] - [re-com.config :refer [debug? include-args-desc?]] - [re-com.debug :refer [->attr]] - [re-com.box :as box] - [re-com.util :as util :refer [deref-or-value px-n add-map-to-hiccup-call merge-css flatten-attr]] - [re-com.validate :refer [vector-atom? ifn-or-nil? map-atom? parts?]] - [re-com.dmm-tracker :refer [make-dmm-tracker captureMouseMoves]])) + [reagent.core :as reagent] + [re-com.config :refer [debug? include-args-desc?]] + [re-com.debug :refer [->attr]] + [re-com.box :as box] + [re-com.util :as util :refer [deref-or-value px-n add-map-to-hiccup-call merge-css flatten-attr]] + [re-com.validate :refer [vector-atom? ifn-or-nil? map-atom? parts?]] + [re-com.dmm-tracker :refer [make-dmm-tracker captureMouseMoves]])) ;; The public API for this component is called table (see last component in this file) @@ -48,9 +48,9 @@ :cursor "default" :background-color (case - dragging? "#777" ;thumb-drag-color - mouse-over? "#999" ;thumb-hover-color - :else "#bbb") ;thumb-color + dragging? "#777" ;thumb-drag-color + mouse-over? "#999" ;thumb-hover-color + :else "#bbb") ;thumb-color (if horizontal? :margin-left :margin-top) (px scroll-position)})}}) @@ -325,7 +325,6 @@ (iterate inc top-row-index) rows)]))) - (defn row-viewport "Render section 5 - the viewport component (which renders the content component as its child)" [row-renderer key-fn top-row-index rows scroll-x scroll-y @@ -554,8 +553,7 @@ :v-scroll-section {:class ["rc-v-table-v-scroll-section"]} :v-scroll {:class ["rc-v-table-v-scroll"] :style (fn [{:keys [scrollbar-margin]}] - {:margin (px-n scrollbar-margin 0)})} - }) + {:margin (px-n scrollbar-margin 0)})}}) ;;This is for the selection-renderer component embedded in v-table. Perhaps it should be a key in ;; v-table-css-spec, above. But it doesn't seem to be addressable using `parts` so for now just has @@ -962,35 +960,35 @@ sel-content-y-end (reagent/atom 0) ;; Current mouse y drag position of the content selection ;; The selection rectangle component - selection-renderer (fn selection-renderer - [class style attr] - (let [selecting-down? (> @sel-content-y-end @sel-content-y-start) - selecting-right? (> @sel-content-x-end @sel-content-x-start) - width (if selecting-right? - (- @sel-content-x-end @sel-content-x-start) - (- @sel-content-x-start @sel-content-x-end)) - height (if selecting-down? - (- @sel-content-y-end @sel-content-y-start) - (- @sel-content-y-start @sel-content-y-end)) - top (if selecting-down? - (- @sel-content-y-start @scroll-y) - (- @sel-content-y-start @scroll-y height)) - left (if selecting-right? - (- @sel-content-x-start @scroll-x) - (- @sel-content-x-start @scroll-x width)) - cmerger (merge-css - v-table-selection-css-spec - {:class class :style style :attr attr})] - [:div - (flatten-attr (cmerger :main {:width width :height height - :top top :left left})) - ""])) - - coords-debug (reagent/atom nil) ;; Handy when debugging - used to show selection coords on the left-hand debug section - event-debug (reagent/atom nil) ;; Handy when debugging - use this to display data from the event object on the left-hand debug section - selection-target (reagent/atom nil) ;; Indicates which section we're selecting in (one of :row, :row-header or :column-header) - sel-max-content-rows-px (reagent/atom 0) ;; The maximum value that can be passed in the callback of px rows to be used for the selection callback - sel-max-content-cols-px (reagent/atom 0) ;; The maximum number of px columns to be used for the selection callback + selection-renderer (fn selection-renderer + [class style attr] + (let [selecting-down? (> @sel-content-y-end @sel-content-y-start) + selecting-right? (> @sel-content-x-end @sel-content-x-start) + width (if selecting-right? + (- @sel-content-x-end @sel-content-x-start) + (- @sel-content-x-start @sel-content-x-end)) + height (if selecting-down? + (- @sel-content-y-end @sel-content-y-start) + (- @sel-content-y-start @sel-content-y-end)) + top (if selecting-down? + (- @sel-content-y-start @scroll-y) + (- @sel-content-y-start @scroll-y height)) + left (if selecting-right? + (- @sel-content-x-start @scroll-x) + (- @sel-content-x-start @scroll-x width)) + cmerger (merge-css + v-table-selection-css-spec + {:class class :style style :attr attr})] + [:div + (flatten-attr (cmerger :main {:width width :height height + :top top :left left})) + ""])) + + coords-debug (reagent/atom nil) ;; Handy when debugging - used to show selection coords on the left-hand debug section + event-debug (reagent/atom nil) ;; Handy when debugging - use this to display data from the event object on the left-hand debug section + selection-target (reagent/atom nil) ;; Indicates which section we're selecting in (one of :row, :row-header or :column-header) + sel-max-content-rows-px (reagent/atom 0) ;; The maximum value that can be passed in the callback of px rows to be used for the selection callback + sel-max-content-cols-px (reagent/atom 0) ;; The maximum number of px columns to be used for the selection callback ;; Calculates the map representing the selection dimensions that will be passed back to the caller (translates px to row numbers if required) selection-coords (fn selection-coords @@ -1194,16 +1192,16 @@ ;; TODO: [DJ] Suggested that the many merges below could be placed in the let above as reaction for performance improvements (readability would suffer a bit) - (let [cmerger (merge-css v-table-css-spec args)] - (add-map-to-hiccup-call - (cmerger :wrapper {:max-width max-width ;; Can't do equivalent of :max-height because we don't know column-header-width or column-footer-width - :max-height - (when remove-empty-row-space? - (+ - (or column-header-height 0) - (or max-row-viewport-height (inc @content-rows-height)) ;; TODO: The inc prevents content scrollbar. Need to inc more if more than 1px borders specified - (or column-footer-height 0) - scrollbar-tot-thick)) + (let [cmerger (merge-css v-table-css-spec args)] + (add-map-to-hiccup-call + (cmerger :wrapper {:max-width max-width ;; Can't do equivalent of :max-height because we don't know column-header-width or column-footer-width + :max-height + (when remove-empty-row-space? + (+ + (or column-header-height 0) + (or max-row-viewport-height (inc @content-rows-height)) ;; TODO: The inc prevents content scrollbar. Need to inc more if more than 1px borders specified + (or column-footer-height 0) + scrollbar-tot-thick)) ;; TODO: Currently, scrolling a v-table with the mouse wheel also scrolls parent scrollbars (usually the one on the ) ;; The solution seems to be to use CSS overscroll-behavior @@ -1214,244 +1212,239 @@ ;:overscroll-behavior "contain" ;:overscroll-behavior-block "none" - - :attr {:on-wheel (handler-fn (on-wheel event))}}) - [box/h-box - :src src - :debug-as (or debug-as (reflect-current-component)) - :size "auto" - :children [ - ;; ========== LEFT SECTION (1, 2, 3) - row header area - - (add-map-to-hiccup-call - (cmerger :left-section) - [box/v-box - :src (at) - :children [ - ;; ========== SECTION 1 - top-left - - [top-left-content - top-left-renderer + :attr {:on-wheel (handler-fn (on-wheel event))}}) + [box/h-box + :src src + :debug-as (or debug-as (reflect-current-component)) + :size "auto" + :children [;; ========== LEFT SECTION (1, 2, 3) - row header area + + (add-map-to-hiccup-call + (cmerger :left-section) + [box/v-box + :src (at) + :children [;; ========== SECTION 1 - top-left + + [top-left-content + top-left-renderer ;----------------- - column-header-height + column-header-height ;----------------- - parts] + parts] ;; ========== SECTION 2 - row-headers - [row-header-viewport - row-header-renderer - key-fn - @top-row-index - (if virtual? @virtual-rows @model) ;; rows - (if virtual? @virtual-scroll-y @scroll-y) ;; scroll-y + [row-header-viewport + row-header-renderer + key-fn + @top-row-index + (if virtual? @virtual-rows @model) ;; rows + (if virtual? @virtual-scroll-y @scroll-y) ;; scroll-y ;----------------- - row-header-selection-fn - selection-fns - (and row-header-selection-fn @sel-parent-bounding-rect (= @selection-target :row-header)) ;; selection-allowed? + row-header-selection-fn + selection-fns + (and row-header-selection-fn @sel-parent-bounding-rect (= @selection-target :row-header)) ;; selection-allowed? ;----------------- - row-viewport-height - @content-rows-height + row-viewport-height + @content-rows-height ;----------------- - parts] + parts] ;; ========== SECTION 3 - bottom-left - [bottom-left-content - bottom-left-renderer + [bottom-left-content + bottom-left-renderer ;----------------- - column-footer-height + column-footer-height ;----------------- - parts] + parts] - [box/gap - :src (at) - :size (px scrollbar-tot-thick)]]]) + [box/gap + :src (at) + :size (px scrollbar-tot-thick)]]]) ;; ========== MIDDLE SECTION (4, 5, 6) - column header/footer and content area - (add-map-to-hiccup-call - (cmerger :middle-section {:max-width @content-rows-width}) - [box/v-box - :src (at) - :size (if row-viewport-width "none" "auto") - :children [ - ;; ========== SECTION 4 - column-headers - - [column-header-viewport - column-header-renderer - @scroll-x + (add-map-to-hiccup-call + (cmerger :middle-section {:max-width @content-rows-width}) + [box/v-box + :src (at) + :size (if row-viewport-width "none" "auto") + :children [;; ========== SECTION 4 - column-headers + + [column-header-viewport + column-header-renderer + @scroll-x ;----------------- - column-header-selection-fn - selection-fns - (and column-header-selection-fn @sel-parent-bounding-rect (= @selection-target :column-header)) ;; selection-allowed? + column-header-selection-fn + selection-fns + (and column-header-selection-fn @sel-parent-bounding-rect (= @selection-target :column-header)) ;; selection-allowed? ;----------------- - row-viewport-width - column-header-height - @content-rows-width + row-viewport-width + column-header-height + @content-rows-width ;----------------- - parts] + parts] ;; ========== SECTION 5 - rows (main content area) - [row-viewport - row-renderer - key-fn - @top-row-index - (if virtual? @virtual-rows @model) ;; rows - @scroll-x - (if virtual? @virtual-scroll-y @scroll-y) ;; scroll-y + [row-viewport + row-renderer + key-fn + @top-row-index + (if virtual? @virtual-rows @model) ;; rows + @scroll-x + (if virtual? @virtual-scroll-y @scroll-y) ;; scroll-y ;----------------- - row-selection-fn - selection-fns - (and row-selection-fn @sel-parent-bounding-rect (= @selection-target :row)) ;; selection-allowed? + row-selection-fn + selection-fns + (and row-selection-fn @sel-parent-bounding-rect (= @selection-target :row)) ;; selection-allowed? ;----------------- - row-viewport-height - row-viewport-width - row-viewport-id - @content-rows-height - @content-rows-width + row-viewport-height + row-viewport-width + row-viewport-id + @content-rows-height + @content-rows-width ;----------------- - parts] + parts] ;; ========== SECTION 6 - column-footers - [column-footer-viewport - column-footer-renderer - @scroll-x + [column-footer-viewport + column-footer-renderer + @scroll-x ;----------------- - row-viewport-width - column-footer-height + row-viewport-width + column-footer-height ;----------------- - parts] + parts] ;; ========== Horizontal scrollbar section - (add-map-to-hiccup-call - (flatten-attr (cmerger :h-scroll {:scrollbar-margin scrollbar-margin})) - [scrollbar - :src (at) - :type :horizontal - :length @rl-row-viewport-width - :width scrollbar-thickness - :content-length @content-rows-width - :scroll-pos @scroll-x - :on-change on-h-scroll-change])]]) + (add-map-to-hiccup-call + (flatten-attr (cmerger :h-scroll {:scrollbar-margin scrollbar-margin})) + [scrollbar + :src (at) + :type :horizontal + :length @rl-row-viewport-width + :width scrollbar-thickness + :content-length @content-rows-width + :scroll-pos @scroll-x + :on-change on-h-scroll-change])]]) ;; ========== Right section (7, 8, 9) - row footer area - (add-map-to-hiccup-call - (cmerger :right-section) - [box/v-box - :src (at) - :children [ - ;; ========== SECTION 7 - top-right + (add-map-to-hiccup-call + (cmerger :right-section) + [box/v-box + :src (at) + :children [;; ========== SECTION 7 - top-right - [top-right-content - top-right-renderer + [top-right-content + top-right-renderer ;----------------- - column-header-height + column-header-height ;----------------- - parts] + parts] ;; ========== SECTION 8 - row-footers - [row-footer-viewport - row-footer-renderer - key-fn - @top-row-index - (if virtual? @virtual-rows @model) ;; rows - (if virtual? @virtual-scroll-y @scroll-y) ;; scroll-y + [row-footer-viewport + row-footer-renderer + key-fn + @top-row-index + (if virtual? @virtual-rows @model) ;; rows + (if virtual? @virtual-scroll-y @scroll-y) ;; scroll-y ;----------------- - row-viewport-height - @content-rows-height + row-viewport-height + @content-rows-height ;----------------- - parts] + parts] ;; ========== SECTION 9 - bottom-right - [bottom-right-content - bottom-right-renderer + [bottom-right-content + bottom-right-renderer ;----------------- - column-footer-height + column-footer-height ;----------------- - parts] + parts] - [box/gap - :src (at) - :size (px scrollbar-tot-thick)]]]) + [box/gap + :src (at) + :size (px scrollbar-tot-thick)]]]) ;; ========== Vertical scrollbar section - (add-map-to-hiccup-call - (cmerger :v-scroll-section) - [box/v-box - :src (at) - :children [[box/gap - :src (at) - :size (px (or column-header-height 0))] - [box/box - :src (at) - :size "auto" - :child (add-map-to-hiccup-call - (flatten-attr - (cmerger - :v-scroll {:scrollbar-margin scrollbar-margin})) - [scrollbar - :src (at) - :type :vertical - :length @rl-row-viewport-height - :width scrollbar-thickness - :content-length @content-rows-height - :scroll-pos @scroll-y - :on-change on-v-scroll-change])] - [box/gap - :src (at) - :size (px (or column-footer-height 0))] - [box/gap - :src (at) - :size (px scrollbar-tot-thick)]]]) + (add-map-to-hiccup-call + (cmerger :v-scroll-section) + [box/v-box + :src (at) + :children [[box/gap + :src (at) + :size (px (or column-header-height 0))] + [box/box + :src (at) + :size "auto" + :child (add-map-to-hiccup-call + (flatten-attr + (cmerger + :v-scroll {:scrollbar-margin scrollbar-margin})) + [scrollbar + :src (at) + :type :vertical + :length @rl-row-viewport-height + :width scrollbar-thickness + :content-length @content-rows-height + :scroll-pos @scroll-y + :on-change on-v-scroll-change])] + [box/gap + :src (at) + :size (px (or column-footer-height 0))] + [box/gap + :src (at) + :size (px scrollbar-tot-thick)]]]) ;; ========== Debug section - #_ [:pre - {:style {:font-size "11px" - :min-width "220px"}} - (str - "virtual?: " virtual? "\n" - "row-height: " row-height "\n" - "rows-per-viewport: " @rows-per-viewport "\n" - "rows: " (if virtual? (count @virtual-rows) (count @model)) " of " (count @model) "\n" - "\n" - - "top-row-index: " @top-row-index "\n" - "bot-row-index: " @bot-row-index "\n" - "max-scroll-y: " @max-scroll-y "\n" - "scroll-y: " @scroll-y "\n" - "v-scroll-y: " @virtual-scroll-y "\n" - "\n" - - "left-col-px: " @scroll-x "\n" - "right-col-px: " (+ @scroll-x @rl-row-viewport-width -1) "\n" - "max-scroll-x: " @max-scroll-x "\n" - "scroll-x: " @scroll-x "\n" - "\n" - - "selection-target: " (if @dragging? @selection-target "-") "\n" - "sel-parent-l/t: " (if @dragging? (str "(" (.-left @sel-parent-bounding-rect) "," (.-top @sel-parent-bounding-rect) ")") "-") "\n" - "sel-parent-r/b: " (if @dragging? (str "(" (.-right @sel-parent-bounding-rect) "," (.-bottom @sel-parent-bounding-rect) ")") "-") "\n" - "sel-parent-w/h: " (if @dragging? (str "(" (.-width @sel-parent-bounding-rect) "," (.-height @sel-parent-bounding-rect) ")") "-") "\n" - "\n" - - "sel-x/y-start: " (if @dragging? (str "(" @sel-content-x-start "," @sel-content-y-start ")") "-") "\n" - "sel-x/y-end: " (if @dragging? (str "(" @sel-content-x-end "," @sel-content-y-end ")") "-") "\n" - "dragging-outside?: " @dragging-outside? "\n" - "sel-rows: " (if @dragging? (str "(" (:start-row @coords-debug) "," (:end-row @coords-debug) ")") "-") "\n" - "sel-cols: " (if @dragging? (str "(" (:start-col @coords-debug) "," (:end-col @coords-debug) ")") "-") "\n" - "clientXY: " (if @dragging? (str "(" (.-clientX @event-debug) "," (.-clientY @event-debug) ")") "-") "\n" - - "viewport-wh: " (str "(" (.-innerWidth js/window) "," (.-innerHeight js/window) ")") "\n" - "content-rows-wh: " (str "(" @content-rows-width "," @content-rows-height ")") "\n")]]])))))}))))) + #_[:pre + {:style {:font-size "11px" + :min-width "220px"}} + (str + "virtual?: " virtual? "\n" + "row-height: " row-height "\n" + "rows-per-viewport: " @rows-per-viewport "\n" + "rows: " (if virtual? (count @virtual-rows) (count @model)) " of " (count @model) "\n" + "\n" + + "top-row-index: " @top-row-index "\n" + "bot-row-index: " @bot-row-index "\n" + "max-scroll-y: " @max-scroll-y "\n" + "scroll-y: " @scroll-y "\n" + "v-scroll-y: " @virtual-scroll-y "\n" + "\n" + + "left-col-px: " @scroll-x "\n" + "right-col-px: " (+ @scroll-x @rl-row-viewport-width -1) "\n" + "max-scroll-x: " @max-scroll-x "\n" + "scroll-x: " @scroll-x "\n" + "\n" + + "selection-target: " (if @dragging? @selection-target "-") "\n" + "sel-parent-l/t: " (if @dragging? (str "(" (.-left @sel-parent-bounding-rect) "," (.-top @sel-parent-bounding-rect) ")") "-") "\n" + "sel-parent-r/b: " (if @dragging? (str "(" (.-right @sel-parent-bounding-rect) "," (.-bottom @sel-parent-bounding-rect) ")") "-") "\n" + "sel-parent-w/h: " (if @dragging? (str "(" (.-width @sel-parent-bounding-rect) "," (.-height @sel-parent-bounding-rect) ")") "-") "\n" + "\n" + + "sel-x/y-start: " (if @dragging? (str "(" @sel-content-x-start "," @sel-content-y-start ")") "-") "\n" + "sel-x/y-end: " (if @dragging? (str "(" @sel-content-x-end "," @sel-content-y-end ")") "-") "\n" + "dragging-outside?: " @dragging-outside? "\n" + "sel-rows: " (if @dragging? (str "(" (:start-row @coords-debug) "," (:end-row @coords-debug) ")") "-") "\n" + "sel-cols: " (if @dragging? (str "(" (:start-col @coords-debug) "," (:end-col @coords-debug) ")") "-") "\n" + "clientXY: " (if @dragging? (str "(" (.-clientX @event-debug) "," (.-clientY @event-debug) ")") "-") "\n" + + "viewport-wh: " (str "(" (.-innerWidth js/window) "," (.-innerHeight js/window) ")") "\n" + "content-rows-wh: " (str "(" @content-rows-width "," @content-rows-height ")") "\n")]]])))))}))))) ;"call-count: " @call-count "\n" From 675e343abe950c53a1afd7bf5855c63934775bd8 Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Fri, 23 Feb 2024 06:36:42 -0800 Subject: [PATCH 64/65] Css split out - reuse backdrop component from popover.cljs --- src/re_com/popover.cljs | 2 +- src/re_com/tree_select.cljs | 327 ++++++++++++++++++------------------ 2 files changed, 168 insertions(+), 161 deletions(-) diff --git a/src/re_com/popover.cljs b/src/re_com/popover.cljs index beaa8188..d683cd2d 100644 --- a/src/re_com/popover.cljs +++ b/src/re_com/popover.cljs @@ -156,7 +156,7 @@ :background-color "black" :opacity (or opacity 0.0)})}}) -(defn- backdrop +(defn backdrop "Renders a backdrop div which fills the entire page and responds to clicks on it. Can also specify how tranparent it should be" [& {:keys [opacity on-click class style attr] :as args}] (or diff --git a/src/re_com/tree_select.cljs b/src/re_com/tree_select.cljs index 6d327d85..602740ce 100644 --- a/src/re_com/tree_select.cljs +++ b/src/re_com/tree_select.cljs @@ -6,9 +6,10 @@ [clojure.string :as str] [reagent.core :as r] [re-com.config :refer [include-args-desc?]] - [re-com.util :refer [deref-or-value remove-id-item]] + [re-com.util :refer [deref-or-value remove-id-item add-map-to-hiccup-call flatten-attr merge-css]] [re-com.box :refer [h-box v-box box gap]] [re-com.checkbox :refer [checkbox]] + [re-com.popover :refer [backdrop]] [re-com.validate :as validate :refer [css-style? html-attr? parts?] :refer-macros [validate-args-macro]])) (def tree-select-dropdown-parts-desc @@ -22,6 +23,43 @@ {:name :dropdown-wrapper :level 2 :class "rc-tree-select-dropdown-dropdown-wrapper" :impl "[v-box]"} {:name :body :level 3 :class "rc-tree-select-dropdown-body" :impl "[tree-select]"}])) +(def tree-select-dropdown-css-spec + {:main {:class ["rc-tree-select-dropdown" "fade in"] + :style {:position "relative"}} + :wrapper {:class ["rc-tree-select-dropdown-wrapper"] + :style (fn [{:keys [max-width min-width]}] + {:display "inline-block" + :max-width max-width + :min-width min-width})} + :anchor {:class ["rc-tree-select-dropdown-anchor"] + :style (fn [{:keys [disabled? min-width max-width]}] + {:min-width min-width + :max-width max-width + :background-color (if disabled? "#EEE" "white") + :border "1px solid lightgrey" + :border-radius "2px" + :overflow "hidden" + :cursor (if disabled? "default" "pointer")})} + :anchor-label {:style (fn [{:keys [max-width]}] + {:max-width max-width + :white-space "nowrap" + :overflow "hidden" + :text-overflow "ellipsis"})} + :counter {:class ["rc-tree-select-dropdown-counter"] + :style {:margin-left "10px" + :margin-right "10px" + :opacity "50%"}} + :anchor-expander {:class ["rc-tree-select-dropdown-anchor-expander"]} + :backdrop {:class ["rc-tree-select-dropdown-backdrop"]} + :dropdown-wrapper {:class ["rc-tree-select-dropdown-dropdown-wrapper"] + :style {:position "absolute" + :background-color "white" + :border-radius "4px" + :border "1px solid #ccc" + :padding "5px 10px 5px 5px" + :box-shadow "0 5px 10px rgba(0, 0, 0, .2)"}} + :body {:class ["rc-tree-select-dropdown-body"]}}) + (def tree-select-dropdown-parts (when include-args-desc? (-> (map :name tree-select-dropdown-parts-desc) set))) @@ -36,6 +74,17 @@ {:name :expander :level 3 :class "rc-tree-select-expander" :impl "[box]"} {:name :checkbox :level 3 :class "rc-tree-select-checkbox" :impl "[checkbox]"}])) +(def tree-select-css-spec + {:main {:class ["rc-tree-select"]} + :wrapper {:class ["rc-tree-select-wrapper"] + :style {:overflow-y "scroll"}} + :choice {:class ["rc-tree-select-choice"]} + :group {:class ["rc-tree-select-group"]} + :offset {:class ["rc-tree-select-offset"]} + :expander {:class ["rc-tree-select-expander"] + :style {:cursor "pointer"}} + :checkbox {:class ["rc-tree-select-checkbox"]}}) + (def tree-select-parts (when include-args-desc? (-> (map :name tree-select-parts-desc) set))) @@ -170,72 +219,55 @@ :validate-fn (parts? tree-select-dropdown-parts) :description "See Parts section below."}]))) -(defn backdrop - [{:keys [opacity on-click parts]}] - [:div - (merge - (into {:class (str "noselect rc-backdrop " (get-in parts [:backdrop :class])) - :style (into {:position "fixed" - :left "0px" - :top "0px" - :width "100%" - :height "100%" - :background-color "black" - :opacity (or opacity 0.0)} - (get-in parts [:backdrop :style])) - :on-click (when on-click (handler-fn (on-click)))} - (get-in parts [:backdrop :attr])))]) - -(defn offset [& {:keys [parts level]}] - [box - :src (at) - :style (into {:visibility "hidden"} (get-in parts [:offset :style])) - :class (str "rc-tree-select-offset " (get-in parts [:offset :class])) - :attr (get-in parts [:offset :attr]) - :child (apply str (repeat level "⯈"))]) +(defn offset [& {:keys [level] :as args}] + (let [cmerger (merge-css tree-select-css-spec args)] + (add-map-to-hiccup-call + (cmerger :offset) + [box + :src (at) + :child (apply str (repeat level "⯈"))]))) -(defn choice-checkbox [{:keys [parts checked? toggle! label disabled? attr]}] - [checkbox - :src (at) - :style (get-in parts [:checkbox :style]) - :class (str "rc-tree-select-checkbox " (get-in parts [:checkbox :class])) - :attr (into attr (get-in parts [:checkbox :attr])) - :model checked? - :on-change toggle! - :label label - :disabled? disabled?]) +(defn choice-checkbox [{:keys [checked? toggle! label disabled?] :as args}] + (let [cmerger (merge-css tree-select-css-spec args)] + (add-map-to-hiccup-call + (cmerger :checkbox) + [checkbox + :src (at) + :model checked? + :on-change toggle! + :label label + :disabled? disabled?]))) -(defn choice-item [& {:keys [level showing? parts] :as props}] +(defn choice-item [& {:keys [level showing? parts] :as args}] (when showing? - [h-box - :src (at) - :style (get-in parts [:choice :style]) - :class (str "rc-tree-select-choice " (get-in parts [:choice :class])) - :attr (get-in parts [:choice :attr]) - :children - [[offset :parts parts :level level] - [choice-checkbox props]]])) + (let [cmerger (merge-css tree-select-css-spec args)] + (add-map-to-hiccup-call + (cmerger :choice) + [h-box + :src (at) + :children + [[offset :parts parts :level level] + [choice-checkbox args]]])))) -(defn group-item [& {:keys [label checked? toggle! hide-show! level showing? open? disabled? parts] :as props}] +(defn group-item [& {:keys [label checked? toggle! hide-show! level showing? open? disabled? parts] :as args}] (when showing? - [h-box - :src (at) - :style (get-in parts [:group :style]) - :class (str "rc-tree-select-group " (get-in parts [:group :class])) - :attr (get-in parts [:group :attr]) - :children - [[offset :parts parts :level (dec level)] - [box - :src (at) - :attr (into {:on-click hide-show!} (get-in parts [:expander :attr])) - :style (into {:cursor "pointer"} (get-in parts [:expander :style])) - :class (str "rc-tree-select-expander " (get-in parts [:expander :class])) - :child - (if open? "⯆" "⯈")] - " " - [choice-checkbox (into props {:attr {:ref #(when % - (set! (.-indeterminate %) - (= :some checked?)))}})]]])) + (let [cmerger (merge-css tree-select-css-spec args)] + (add-map-to-hiccup-call + (cmerger :group) + [h-box + :src (at) + :children + [[offset :parts parts :level (dec level)] + (add-map-to-hiccup-call + (cmerger :expander) + [box + :src (at) + :child + (if open? "⯆" "⯈")]) + " " + [choice-checkbox (into args {:attr {:ref #(when % + (set! (.-indeterminate %) + (= :some checked?)))}})]]])))) (def group? (comp #{:group} :type)) @@ -348,17 +380,17 @@ new-groups (into #{} (map :group) (full-groups new-model choices))] (on-change new-model new-groups))) :checked? (get model id) - :level (inc (count group))])))] - [v-box - :src (at) - :min-width min-width - :max-width max-width - :min-height min-height - :max-height max-height - :class (str "rc-tree-select-wrapper " class (get-in parts [:wrapper :class])) - :style (merge {:overflow-y "scroll"} style (get-in parts [:wrapper :style])) - :attr (merge attr (get-in parts [:wrapper :attr])) - :children (mapv item items)]))))) + :level (inc (count group))]))) + cmerger (merge-css tree-select-css-spec args)] + (add-map-to-hiccup-call + (cmerger :wrapper) + [v-box + :src (at) + :min-width min-width + :max-width max-width + :min-height min-height + :max-height max-height + :children (mapv item items)])))))) (defn field-label [{:keys [items group-label-fn label-fn]}] (let [item-label-fn #((if (group? %) group-label-fn label-fn) %)] @@ -405,96 +437,71 @@ anchor-label (field-label-fn {:items labelable-items :label-fn label-fn :group-label-fn group-label-fn}) - body [v-box - :src (at) - :height "fit-content" - :class (str "rc-tree-select-dropdown-dropdown-wrapper " (get-in parts [:dropdown-wrapper :class])) - :attr (get-in parts [:dropdown-wrapper :attr]) - :style (merge {:position "absolute" - :background-color "white" - :border-radius "4px" - :border "1px solid #ccc" - :padding "5px 10px 5px 5px" - :box-shadow "0 5px 10px rgba(0, 0, 0, .2)"} - (get-in parts [:dropdown-wrapper :style])) - :children [[tree-select - :class (str "rc-tree-select-dropdown-body " (get-in parts [:body :class])) - :style (get-in parts [:body :style]) - :attr (get-in parts [:body :attr]) - :choices choices - :group-label-fn group-label-fn - :disabled? disabled? - :min-width min-width - :max-width max-width - :min-height min-height - :max-height max-height - :on-change on-change - :label-fn label-fn - :model model]]] + cmerger (merge-css tree-select-dropdown-css-spec args) + body (add-map-to-hiccup-call + (cmerger :dropdown-wrapper) + [v-box + :src (at) + :height "fit-content" + :children [(add-map-to-hiccup-call + (cmerger :body) + [tree-select + :choices choices + :group-label-fn group-label-fn + :disabled? disabled? + :min-width min-width + :max-width max-width + :min-height min-height + :max-height max-height + :on-change on-change + :label-fn label-fn + :model model])]]) anchor (fn [] (let [model (deref-or-value model) disabled? (deref-or-value disabled?)] - [h-box - :src (at) - :height height - :padding "0px 6px" - :class (str "rc-multi-select-dropdown " (get-in parts [:anchor :class])) - :style (merge {:min-width min-width - :max-width max-width - :background-color (if disabled? "#EEE" "white") - :border "1px solid lightgrey" - :border-radius "2px" - :overflow "hidden" - :cursor (if disabled? "default" "pointer")} - style - (get-in parts [:anchor :style])) - :attr (merge {} - (when (not disabled?) {:on-click #(swap! showing? not)}) - (get-in parts [:anchor :attr])) - :children [(if (empty? model) - placeholder - (let [selections (filter (comp (set model) id-fn) choices) - _ (reset! !anchor-label anchor-label)] - [:span {:ref #(reset! !anchor-span %) - :title (alt-text-fn {:items labelable-items - :label-fn label-fn - :group-label-fn group-label-fn}) - :style {:max-width max-width - :white-space "nowrap" - :overflow "hidden" - :text-overflow "ellipsis"}} - anchor-label])) - [gap - :src (at) - :size "1"] - (when-let [model (seq model)] - [box - :src (at) - :class (str "rc-tree-select-dropdown-counter " (get-in parts [:counter :class])) - :style (merge {:margin-left "10px" - :margin-right "10px" - :opacity "50%"} - (get-in parts [:counter :style])) - :attr (get-in parts [:counter :attr]) - :child (str (count model))]) - (when-not disabled? - [box - :src (at) - :class (str "rc-tree-select-dropdown-anchor-expander " (get-in parts [:anchor-expander :class])) - :style (get-in parts [:anchor-expander :style]) - :attr (get-in parts [:anchor-expander :attr]) - :child - (if @showing? "▲" "▼")])]]))] - [:div (into {:class (str "rc-tree-select-dropdown-wrapper " (get-in parts [:wrapper :class])) - :style (into {:display "inline-block" - :maxWidth max-width - :minWidth min-width} - (get-in parts [:wrapper :style]))} - (get-in parts [:wrapper :attr])) + (add-map-to-hiccup-call + (cmerger :anchor {:disabled? disabled? + :min-width min-width + :max-width max-width + :attr + {:on-click #(swap! showing? not)}}) + [h-box + :src (at) + :height height + :padding "0px 6px" + :children [(if (empty? model) + placeholder + (let [selections (filter (comp (set model) id-fn) choices) + _ (reset! !anchor-label anchor-label)] + [:span (merge + (flatten-attr (cmerger :anchor-label {:max-width max-width})) + {:ref #(reset! !anchor-span %) + :title (alt-text-fn {:items labelable-items + :label-fn label-fn + :group-label-fn group-label-fn})}) + anchor-label])) + [gap + :src (at) + :size "1"] + (when-let [model (seq model)] + (add-map-to-hiccup-call + (cmerger :counter) + [box + :src (at) + :child (str (count model))])) + (when-not disabled? + (add-map-to-hiccup-call + (cmerger :anchor-expander) + [box + :src (at) + :child + (if @showing? "▲" "▼")]))]])))] + [:div (flatten-attr (cmerger :wrapper {:max-width max-width :min-width min-width})) [anchor] (when @showing? - [:div {:class "fade in" - :style {:position "relative"}} - [backdrop {:parts parts - :on-click #(reset! showing? false)}] + [:div (flatten-attr (cmerger :main)) + (add-map-to-hiccup-call + (cmerger :backdrop) + [backdrop + :on-click #(reset! showing? false)]) body])])))) From 1ba0b8a85f702864d88108c2ea7a6dc05274929c Mon Sep 17 00:00:00 2001 From: "bnmcgn@gmail.com" Date: Sat, 24 Feb 2024 07:50:15 -0800 Subject: [PATCH 65/65] Bugfixes --- src/re_com/tree_select.cljs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/re_com/tree_select.cljs b/src/re_com/tree_select.cljs index 602740ce..3646709b 100644 --- a/src/re_com/tree_select.cljs +++ b/src/re_com/tree_select.cljs @@ -80,7 +80,8 @@ :style {:overflow-y "scroll"}} :choice {:class ["rc-tree-select-choice"]} :group {:class ["rc-tree-select-group"]} - :offset {:class ["rc-tree-select-offset"]} + :offset {:class ["rc-tree-select-offset"] + :style {:visibility "hidden"}} :expander {:class ["rc-tree-select-expander"] :style {:cursor "pointer"}} :checkbox {:class ["rc-tree-select-checkbox"]}}) @@ -227,10 +228,10 @@ :src (at) :child (apply str (repeat level "⯈"))]))) -(defn choice-checkbox [{:keys [checked? toggle! label disabled?] :as args}] +(defn choice-checkbox [{:keys [checked? toggle! label disabled? indeterminate?] :as args}] (let [cmerger (merge-css tree-select-css-spec args)] (add-map-to-hiccup-call - (cmerger :checkbox) + (cmerger :checkbox {:attr {:ref #(when % (set! (.-indeterminate %) indeterminate?))}}) [checkbox :src (at) :model checked? @@ -259,15 +260,13 @@ :children [[offset :parts parts :level (dec level)] (add-map-to-hiccup-call - (cmerger :expander) + (cmerger :expander {:attr {:on-click hide-show!}}) [box :src (at) :child (if open? "⯆" "⯈")]) " " - [choice-checkbox (into args {:attr {:ref #(when % - (set! (.-indeterminate %) - (= :some checked?)))}})]]])))) + [choice-checkbox (into args {:indeterminate? (= :some checked?)})]]])))) (def group? (comp #{:group} :type))