(ns frontend.components.plugins-settings (:require [rum.core :as rum] [frontend.util :as util] [frontend.ui :as ui] [frontend.handler.plugin :as plugin-handler] [cljs-bean.core :as bean] [goog.functions :refer [debounce]])) (defn- dom-purify [html opts] (try (js-invoke js/DOMPurify "sanitize" html (bean/->js opts)) (catch js/Error e (js/console.warn e) html))) (rum/defc html-content [html] [:div.html-content.pl-1.flex-1.text-sm {:dangerouslySetInnerHTML {:__html (dom-purify html nil)}}]) (rum/defc edit-settings-file [pid {:keys [class]}] [:a.text-sm.hover:underline {:class class :on-click #(plugin-handler/open-settings-file-in-default-app! pid)} "Edit settings.json"]) (rum/defc render-item-input [val {:keys [key type title default description inputAs]} update-setting!] [:div.desc-item.as-input {:data-key key :key key} [:h2 [:code key] (ui/icon "caret-right") [:strong title]] [:label.form-control (html-content description) (let [input-as (util/safe-lower-case (or inputAs (name type))) input-as (if (= input-as "string") :text (keyword input-as))] [(if (= input-as :textarea) :textarea :input) {:class (util/classnames [{:form-input (not (contains? #{:color :range} input-as))}]) :type (name input-as) :defaultValue (or val default) :on-key-down #(.stopPropagation %) :on-change (debounce #(update-setting! key (util/evalue %)) 1000)}])]]) (rum/defc render-item-toggle [val {:keys [key title description default]} update-setting!] (let [val (if (boolean? val) val (boolean default))] [:div.desc-item.as-toggle {:data-key key} [:h2 [:code key] (ui/icon "caret-right") [:strong title]] [:label.form-control (ui/checkbox {:checked val :on-change #(update-setting! key (not val))}) (html-content description)]])) (rum/defc render-item-enum [val {:keys [key title description default enumChoices enumPicker]} update-setting!] (let [val (or val default) vals (into #{} (if (sequential? val) val [val])) options (map (fn [v] {:label v :value v :selected (contains? vals v)}) enumChoices) picker (keyword enumPicker)] [:div.desc-item.as-enum {:data-key key} [:h2 [:code key] (ui/icon "caret-right") [:strong title]] [:div.form-control [(if (contains? #{:radio :checkbox} picker) :div.wrap :label.wrap) (html-content description) (case picker :radio (ui/radio-list options #(update-setting! key %) nil) :checkbox (ui/checkbox-list options #(update-setting! key %) nil) ;; select (ui/select options (fn [_ value ] (update-setting! key value)))) ]]])) (rum/defc render-item-object [_val {:keys [key title description _default]} pid] [:div.desc-item.as-object {:data-key key} [:h2 [:code key] (ui/icon "caret-right") [:strong title]] [:div.form-control (html-content description) [:div.pl-1 (edit-settings-file pid nil)]]]) (rum/defc render-item-heading [{:keys [key title description]}] [:div.heading-item {:data-key key} [:h2 title] (html-content description)]) (rum/defc render-item-not-handled [s] [:p.text-red-500 (str "#Not Handled# " s)]) (rum/defc settings-container [schema ^js pl] (let [^js plugin-settings (.-settings pl) pid (.-id pl) [settings, set-settings] (rum/use-state (bean/->clj (.toJSON plugin-settings))) update-setting! (fn [k v] (.set plugin-settings (name k) (bean/->js v)))] (rum/use-effect! (fn [] (let [on-change (fn [^js s] (when-let [s (bean/->clj s)] (set-settings s)))] (.on plugin-settings "change" on-change) #(.off plugin-settings "change" on-change))) [pid]) (if (seq schema) [:<> [:h2.text-xl.px-2.pt-1.opacity-90 "ID: " pid] [:div.cp__plugins-settings-inner ;; settings.json [:span.edit-file (edit-settings-file pid nil)] ;; render items (for [desc schema :let [key (:key desc) val (get settings (keyword key)) type (keyword (:type desc)) desc (update desc :description #(plugin-handler/markdown-to-html %))]] (rum/with-key (condp contains? type #{:string :number} (render-item-input val desc update-setting!) #{:boolean} (render-item-toggle val desc update-setting!) #{:enum} (render-item-enum val desc update-setting!) #{:object} (render-item-object val desc pid) #{:heading} (render-item-heading desc) (render-item-not-handled key)) key))]] ;; no settings [:h2.font-bold.text-lg.py-4.warning "No Settings Schema!"])))