From b6a69968997c85bf071ab1537471825305eb8a1e Mon Sep 17 00:00:00 2001 From: Bruno Burke Date: Wed, 29 Jul 2020 11:31:17 +0200 Subject: [PATCH] restructure bulma component files --- .../lernmeister/components/bulma/card.cljs | 20 + .../lernmeister/components/bulma/core.cljs | 502 ------------------ .../components/bulma/datetime.cljs | 198 +++++++ .../components/bulma/form/field.cljs | 22 + .../lernmeister/components/bulma/modal.cljs | 37 ++ .../lernmeister/components/bulma/navbar.cljs | 86 +++ .../lernmeister/components/bulma/select.cljs | 75 +++ .../lernmeister/components/bulma/tag.cljs | 60 +++ .../lernmeister/components/bulma/toast.cljs | 35 ++ src/cljs/lernmeister/components/ui.cljs | 44 +- .../lernmeister/components/core_card.cljs | 2 +- .../{bulma_card.cljs => ui_showcase.cljs} | 42 +- 12 files changed, 580 insertions(+), 543 deletions(-) create mode 100644 src/cljs/lernmeister/components/bulma/card.cljs create mode 100644 src/cljs/lernmeister/components/bulma/datetime.cljs create mode 100644 src/cljs/lernmeister/components/bulma/form/field.cljs create mode 100644 src/cljs/lernmeister/components/bulma/modal.cljs create mode 100644 src/cljs/lernmeister/components/bulma/navbar.cljs create mode 100644 src/cljs/lernmeister/components/bulma/select.cljs create mode 100644 src/cljs/lernmeister/components/bulma/tag.cljs create mode 100644 src/cljs/lernmeister/components/bulma/toast.cljs rename src/devcards/lernmeister/components/{bulma_card.cljs => ui_showcase.cljs} (93%) diff --git a/src/cljs/lernmeister/components/bulma/card.cljs b/src/cljs/lernmeister/components/bulma/card.cljs new file mode 100644 index 0000000..87b8bb4 --- /dev/null +++ b/src/cljs/lernmeister/components/bulma/card.cljs @@ -0,0 +1,20 @@ +(ns lernmeister.components.bulma.card) + +(defn card-panel [& content] + [:div.card + [:div.card-content + (apply vector + :div.content + content)]]) + +(defn card [{:keys [title footer-items class]} content] + [:div.card {:class class} + (when title + [:header.card-header + [:p.card-header-title + title]]) + [:div.card-content content] + (when (pos? (count footer-items)) + [:footer.card-footer + (for [fi footer-items] + ^{:key (hash fi)} [:span.card-footer-item fi])])]) diff --git a/src/cljs/lernmeister/components/bulma/core.cljs b/src/cljs/lernmeister/components/bulma/core.cljs index c90e2e1..54511df 100644 --- a/src/cljs/lernmeister/components/bulma/core.cljs +++ b/src/cljs/lernmeister/components/bulma/core.cljs @@ -1,34 +1,9 @@ (ns lernmeister.components.bulma.core (:require [reagent.core :as reagent] [clojure.string :refer [lower-case split]] - [lernmeister.components.jshelper :refer [execute-and-reset]] - [lernmeister.components.bulma.table :as bulma-table] [lernmeister.components.bulma.common :refer [icon]] [oops.core :refer [oget]])) -(def table bulma-table/table) - -(defonce toastcounter (atom 0)) - -(defn card-panel [& content] - [:div.card - [:div.card-content - (apply vector - :div.content - content)]]) - -(defn card [{:keys [title footer-items class]} content] - [:div.card {:class class} - (when title - [:header.card-header - [:p.card-header-title - title]]) - [:div.card-content content] - (when (pos? (count footer-items)) - [:footer.card-footer - (for [fi footer-items] - ^{:key (hash fi)} [:span.card-footer-item fi])])]) - (defn radiobutton [{:keys [name disabled? on-change checked? label]}] (let [id (gensym "radiobutton")] @@ -167,286 +142,6 @@ [:span.lever] (or label-right "")]])}))) -(defn select - [{:keys [value on-change options label multiple - choose-option-label]}] - (let [id (gensym "select") - instance (reagent/atom nil) - change-fn (reagent/atom on-change)] - (reagent/create-class - {:display-name "input-select-component" - :reagent-render - (fn [{:keys [value on-change options label disabled multiple - choose-option-label]}] - [:div.field - [:label.label {:for id} label] - [:div.select.control.is-fullwidth - {:class (when multiple - "is-multiple")} - [:select - {:id id - :disabled disabled - :multiple multiple - :value (if multiple - (or value []) - (or value "")) - :on-change (fn [event] - (js/console.log multiple) - (let [value (-> event - .-target - .-value)] - (when on-change - (on-change - (if multiple - (set (map (fn [o] - (keyword (.-value o))) - (-> event - .-target - .-selectedOptions - array-seq))) - (keyword value))))))} - [:option {:value "" - :disabled true} - (or choose-option-label "Bitte wählen")] - (when options - (doall - (for [o options] - (let [key (or (:key o) (first o)) - title (or (:title o) (:label o) (second o))] - [:option {:value (name key) - :key (str id "-" key)} - title]))))]]])}))) - -(defn select* - [{:keys [cursor options label multiple - choose-option-label on-change]}] - (let [id (gensym "select") - instance (reagent/atom nil) - get-element #(js/document.querySelector (str "#" id))] - (reagent/create-class - {:display-name "select*-component" - :reagent-render - (fn [{:keys [cursor options label multiple - choose-option-label on-change]}] - [select - {:value @cursor - :options options - :label label - :multiple multiple - :choose-option-label choose-option-label - :on-change (fn [value] - (reset! cursor value) - (when on-change - (on-change cursor)))}])}))) - -(defn tags-editor - [{:keys [tags label placeholder prefix-icon]}] - (let [add-element? (reagent/atom nil) - new-element (reagent/atom "")] - (reagent/create-class - {:reagent-render - (fn [{:keys [tags label placeholder prefix-icon]}] - (let [tags-set (into #{} (filter (comp not clojure.string/blank?) @tags))] - [:div.field.is-grouped.is-grouped-multiline - (for [t tags-set] - ^{:key (str "tags" t)} - [:div.control - [:div.tags.has-addons - [:span.tag - t] - [:a.tag.is-delete {:on-click #(swap! tags (fn [tags] - (set (remove (partial = t) tags))))}]]]) - - (if @add-element? - [:div.control.level - [:div.level-item - [input-text - {:value @new-element - :on-change #(reset! new-element %)}]] - [:div.level-item - [:button.button {:on-click #(do - (swap! tags (fn [t] - (-> t - (conj @new-element) - set))) - (reset! new-element "") - (swap! add-element? not))} - [:span.icon [:i.fas.fa-check]]]] - [:div.level-item - [:button.button {:on-click #(do - (reset! new-element "") - (swap! add-element? not))} - [:span.icon [:i.fas.fa-times]]]]] - [:div.control - [:div.tags - [:a.tag {:on-click #(swap! add-element? not)} - [:span.icon [:i.fas.fa-plus]]]]])]))}))) - -(defn tags - [{:keys [tags label placeholder prefix-icon]}] - (reagent/create-class - {:reagent-render - (fn [{:keys [tags label placeholder prefix-icon]}] - (let [tags-set (into #{} (filter (comp not clojure.string/blank?) tags))] - [:div.field.is-grouped.is-grouped-multiline - (for [t tags-set] - ^{:key (str "tags" t)} - [:div.control - [:div.tags.has-addons - [:span.tag t]]])]))})) - -(def chips tags-editor) ;;fallback - - -(defn navbar - [{:keys [links active-link brand brand-align - links-position - side-nav - side-nav-id - color - font-color - fixed - side-nav-trigger-position :left - side-nav-trigger-position] - :or {brand false - brand-align "right" - side-nav false - color "purple" - font-color "black"}}] - (let [navbar-id (gensym "navbar")] - (reagent/create-class - {:reagent-render - (fn [{:keys [links active-link brand brand-align - links-position - side-nav - side-nav-id - color - font-color - fixed - side-nav-trigger-position] - :or {brand false - side-nav false - side-nav-trigger-position :left - color "purple" - font-color "black"}}] - [:nav.navbar {:class (when fixed "navbar-fixed")} - (when brand - [:div.navbar-brand {:class (str "brand-logo " (when brand-align - (name brand-align)))} - [:a.navbar-item brand]]) - [:div.navbar-menu - [:div.navbar-start - (when (and side-nav (= (keyword side-nav-trigger-position) :left)) - [:a.navbar-burger {:href "#" - :data-target side-nav-id} - [:i.material-icons "menu"]]) - (for [nl links] - [:a.navbar-item {:key (hash nl) - :href (:route nl) - :style {:color font-color}} - (when (:icon nl) - [icon (:icon nl) :class "left"]) (:label nl)])] - [:div.navbar-end - (when (and side-nav (= (keyword side-nav-trigger-position) :right)) - [:a.sidenav-trigger.right {:href "#" - :data-target side-nav-id} - [:i.material-icons "menu"]])]]])}))) - -(defn side-nav [& {:keys [links active-link - side-nav-id - edge] - :or {side-nav-id (gensym "side-nav") - edge "left"}}] - (let [instance (reagent/atom nil) - id side-nav-id] - (reagent/create-class - {:component-did-mount #(let [elem (js/document.querySelector (str "#" id))] - #_(reset! instance - (js/M.Sidenav.init elem (clj->js {:closeOnClick true - :draggable true - :edge edge})))) - :reagent-render - (fn [& {:keys [links active-link - side-nav-id - edge] - :or {side-nav-id (gensym "side-nav") - edge "left"}}] - [:p ""] - #_[:ul.sidenav {:id id} - (for [nl links] - [:li {:class (when (= (:panel nl) active-link) ["active"]) - :key (str id "_" (:label nl))} - [:a {:href (:route nl) - :on-click #(.close @instance)} - (when (:icon nl) - [icon (:icon nl) :class "left"]) (:label nl)]])])}))) - -(defonce toastbox-id (str (gensym "toastbox"))) - -(defn get-toastbox [] - (if-let [toastbox (js/document.getElementById toastbox-id)] - toastbox - (let [div (js/document.createElement "div")] - (set! (.-id div) toastbox-id) - (set! (.-classList div) (str "toastbox")) - (js/document.body.appendChild div) - (recur)))) - -(defn toast - [& {:keys [html displayLength classes]}] - (let [div (js/document.createElement "div") - initial-classes (str "notification toast " classes)] - (set! (.-innerHTML div) html) - (set! (.-classList div) initial-classes) - (js/setTimeout #(do - (set! (.-classList div) initial-classes) - ;;;wait css fade-out - (js/setTimeout (fn [] - (.remove div)) 1000)) - (or displayLength 1000)) - (.appendChild (get-toastbox) div) - (set! (.-classList div) (str (.-classList div) " active"))) - - #_(js/M.toast (clj->js (merge {:html html} - (when displayLength - {:displayLength displayLength}) - (when classes - {:classes classes}))))) - -(defn modal-panel [{:keys [title state footer subject on-close-fn]} & content] - (let [close-fn (fn [] - (execute-and-reset on-close-fn subject) - (reset! state false))] - [:div.modal {:class (str (when @state - "is-active"))} - [:div.modal-background {:on-click close-fn}] - [:div.modal-card - [:header.modal-card-head - [:span.modal-card-title (or title "")] - [:button.delete {:on-click close-fn}]] - (into [:div.modal-card-body] - content) - (when footer - [:footer.modal-card-foot - footer])]])) - -(defn open-modal [selector-tag] - #_(.open (js/M.Modal.getInstance (js/document.querySelector selector-tag)))) - -(defn close-modal [selector-tag] - #_(.close (js/M.Modal.getInstance (js/document.querySelector selector-tag)))) - -(defn modal-exists? [selector-tag] - #_(try - (js/M.Modal.getInstance (js/document.querySelector selector-tag)) - (catch js/Error. e - nil))) - -(defn init-modal [selector-tag] - #_(if-let [instance (modal-exists? selector-tag)] - instance - (js/M.Modal.init (js/document.querySelector selector-tag)))) - (defn tooltip [{:keys [selector-tag label]}] (js/M.Tooltip.init (js/document.querySelector selector-tag) (clj->js {:html label}))) @@ -508,203 +203,6 @@ (when-let [td (nth tabs @selected-tab)] (:component td))]])}))) -(defn parseDate [date] - (if (not-empty date) - (let [parts (split (js->clj date) ".") - parsed-date (js/Date. (js/Number. (get parts 2)) - (- (js/Number. (get parts 1)) 1) - (js/Number. (get parts 0)))] - parsed-date) - (new js/Date))) - -(def weekdays ["Sonntag" - "Montag" - "Dienstag" - "Mittwoch" - "Donnerstag" - "Freitag" - "Samstag"]) - -(def calendar-settings - {:i18n {:months ["Januar" - "Februar" - "März" - "April" - "Mai" - "Juni" - "Juli" - "August" - "September" - "Oktober" - "November" - "Dezember"] - :monthsShort ["Jan" - "Feb" - "Mär" - "Apr" - "Mai" - "Jun" - "Jul" - "Aug" - "Sep" - "Okt" - "Nov" - "Dez"] - :weekdays weekdays - :weekdaysShort ["So" - "Mo" - "Di" - "Mi" - "Do" - "Fr" - "Sa"] - :weekdaysAbbrev ["S" - "M" - "D" - "M" - "D" - "F" - "S"]} - :firstDay 1 - :format "dd.mm.yyyy" - :parse parseDate - :showDaysInNextAndPreviousMonths true}) - -(defn format-date [date] - (let [month (+ (.getMonth date) 1) - day (.getDate date)] - (str (.getFullYear date) - "-" - (when-not (>= month 10) - 0) - (+ (.getMonth date) 1) - "-" - (when-not (>= day 10) - 0) - day))) - -(defn datepicker [{:keys [field-class icon input-class input-size - label-icon value placeholder on-change - label disabled tooltip autocomplete]}] - (fn [{:keys [field-class icon input-class input-size - label-icon value placeholder on-change - label disabled tooltip autocomplete]}] - [:div.field - [:label.label - label] - [:div.control - - [:input {:type :date - :disabled disabled - :value (when value - (format-date value)) - :on-change #(on-change - (-> % - .-target - .-valueAsDate))}]]])) - -#_(defn datepicker - [{:keys [field-class icon input-class input-size - label-icon value placeholder on-change - label disabled tooltip autocomplete]}] - (let [id (gensym "datepicker") - instance (reagent/atom nil)] - (reagent/create-class - {:component-did-mount - (fn [] - (let [elems (js/document.querySelectorAll (str "#" id))] - (reset! instance - (.attach js/bulmaCalendar (str "[id=" id "]") - (clj->js {:displayMode "dialog"}))) - (when-let [calendar (first @instance)] - (.value calendar value) - (.on calendar "select" - (fn [event] - (on-change (oget event "data.date.start"))))))) - - :component-did-update (fn [this _] - #_(let [[{:keys [value]}] (rest (reagent/argv this))] - (when-let [calendar (first @instance)] - (.value calendar value)))) - :reagent-render - (fn [{:keys [field-class icon input-class input-size - label-icon value placeholder on-change - label disabled tooltip autocomplete]}] - [:div.field - ;;;TODO https://reactjs.org/docs/integrating-with-other-libraries.html - (when label - [:label.label {:for id} label]) - [:div.control.has-icons-left - [:span.icon.is-left [:i.fas.fa-calendar]] - [:input.input {:type "date" - :id id - :disabled disabled - :class input-class - :data-date value - :placeholder (or placeholder "") - :default-value (when value - (str (.getFullYear value) - "-" - (+ (.getMonth value) 1) - "-" - (.getDate value))) - ;;:on-change #(on-change (js/Date. (-> % .-target .-value))) - }]]])}))) - -(defn timepicker [{:keys [field-class icon input-class input-size - label-icon value placeholder on-change - label disabled tooltip autocomplete]}] - (fn [{:keys [field-class icon input-class input-size - label-icon value placeholder on-change - label disabled tooltip autocomplete]}] - [:div.field - [:label.label - label] - [:div.control - - [:input {:type :time - :value value - :on-change #(on-change - (-> % - .-target - .-value))}]]])) - -#_(defn timepicker - [{:keys [field-class icon input-class input-size - value on-change placeholder label - disabled tooltip autocomplete]}] - (let [id (gensym "timepicker") - instances (reagent/atom nil)] - (reagent/create-class - {:component-did-mount - (fn [] - (let [elems (js/document.querySelectorAll (str "#" id))] - (reset! instances - (first (js/M.Timepicker.init - elems - (clj->js {:twelveHour false - :autoClose true - :onCloseEnd #(when-let [time (.-time @instances)] - (on-change time))})))))) - - :reagent-render - (fn [{:keys [field-class icon input-class input-size - placeholder value on-change label - disabled tooltip autocomplete]}] - [:div.input-field - (when icon - [:i.material-icons.prefix - icon]) - [:input {:type "text" - :id id - :class input-class - :disabled disabled - :placeholder (or placeholder "") - :read-only true - :value (or value "")}] - (when label - [:label.active {:for id} label])])}))) - (defn indeterminate-progress [] (let [percentage (str (+ 5 (rand-int 50)) "%")] [:progress.progress.is-info {:max "100"} diff --git a/src/cljs/lernmeister/components/bulma/datetime.cljs b/src/cljs/lernmeister/components/bulma/datetime.cljs new file mode 100644 index 0000000..8466e85 --- /dev/null +++ b/src/cljs/lernmeister/components/bulma/datetime.cljs @@ -0,0 +1,198 @@ +(ns lernmeister.components.bulma.datetime) + +(defn parseDate [date] + (if (not-empty date) + (let [parts (split (js->clj date) ".") + parsed-date (js/Date. (js/Number. (get parts 2)) + (- (js/Number. (get parts 1)) 1) + (js/Number. (get parts 0)))] + parsed-date) + (new js/Date))) + +(def weekdays ["Sonntag" + "Montag" + "Dienstag" + "Mittwoch" + "Donnerstag" + "Freitag" + "Samstag"]) + +(def calendar-settings + {:i18n {:months ["Januar" + "Februar" + "März" + "April" + "Mai" + "Juni" + "Juli" + "August" + "September" + "Oktober" + "November" + "Dezember"] + :monthsShort ["Jan" + "Feb" + "Mär" + "Apr" + "Mai" + "Jun" + "Jul" + "Aug" + "Sep" + "Okt" + "Nov" + "Dez"] + :weekdays weekdays + :weekdaysShort ["So" + "Mo" + "Di" + "Mi" + "Do" + "Fr" + "Sa"] + :weekdaysAbbrev ["S" + "M" + "D" + "M" + "D" + "F" + "S"]} + :firstDay 1 + :format "dd.mm.yyyy" + :parse parseDate + :showDaysInNextAndPreviousMonths true}) + +(defn format-date [date] + (let [month (+ (.getMonth date) 1) + day (.getDate date)] + (str (.getFullYear date) + "-" + (when-not (>= month 10) + 0) + (+ (.getMonth date) 1) + "-" + (when-not (>= day 10) + 0) + day))) + +(defn datepicker [{:keys [field-class icon input-class input-size + label-icon value placeholder on-change + label disabled tooltip autocomplete]}] + (fn [{:keys [field-class icon input-class input-size + label-icon value placeholder on-change + label disabled tooltip autocomplete]}] + [:div.field + [:label.label + label] + [:div.control + + [:input {:type :date + :disabled disabled + :value (when value + (format-date value)) + :on-change #(on-change + (-> % + .-target + .-valueAsDate))}]]])) + +#_(defn datepicker + [{:keys [field-class icon input-class input-size + label-icon value placeholder on-change + label disabled tooltip autocomplete]}] + (let [id (gensym "datepicker") + instance (reagent/atom nil)] + (reagent/create-class + {:component-did-mount + (fn [] + (let [elems (js/document.querySelectorAll (str "#" id))] + (reset! instance + (.attach js/bulmaCalendar (str "[id=" id "]") + (clj->js {:displayMode "dialog"}))) + (when-let [calendar (first @instance)] + (.value calendar value) + (.on calendar "select" + (fn [event] + (on-change (oget event "data.date.start"))))))) + + :component-did-update (fn [this _] + #_(let [[{:keys [value]}] (rest (reagent/argv this))] + (when-let [calendar (first @instance)] + (.value calendar value)))) + :reagent-render + (fn [{:keys [field-class icon input-class input-size + label-icon value placeholder on-change + label disabled tooltip autocomplete]}] + [:div.field + ;;;TODO https://reactjs.org/docs/integrating-with-other-libraries.html + (when label + [:label.label {:for id} label]) + [:div.control.has-icons-left + [:span.icon.is-left [:i.fas.fa-calendar]] + [:input.input {:type "date" + :id id + :disabled disabled + :class input-class + :data-date value + :placeholder (or placeholder "") + :default-value (when value + (str (.getFullYear value) + "-" + (+ (.getMonth value) 1) + "-" + (.getDate value))) + ;;:on-change #(on-change (js/Date. (-> % .-target .-value))) + }]]])}))) + +(defn timepicker [{:keys [field-class icon input-class input-size + label-icon value placeholder on-change + label disabled tooltip autocomplete]}] + (fn [{:keys [field-class icon input-class input-size + label-icon value placeholder on-change + label disabled tooltip autocomplete]}] + [:div.field + [:label.label + label] + [:div.control + + [:input {:type :time + :value value + :on-change #(on-change + (-> % + .-target + .-value))}]]])) + +#_(defn timepicker + [{:keys [field-class icon input-class input-size + value on-change placeholder label + disabled tooltip autocomplete]}] + (let [id (gensym "timepicker") + instances (reagent/atom nil)] + (reagent/create-class + {:component-did-mount + (fn [] + (let [elems (js/document.querySelectorAll (str "#" id))] + (reset! instances + (first (js/M.Timepicker.init + elems + (clj->js {:twelveHour false + :autoClose true + :onCloseEnd #(when-let [time (.-time @instances)] + (on-change time))})))))) + + :reagent-render + (fn [{:keys [field-class icon input-class input-size + placeholder value on-change label + disabled tooltip autocomplete]}] + [:div.input-field + (when icon + [:i.material-icons.prefix + icon]) + [:input {:type "text" + :id id + :class input-class + :disabled disabled + :placeholder (or placeholder "") + :read-only true + :value (or value "")}] + (when label + [:label.active {:for id} label])])}))) diff --git a/src/cljs/lernmeister/components/bulma/form/field.cljs b/src/cljs/lernmeister/components/bulma/form/field.cljs new file mode 100644 index 0000000..7fc5544 --- /dev/null +++ b/src/cljs/lernmeister/components/bulma/form/field.cljs @@ -0,0 +1,22 @@ +(ns lernmeister.components.bulma.form.field) + + +(defn field [{:keys [field-class label grouped? addons?]} & content] + [:div.field {:class (str field-class + (when grouped? + "is-grouped") + (when addons? + "has-addons"))} + (when label + [:label.label + label]) + (into [:<>] content)]) + + +(defn horizontal-field [{:keys [field-class label]} & content] + [:div.field {:class field-class} + (when label + [:div.field-label + label]) + [:div.field-body + (into [:<>] content)]]) diff --git a/src/cljs/lernmeister/components/bulma/modal.cljs b/src/cljs/lernmeister/components/bulma/modal.cljs new file mode 100644 index 0000000..80c5555 --- /dev/null +++ b/src/cljs/lernmeister/components/bulma/modal.cljs @@ -0,0 +1,37 @@ +(ns lernmeister.components.bulma.modal + (:require [lernmeister.components.jshelper :refer [execute-and-reset]])) + + +(defn modal-panel [{:keys [title state footer subject on-close-fn]} & content] + (let [close-fn (fn [] + (execute-and-reset on-close-fn subject) + (reset! state false))] + [:div.modal {:class (str (when @state + "is-active"))} + [:div.modal-background {:on-click close-fn}] + [:div.modal-card + [:header.modal-card-head + [:span.modal-card-title (or title "")] + [:button.delete {:on-click close-fn}]] + (into [:div.modal-card-body] + content) + (when footer + [:footer.modal-card-foot + footer])]])) + +(defn open-modal [selector-tag] + #_(.open (js/M.Modal.getInstance (js/document.querySelector selector-tag)))) + +(defn close-modal [selector-tag] + #_(.close (js/M.Modal.getInstance (js/document.querySelector selector-tag)))) + +(defn modal-exists? [selector-tag] + #_(try + (js/M.Modal.getInstance (js/document.querySelector selector-tag)) + (catch js/Error. e + nil))) + +(defn init-modal [selector-tag] + #_(if-let [instance (modal-exists? selector-tag)] + instance + (js/M.Modal.init (js/document.querySelector selector-tag)))) diff --git a/src/cljs/lernmeister/components/bulma/navbar.cljs b/src/cljs/lernmeister/components/bulma/navbar.cljs new file mode 100644 index 0000000..33bc5f6 --- /dev/null +++ b/src/cljs/lernmeister/components/bulma/navbar.cljs @@ -0,0 +1,86 @@ +(ns lernmeister.components.bulma.navbar + (:require [reagent.core :as reagent] + [lernmeister.components.bulma.common :refer [icon]])) + +(defn navbar + [{:keys [links active-link brand brand-align + links-position + side-nav + side-nav-id + color + font-color + fixed + side-nav-trigger-position :left + side-nav-trigger-position] + :or {brand false + brand-align "right" + side-nav false + color "purple" + font-color "black"}}] + (let [navbar-id (gensym "navbar")] + (reagent/create-class + {:reagent-render + (fn [{:keys [links active-link brand brand-align + links-position + side-nav + side-nav-id + color + font-color + fixed + side-nav-trigger-position] + :or {brand false + side-nav false + side-nav-trigger-position :left + color "purple" + font-color "black"}}] + [:nav.navbar {:class (when fixed "navbar-fixed")} + (when brand + [:div.navbar-brand {:class (str "brand-logo " (when brand-align + (name brand-align)))} + [:a.navbar-item brand]]) + [:div.navbar-menu + [:div.navbar-start + (when (and side-nav (= (keyword side-nav-trigger-position) :left)) + [:a.navbar-burger {:href "#" + :data-target side-nav-id} + [:i.material-icons "menu"]]) + (for [nl links] + [:a.navbar-item {:key (hash nl) + :href (:route nl) + :style {:color font-color}} + (when (:icon nl) + [icon (:icon nl) :class "left"]) (:label nl)])] + [:div.navbar-end + (when (and side-nav (= (keyword side-nav-trigger-position) :right)) + [:a.sidenav-trigger.right {:href "#" + :data-target side-nav-id} + [:i.material-icons "menu"]])]]])}))) + +(defn side-nav [& {:keys [links active-link + side-nav-id + edge] + :or {side-nav-id (gensym "side-nav") + edge "left"}}] + (let [instance (reagent/atom nil) + id side-nav-id] + (reagent/create-class + {:component-did-mount #(let [elem (js/document.querySelector (str "#" id))] + #_(reset! instance + (js/M.Sidenav.init elem (clj->js {:closeOnClick true + :draggable true + :edge edge})))) + :reagent-render + (fn [& {:keys [links active-link + side-nav-id + edge] + :or {side-nav-id (gensym "side-nav") + edge "left"}}] + [:p ""] + #_[:ul.sidenav {:id id} + (for [nl links] + [:li {:class (when (= (:panel nl) active-link) ["active"]) + :key (str id "_" (:label nl))} + [:a {:href (:route nl) + :on-click #(.close @instance)} + (when (:icon nl) + [icon (:icon nl) :class "left"]) (:label nl)]])])}))) diff --git a/src/cljs/lernmeister/components/bulma/select.cljs b/src/cljs/lernmeister/components/bulma/select.cljs new file mode 100644 index 0000000..7a6dfa2 --- /dev/null +++ b/src/cljs/lernmeister/components/bulma/select.cljs @@ -0,0 +1,75 @@ +(ns lernmeister.components.bulma.select + (:require [reagent.core :as reagent])) + + +(defn select + [{:keys [value on-change options label multiple + choose-option-label]}] + (let [id (gensym "select") + instance (reagent/atom nil) + change-fn (reagent/atom on-change)] + (reagent/create-class + {:display-name "input-select-component" + :reagent-render + (fn [{:keys [value on-change options label disabled multiple + choose-option-label]}] + [:div.field + [:label.label {:for id} label] + [:div.select.control.is-fullwidth + {:class (when multiple + "is-multiple")} + [:select + {:id id + :disabled disabled + :multiple multiple + :value (if multiple + (or value []) + (or value "")) + :on-change (fn [event] + (js/console.log multiple) + (let [value (-> event + .-target + .-value)] + (when on-change + (on-change + (if multiple + (set (map (fn [o] + (keyword (.-value o))) + (-> event + .-target + .-selectedOptions + array-seq))) + (keyword value))))))} + [:option {:value "" + :disabled true} + (or choose-option-label "Bitte wählen")] + (when options + (doall + (for [o options] + (let [key (or (:key o) (first o)) + title (or (:title o) (:label o) (second o))] + [:option {:value (name key) + :key (str id "-" key)} + title]))))]]])}))) + +(defn select* + [{:keys [cursor options label multiple + choose-option-label on-change]}] + (let [id (gensym "select") + instance (reagent/atom nil) + get-element #(js/document.querySelector (str "#" id))] + (reagent/create-class + {:display-name "select*-component" + :reagent-render + (fn [{:keys [cursor options label multiple + choose-option-label on-change]}] + [select + {:value @cursor + :options options + :label label + :multiple multiple + :choose-option-label choose-option-label + :on-change (fn [value] + (reset! cursor value) + (when on-change + (on-change cursor)))}])}))) diff --git a/src/cljs/lernmeister/components/bulma/tag.cljs b/src/cljs/lernmeister/components/bulma/tag.cljs new file mode 100644 index 0000000..1c9ab58 --- /dev/null +++ b/src/cljs/lernmeister/components/bulma/tag.cljs @@ -0,0 +1,60 @@ +(ns lernmeister.components.bulma.tag + (:require [reagent.core :as reagent])) + +(defn tags-editor + [{:keys [tags label placeholder prefix-icon]}] + (let [add-element? (reagent/atom nil) + new-element (reagent/atom "")] + (reagent/create-class + {:reagent-render + (fn [{:keys [tags label placeholder prefix-icon]}] + (let [tags-set (into #{} (filter (comp not clojure.string/blank?) @tags))] + [:div.field.is-grouped.is-grouped-multiline + (for [t tags-set] + ^{:key (str "tags" t)} + [:div.control + [:div.tags.has-addons + [:span.tag + t] + [:a.tag.is-delete {:on-click #(swap! tags (fn [tags] + (set (remove (partial = t) tags))))}]]]) + + (if @add-element? + [:div.control.level + [:div.level-item + [input-text + {:value @new-element + :on-change #(reset! new-element %)}]] + [:div.level-item + [:button.button {:on-click #(do + (swap! tags (fn [t] + (-> t + (conj @new-element) + set))) + (reset! new-element "") + (swap! add-element? not))} + [:span.icon [:i.fas.fa-check]]]] + [:div.level-item + [:button.button {:on-click #(do + (reset! new-element "") + (swap! add-element? not))} + [:span.icon [:i.fas.fa-times]]]]] + [:div.control + [:div.tags + [:a.tag {:on-click #(swap! add-element? not)} + [:span.icon [:i.fas.fa-plus]]]]])]))}))) + +(defn tags + [{:keys [tags label placeholder prefix-icon]}] + (reagent/create-class + {:reagent-render + (fn [{:keys [tags label placeholder prefix-icon]}] + (let [tags-set (into #{} (filter (comp not clojure.string/blank?) tags))] + [:div.field.is-grouped.is-grouped-multiline + (for [t tags-set] + ^{:key (str "tags" t)} + [:div.control + [:div.tags.has-addons + [:span.tag t]]])]))})) + +(def chips tags-editor) ;;fallback diff --git a/src/cljs/lernmeister/components/bulma/toast.cljs b/src/cljs/lernmeister/components/bulma/toast.cljs new file mode 100644 index 0000000..400e22b --- /dev/null +++ b/src/cljs/lernmeister/components/bulma/toast.cljs @@ -0,0 +1,35 @@ +(ns lernmeister.components.bulma.toast) + + +(defonce toastcounter (atom 0)) +(defonce toastbox-id (str (gensym "toastbox"))) + +(defn get-toastbox [] + (if-let [toastbox (js/document.getElementById toastbox-id)] + toastbox + (let [div (js/document.createElement "div")] + (set! (.-id div) toastbox-id) + (set! (.-classList div) (str "toastbox")) + (js/document.body.appendChild div) + (recur)))) + +(defn toast + [& {:keys [html displayLength classes]}] + (let [div (js/document.createElement "div") + initial-classes (str "notification toast " classes)] + (set! (.-innerHTML div) html) + (set! (.-classList div) initial-classes) + (js/setTimeout #(do + (set! (.-classList div) initial-classes) + ;;;wait css fade-out + (js/setTimeout (fn [] + (.remove div)) 1000)) + (or displayLength 1000)) + (.appendChild (get-toastbox) div) + (set! (.-classList div) (str (.-classList div) " active"))) + + #_(js/M.toast (clj->js (merge {:html html} + (when displayLength + {:displayLength displayLength}) + (when classes + {:classes classes}))))) diff --git a/src/cljs/lernmeister/components/ui.cljs b/src/cljs/lernmeister/components/ui.cljs index 8fda2b7..122df71 100644 --- a/src/cljs/lernmeister/components/ui.cljs +++ b/src/cljs/lernmeister/components/ui.cljs @@ -2,34 +2,40 @@ (:require [lernmeister.components.material-design :as md] [lernmeister.components.bulma.core :as bulma] [lernmeister.components.bulma.table :as bulma-table] - [lernmeister.components.bulma.common :as bulma-common])) + [lernmeister.components.bulma.common :as bulma-common] + [lernmeister.components.bulma.modal :as bulma-modal] + [lernmeister.components.bulma.navbar :as bulma-navbar] + [lernmeister.components.bulma.toast :as bulma-toast] + [lernmeister.components.bulma.card :as bulma-card] + [lernmeister.components.bulma.tag :as bulma-tag] + [lernmeister.components.bulma.datetime :as bulma-datetime] + [lernmeister.components.bulma.select :as bulma-select])) (def icon bulma-common/icon) -(def card-panel bulma/card-panel) +(def card bulma-card/card) +(def card-panel bulma-card/card-panel) (def radiobutton bulma/radiobutton) -(def card bulma/card) (def input-text bulma/input-text) (def input-range bulma/input-range) (def checkbox bulma/checkbox) -(def select* bulma/select*) -(def select bulma/select) -(def chips bulma/chips) -(def tags bulma/tags) -(def tags-editor bulma/tags-editor) -(def navbar bulma/navbar) -(def side-nav bulma/side-nav) -(def toast bulma/toast) -(def init-modal (comp (fn [] (js/console.log "DEPRECATED init-modal")) bulma/init-modal)) -(def open-modal (comp (fn [] (js/console.log "DEPRECATED open-modal")) bulma/open-modal)) -(def close-modal (comp (fn [] (js/console.log "DEPRECATED close-modal")) bulma/close-modal)) -(def modal-exists? (comp (fn [] (js/console.log "DEPRECATED modal-exists?")) bulma/modal-exists?)) -(def modal-panel bulma/modal-panel) +(def select* bulma-select/select*) +(def select bulma-select/select) +(def chips bulma-tag/chips) +(def tags bulma-tag/tags) +(def tags-editor bulma-tag/tags-editor) +(def navbar bulma-navbar/navbar) +(def side-nav bulma-navbar/side-nav) +(def toast bulma-toast/toast) +(def init-modal (comp (fn [] (js/console.log "DEPRECATED init-modal")) bulma-modal/init-modal)) +(def open-modal (comp (fn [] (js/console.log "DEPRECATED open-modal")) bulma-modal/open-modal)) +(def close-modal (comp (fn [] (js/console.log "DEPRECATED close-modal")) bulma-modal/close-modal)) +(def modal-exists? (comp (fn [] (js/console.log "DEPRECATED modal-exists?")) bulma-modal/modal-exists?)) +(def modal-panel bulma-modal/modal-panel) (def tooltip bulma/tooltip) (def table bulma-table/table) (def collapsible bulma/collapsible) (def scrollable-tabs bulma/scrollable-tabs) -(def datepicker bulma/datepicker) -(def timepicker bulma/timepicker) +(def datepicker bulma-datetime/datepicker) +(def timepicker bulma-datetime/timepicker) (def switch bulma/switch) (def indeterminate-progress bulma/indeterminate-progress) -;;; TODO tabs diff --git a/src/devcards/lernmeister/components/core_card.cljs b/src/devcards/lernmeister/components/core_card.cljs index 2ef0a7f..c3b8a12 100644 --- a/src/devcards/lernmeister/components/core_card.cljs +++ b/src/devcards/lernmeister/components/core_card.cljs @@ -4,7 +4,7 @@ [lernmeister.components.first-card] [lernmeister.components.tex-test-card] [lernmeister.components.md-card] - [lernmeister.components.bulma-card] + [lernmeister.components.ui-showcase] [lernmeister.components.core :as core] [lernmeister.components.content-editor-card] [lernmeister.components.ce-shuffle] diff --git a/src/devcards/lernmeister/components/bulma_card.cljs b/src/devcards/lernmeister/components/ui_showcase.cljs similarity index 93% rename from src/devcards/lernmeister/components/bulma_card.cljs rename to src/devcards/lernmeister/components/ui_showcase.cljs index 4769a48..d3ff326 100644 --- a/src/devcards/lernmeister/components/bulma_card.cljs +++ b/src/devcards/lernmeister/components/ui_showcase.cljs @@ -1,4 +1,4 @@ -(ns lernmeister.components.bulma-card +(ns lernmeister.components.ui-showcase (:require-macros [devcards.core :refer [defcard-doc defcard-rg @@ -17,7 +17,7 @@ [devcards.core] [reagent.core :as reagent])) -(defcard "#Bulma Components") +(defcard "#UI Components") (defcard "## Chips") @@ -31,7 +31,7 @@ (defcard-rg chips-card (fn [] - [bulma/chips + [ui/chips {:tags (reagent/cursor data [:tags]) :placeholder "PLATZHALTER" :label "Tags"}])) @@ -41,7 +41,7 @@ (defcard-rg input-datepicker (fn [] [:div - [bulma/datepicker + [ui/datepicker {:label "Startdatum" :placeholder "Datum" :value (if-let [date @dateinput] @@ -70,7 +70,7 @@ (defcard-rg input-datepicker (fn [] [:div - [bulma/timepicker + [ui/timepicker {:label "Startzeit" :placeholder "Zeit" :value (when-let [time @timeinput] @@ -93,7 +93,7 @@ (reagent/with-let [visible? (reagent/atom false)] [:div (if @visible? - [bulma/modal-panel + [ui/modal-panel {:state visible? :title "ABC" :footer [:<> [:button.button {:on-click #(swap! visible? not)} "ok"]] @@ -108,7 +108,7 @@ (defonce textinput (reagent/atom "")) (defcard-rg input-text-card-with-tooltip (fn [] - [bulma/input-text + [ui/input-text {:label "Input" :value @textinput :tooltip {:position "bottom" @@ -118,7 +118,7 @@ (defcard-rg input-text-card-with-icon (fn [] - [bulma/input-text + [ui/input-text {:label-icon "fa-book" :label "Input" :value @textinput @@ -126,7 +126,7 @@ (defcard-rg input-range (fn [] - [bulma/input-range + [ui/input-range {:label-icon "fa-book" :label "Input" :value @(reagent/cursor data [:range]) @@ -135,12 +135,12 @@ (defcard "## Cardpanel") (defcard-rg card-panel-card - [bulma/card-panel + [ui/card-panel [:p.flow-text "Dies ist ein Flow-Text in einer Card"] [:p "Dies ist ein normaler Text"]]) (defcard-rg card-card - [bulma/card + [ui/card {:footer-items [[:a.footer-item "senden"] [:a.footer-item "lll"]]} [:div [:p.flow-text "Dies ist ein Flow-Text in einer Card"] @@ -153,7 +153,7 @@ links [{:panel :home-panel :label "HOME" :route "#/home"} {:panel :info-panel :label "INFO" :icon "star" :route "#/info"}]] [:div - [bulma/navbar + [ui/navbar {:links links :active-link :home-panel :color "green" @@ -161,7 +161,7 @@ :brand-align "right" :side-nav true :side-nav-id sidenav-id}] - [bulma/side-nav + [ui/side-nav :links links :active-link :home-panel :side-nav-id sidenav-id]])) @@ -171,7 +171,7 @@ links [{:panel :home-panel :label "HOME" :route "#/home"} {:panel :info-panel :label "INFO" :icon "star" :route "#/info"}]] [:div - [bulma/navbar + [ui/navbar {:links links :links-position :right :active-link :home-panel @@ -181,7 +181,7 @@ :side-nav-trigger-position :right :side-nav true :side-nav-id sidenav-id}] - [bulma/side-nav + [ui/side-nav {:links links :active-link :home-panel :side-nav-id sidenav-id}]])) @@ -199,7 +199,7 @@ (defcard-rg select (fn [state] [:div - [bulma/select* + [ui/select* {:cursor state :options options :label "Normal Select"}]]) @@ -209,7 +209,7 @@ (defcard-rg select-multiple (fn [state] [:div - [bulma/select* + [ui/select* {:choose-option-label "Wähle ein oder mehrere Elemente 🤩" :cursor state :options options @@ -255,7 +255,7 @@ {:id "12" :title "Tab12" :component [:p "Inhalt12"]}]] - [bulma/scrollable-tabs {:tabs tabs-data}])) + [ui/scrollable-tabs {:tabs tabs-data}])) (defcard-rg scrollable-tabs-without-overflow @@ -271,15 +271,15 @@ {:id "4" :title "Tab4" :component [:p "Inhalt4"]}]] - [bulma/scrollable-tabs {:tabs tabs-data}])) + [ui/scrollable-tabs {:tabs tabs-data}])) (defcard-rg card-panel-card - [bulma/card-panel + [ui/card-panel [:div.content [:a.button {:on-click #(do - (bulma/toast + (ui/toast :html "

TEST

" :displayLength 1000 :classes "is-success" -- GitLab