diff --git a/bases/behave_components/resources/public/css/styles.css b/bases/behave_components/resources/public/css/styles.css index 64bc47b45..a3e40a17e 100644 --- a/bases/behave_components/resources/public/css/styles.css +++ b/bases/behave_components/resources/public/css/styles.css @@ -1345,6 +1345,7 @@ p { .input-number__error { color: var(--error); font-size: var(--font-size-10); + margin-top: 3px; } .input-number--disabled .input-number__input, diff --git a/bases/behave_components/src/cljs/behave/components/inputs.cljs b/bases/behave_components/src/cljs/behave/components/inputs.cljs index fa06265e0..17639170a 100644 --- a/bases/behave_components/src/cljs/behave/components/inputs.cljs +++ b/bases/behave_components/src/cljs/behave/components/inputs.cljs @@ -62,7 +62,7 @@ ;;============================================================================== #_{:clj-kondo/ignore [:shadowed-var]} -(defn number-input [{:keys [label id name on-change on-blur disabled? error? error-msg min max value value-atom default-value step]}] +(defn number-input [{:keys [label id name on-change on-blur disabled? error? error-msg min max value value-atom default-value step placeholder]}] [:div {:class ["input-number " (when error? "input-number--error")]} [:label {:class "input-number__label" :for id} @@ -78,6 +78,7 @@ :min min :max max} step (assoc :step step) + placeholder (assoc :placeholder placeholder) value (assoc :value value) value-atom (assoc :value @value-atom) default-value (assoc :default-value default-value))] diff --git a/development/migrations/2026_05_18_set_calculator_variable_min_max.clj b/development/migrations/2026_05_18_set_calculator_variable_min_max.clj new file mode 100644 index 000000000..d342fd034 --- /dev/null +++ b/development/migrations/2026_05_18_set_calculator_variable_min_max.clj @@ -0,0 +1,114 @@ +(ns migrations.2026-05-18-set-calculator-variable-min-max + (:require [behave-cms.server :as cms] + [behave-cms.store :refer [default-conn]] + [datomic.api :as d] + [schema-migrate.interface :as sm])) + +;; =========================================================================================================== +;; Overview +;; =========================================================================================================== +;; Sets :variable/minimum and :variable/maximum for calculator (tool) variables. +;; +;; All values are in native (English) units. The app handles unit conversion +;; for display when the user selects metric units. +;; +;; Probability of Ignition: +;; 1-h Fuel Moisture: 1 - 60 +;; Air Temperature: -40 - 120 (F) +;; Fuel Shading from the Sun: 0 - 100 +;; +;; Vapor Pressure Deficit: +;; Air Temperature: -40 - 120 (F) +;; Relative Humidity: 0 - 100 +;; +;; Relative Humidity: +;; Dry Bulb Temperature: -40 - 120 (F) +;; Wet Bulb Temperature: -40 - 120 (F) +;; Site Elevation: 0 - 10,000 +;; +;; SSD / SZS: +;; Vegetation Height: 0 - 300 (ft) + +;; =========================================================================================================== +;; Initialize +;; =========================================================================================================== + +(cms/init-db!) + +#_{:clj-kondo/ignore [:missing-docstring]} +(def conn (default-conn)) + +;; =========================================================================================================== +;; Payload +;; =========================================================================================================== + +#_{:clj-kondo/ignore [:missing-docstring]} +(def payload + [{:db/id (sm/name->eid conn :variable/name "1-h Fuel Moisture") + :variable/minimum 1.0 + :variable/maximum 60.0} + + {:db/id (sm/name->eid conn :variable/name "Air Temperature") + :variable/minimum -40.0 + :variable/maximum 120.0} + + {:db/id (sm/name->eid conn :variable/name "Fuel Shading from the Sun") + :variable/minimum 0.0 + :variable/maximum 100.0} + + {:db/id (sm/name->eid conn :variable/name "Relative Humidity") + :variable/minimum 0.0 + :variable/maximum 100.0} + + {:db/id (sm/name->eid conn :variable/name "Dry Bulb Temperature") + :variable/minimum -40.0 + :variable/maximum 120.0} + + {:db/id (sm/name->eid conn :variable/name "Wet Bulb Temperature") + :variable/minimum -40.0 + :variable/maximum 120.0} + + {:db/id (sm/name->eid conn :variable/name "Site Elevation") + :variable/minimum 0.0 + :variable/maximum 10000.0} + + {:db/id (sm/name->eid conn :variable/name "Vegetation Height") + :variable/minimum 0.0 + :variable/maximum 300.0}]) + +;; =========================================================================================================== +;; Transact Payload +;; =========================================================================================================== + +(comment + #_{:clj-kondo/ignore [:missing-docstring]} + (try (def tx-data @(d/transact conn payload)) + (catch Exception e (str "caught exception: " (.getMessage e))))) + +;; =========================================================================================================== +;; Verify +;; =========================================================================================================== + +(comment + (let [variable-names ["1-h Fuel Moisture" + "Air Temperature" + "Fuel Shading from the Sun" + "Relative Humidity" + "Dry Bulb Temperature" + "Wet Bulb Temperature" + "Site Elevation" + "Vegetation Height"]] + (doseq [vname variable-names] + (let [eid (sm/name->eid conn :variable/name vname) + e (d/entity (d/db conn) eid)] + (println (format "%-30s min: %-8s max: %s" + vname + (:variable/minimum e) + (:variable/maximum e))))))) + +;; =========================================================================================================== +;; In case we need to rollback. +;; =========================================================================================================== + +(comment + (sm/rollback-tx! conn tx-data)) diff --git a/projects/behave/resources/public/css/app-style.css b/projects/behave/resources/public/css/app-style.css index 62c22d5b9..7cca245d7 100644 --- a/projects/behave/resources/public/css/app-style.css +++ b/projects/behave/resources/public/css/app-style.css @@ -1223,8 +1223,9 @@ body { .tool-output, .tool-input { - display: flex; - align-items: center; + display: grid; + grid-template-columns: auto 1fr; + align-items: start; min-height: 40px; padding-bottom: 5px; } @@ -1236,12 +1237,12 @@ body { flex-direction: row; } + .tool-output .input-text__label, .tool-input .input-radio-group__label, .tool-input__input > .input-dropdown > .input-dropdown__label, .tool-input .input-number__label { min-width: 250px; - padding: 0px; } .tool-input .input-dropdown__select-wrapper { @@ -1250,12 +1251,18 @@ body { .tool-input .input-number__label, .tool-input .input-text__label, -.tool-input .input-dropdown__label, +.tool-input .input-dropdown__label { + padding: 7px 10px 0px 0px; + font-size: var(--font-size-15); + font-weight: 400; + color: var(--gray-5); +} + .tool-input .input-radio-group__label { padding: 0px 10px 0px 0px; - font-size: var(--font-size-14); + font-size: var(--font-size-15); font-weight: 400; - color: var(--gray-6); + color: var(--gray-5); } .tool-input + .tool-input:last-child { @@ -1302,8 +1309,13 @@ body { background-color: var(--white); } +.tool-input .wizard-input__units { + padding-top: 12px; +} + .tool__units-system-selector { padding-left: 5px; + margin-bottom: 15px; } .tool__units-system-selector .input-toggle__label { diff --git a/projects/behave/src/cljs/behave/tool/subs.cljs b/projects/behave/src/cljs/behave/tool/subs.cljs index 9f9568474..57b9f9d2d 100644 --- a/projects/behave/src/cljs/behave/tool/subs.cljs +++ b/projects/behave/src/cljs/behave/tool/subs.cljs @@ -1,9 +1,60 @@ (ns behave.tool.subs - (:require [behave.vms.store :as s] - [clojure.set :refer [rename-keys]] + (:require [behave.lib.units :refer [convert]] [behave.translate :refer [> units (map (fn [unit] (cond-> - {:label (:unit/name unit) - :value (:bp/uuid unit)} + {:label (:unit/name unit) + :value (:bp/uuid unit)} (= @*unit-uuid (:bp/uuid unit)) (assoc :selected? true)))) (sort-by :label))}]])) @@ -123,6 +123,8 @@ (let [{sv-uuid :bp/uuid domain-uuid :variable/domain-uuid var-name :variable/name + var-min :variable/minimum + var-max :variable/maximum dimension-uuid :variable/dimension-uuid native-unit-uuid :variable/native-unit-uuid english-unit-uuid :variable/english-unit-uuid @@ -131,16 +133,35 @@ help-key :subtool-variable/help-key} variable translated-name @(rf/subscribe [:tool/sv->translated-name sv-uuid]) domain @(rf/subscribe [:vms/entity-from-uuid domain-uuid]) + resolved-native-uuid (or (:domain/native-unit-uuid domain) native-unit-uuid) + resolved-english-uuid (or (:domain/english-unit-uuid domain) english-unit-uuid) + resolved-metric-uuid (or (:domain/metric-unit-uuid domain) metric-unit-uuid) unit-uuid @(rf/subscribe [:tool/input-units tool-uuid subtool-uuid sv-uuid]) + units-system (rf/subscribe [:settings/tool-units-system]) + effective-unit-uuid (or unit-uuid + (case @units-system + :english resolved-english-uuid + :metric resolved-metric-uuid + resolved-native-uuid)) value @(rf/subscribe [:tool/input-value tool-uuid subtool-uuid sv-uuid]) - value-atom (r/atom value)] + value-atom (r/atom value) + error-msg @(rf/subscribe [:tool/input-error-msg + tool-uuid subtool-uuid sv-uuid + resolved-native-uuid var-min var-max + effective-unit-uuid value]) + range-placeholder @(rf/subscribe [:tool/input-range-placeholder + resolved-native-uuid var-min var-max + effective-unit-uuid])] [:div.tool-input [:div.tool-input__input {:on-mouse-over #(rf/dispatch [:help/highlight-section help-key])} - [c/number-input {:id sv-uuid - :label (or translated-name var-name) - :value-atom value-atom - :required? true + [c/number-input {:id sv-uuid + :label (or translated-name var-name) + :value-atom value-atom + :required? true + :placeholder range-placeholder + :error? (some? error-msg) + :error-msg error-msg :on-change #(reset! value-atom (input-value %)) :on-blur #(rf/dispatch [:tool/upsert-input-value tool-uuid @@ -268,13 +289,13 @@ (defmethod tool-output :discrete [{:keys [variable tool-uuid subtool-uuid]}] - (let [{sv-uuid :bp/uuid - var-name :variable/name - list :variable/list - help-key :subtool-variable/help-key} variable - translated-name @(rf/subscribe [:tool/sv->translated-name sv-uuid]) - value @(rf/subscribe [:tool/output-value tool-uuid subtool-uuid sv-uuid]) - list-options (index-by :list-option/value (:list/options list)) + (let [{sv-uuid :bp/uuid + var-name :variable/name + vlist :variable/list + help-key :subtool-variable/help-key} variable + translated-name @(rf/subscribe [:tool/sv->translated-name sv-uuid]) + value @(rf/subscribe [:tool/output-value tool-uuid subtool-uuid sv-uuid]) + list-options (index-by :list-option/value (:list/options vlist)) matching-option (get list-options (str value)) background (get-in matching-option [:list-option/color-tag-ref :tag/color] "#FFFFFF") lum-bg (apply luminance (hex-to-rgb background)) @@ -296,28 +317,33 @@ (defn- auto-compute-subtool "Renders a subtool that automatically computes outputs as inputs change." [tool-uuid subtool-uuid] - (rf/dispatch [:tool/solve tool-uuid subtool-uuid]) - (let [variables @(rf/subscribe [:subtool/encriched-subtool-variables subtool-uuid])] - [:div - (for [{io :subtool-variable/io :as variable} variables - :let [params {:variable variable - :tool-uuid tool-uuid - :subtool-uuid subtool-uuid - :auto-compute? true}]] - (if (= io :input) - [tool-input params] - [tool-output params])) - [c/button {:label @(