From 2b5f07445f172d254d75d79ec0d249af289d0e21 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Mon, 3 Jul 2023 16:15:25 +0800 Subject: [PATCH] fix: checkbox type UX --- src/main/frontend/components/property.cljs | 89 +++++++----- src/main/frontend/handler/property.cljs | 152 +++++++++++---------- 2 files changed, 133 insertions(+), 108 deletions(-) diff --git a/src/main/frontend/components/property.cljs b/src/main/frontend/components/property.cljs index c10f067c61..e2931b7512 100644 --- a/src/main/frontend/components/property.cljs +++ b/src/main/frontend/components/property.cljs @@ -54,12 +54,13 @@ (let [type (keyword (string/lower-case v))] (swap! *property-schema assoc :type type)))))] - [:div.grid.grid-cols-4.gap-1.items-center.leading-8 - [:label.cols-1 "Multiple values:"] - (let [many? (boolean (= :many (:cardinality @*property-schema)))] - (ui/checkbox {:checked many? - :on-change (fn [] - (swap! *property-schema assoc :cardinality (if many? :one :many)))}))] + (when-not (= (:type @*property-schema) :checkbox) + [:div.grid.grid-cols-4.gap-1.items-center.leading-8 + [:label.cols-1 "Multiple values:"] + (let [many? (boolean (= :many (:cardinality @*property-schema)))] + (ui/checkbox {:checked many? + :on-change (fn [] + (swap! *property-schema assoc :cardinality (if many? :one :many)))}))]) [:div (ui/button @@ -167,21 +168,21 @@ (let [*property-key (::property-key state) *property-value (::property-value state)] (cond - new-property? - [:div#edit-new-property - (property-key-input block *property-key *property-value)] + new-property? + [:div#edit-new-property + (property-key-input block *property-key *property-value)] - (seq properties) - [:a {:title "Add another property" - :on-click (fn [] - (property-handler/set-editing-new-property! edit-input-id) - (reset! *property-key nil) - (reset! *property-value nil))} - [:div.block {:style {:height 20 - :width 20}} - [:a.add-button-link.block {:title "Add another value" - :style {:margin-left -4}} - (ui/icon "circle-plus")]]]))) + (seq properties) + [:a {:title "Add another property" + :on-click (fn [] + (property-handler/set-editing-new-property! edit-input-id) + (reset! *property-key nil) + (reset! *property-value nil))} + [:div.block {:style {:height 20 + :width 20}} + [:a.add-button-link.block {:title "Add another value" + :style {:margin-left -4}} + (ui/icon "circle-plus")]]]))) (rum/defcs property-key < (rum/local false ::show-close?) [state block property] @@ -202,6 +203,27 @@ (property-handler/remove-property! repo block (:block/uuid property)))} (ui/icon "x")]])])) +(rum/defc property-scalar-value + [block property value {:keys [editor-on-click inline-text]}] + (case (:type (:block/schema property)) + :date + (ui/button + (or value "Empty") + :icon :calendar) + + :checkbox + (ui/checkbox {:checked value + :on-change (fn [_e] + (let [repo (state/get-current-repo)] + (property-handler/add-property! repo block + (:block/name property) + (boolean (not value)))))}) + ;; :object + ;; default and others + [:div (when editor-on-click {:on-click editor-on-click}) + (when-not (string/blank? (str value)) + (inline-text {} :markdown (str value)))])) + (rum/defcs multiple-value-item < (rum/local false ::show-close?) [state entity property item dom-id' editor-id' {:keys [edit-fn page-cp inline-text]}] (let [*show-close? (::show-close? state) @@ -229,9 +251,9 @@ item))} svg/close])])) -;; (add-property! block *property-key *property-value) (rum/defcs property-value < rum/reactive - [state block property {:keys [inline-text editor-box page-cp]}] + [state block property value + {:keys [inline-text editor-box page-cp]}] (let [k (:block/uuid property) v (get (:block/properties-text-values block) k @@ -288,20 +310,15 @@ (editor-box editor-args editor-id {}) :else - [:div.flex.flex-1.property-value-content - {:id dom-id - :on-click (fn [] - (edit-fn editor-id nil v))} - (cond - (and (= type :date) (string/blank? v)) - [:div "TBD (date icon)"] + [:div.flex.flex-1.items-center.property-value-content + {:id dom-id} + (property-scalar-value block property value + {:editor-on-click (fn [] + (edit-fn editor-id nil v)) + :inline-text inline-text})]))) - :else - (when-not (string/blank? (str v)) - (inline-text {} :markdown (str v))))]))) - -(rum/defc properties-area < rum/reactive - [block properties properties-text-values edit-input-id block-components-m] +(rum/defcs properties-area < rum/reactive + [state block properties properties-text-values edit-input-id block-components-m] (let [repo (state/get-current-repo) new-property? (= edit-input-id (state/sub :ui/new-property-input-id))] (when (or (seq properties) new-property?) @@ -315,7 +332,7 @@ [:div.property-key.col-span-1 (property-key block property)] [:div.property-value.col-span-3 - (property-value block property block-components-m)]]) + (property-value block property v block-components-m)]]) ;; TODO: built in properties should have UUID and corresponding schema ;; builtin [:div diff --git a/src/main/frontend/handler/property.cljs b/src/main/frontend/handler/property.cljs index 713b768b5c..e6a8874886 100644 --- a/src/main/frontend/handler/property.cljs +++ b/src/main/frontend/handler/property.cljs @@ -25,14 +25,14 @@ (not= (str d) "Invalid Date"))) (def builtin-schema-types - {:default string? ; default, might be mixed with refs, tags - :number number? - :date inst? - :boolean boolean? - :url [:fn - {:error/message "should be a URL"} - gp-util/url?] - :object uuid?}) ; TODO: make sure block exists + {:default string? ; default, might be mixed with refs, tags + :number number? + :date inst? + :checkbox boolean? + :url [:fn + {:error/message "should be a URL"} + gp-util/url?] + :object uuid?}) ; TODO: make sure block exists ;; schema -> type, cardinality, object's class ;; min, max -> string length, number range, cardinality size limit @@ -56,90 +56,98 @@ (defn- infer-schema-from-input-string [v-str] - (cond - (parse-long v-str) :number - (parse-double v-str) :number - (util/uuid-string? v-str) :object - (gp-util/url? v-str) :url - (date-str? v-str) :date - (contains? #{"true" "false"} (string/lower-case v-str)) :boolean - :else :default)) + (try + (cond + (parse-long v-str) :number + (parse-double v-str) :number + (util/uuid-string? v-str) :object + (gp-util/url? v-str) :url + (date-str? v-str) :date + (contains? #{"true" "false"} (string/lower-case v-str)) :boolean + :else :default) + (catch :default _e + :default))) (defn convert-property-input-string [schema-type v-str] - (case schema-type - :default - v-str + (if (string? v-str) + (case schema-type + :default + v-str - :number - (edn/read-string v-str) + :number + (edn/read-string v-str) - :boolean - (edn/read-string (string/lower-case v-str)) + :boolean + (edn/read-string (string/lower-case v-str)) - :object - (uuid v-str) + :object + (uuid v-str) - :date - (js/Date. v-str) + :date + (js/Date. v-str) - :url + :url + v-str) v-str)) (defn add-property! [repo block k-name v] - (let [property (db/pull repo '[*] [:block/name k-name]) + (prn :debug " add-property! " {:k k-name + :v v}) + (let [property (db/pull repo '[*] [:block/name (gp-util/page-name-sanity-lc k-name)]) property-uuid (or (:block/uuid property) (random-uuid)) {:keys [type cardinality]} (:block/schema property) multiple-values? (= cardinality :many) - infer-schema (infer-schema-from-input-string v) + infer-schema (when-not type (infer-schema-from-input-string v)) property-type (or type infer-schema :default) schema (get builtin-schema-types property-type) properties (:block/properties block) value (get properties property-uuid)] (when-not (and multiple-values? (string/blank? (str v))) - (when-let [v* (try - (convert-property-input-string property-type v) - (catch :default e - (notification/show! (str e) :error false) - nil))] - (when-not (contains? (if (set? value) value #{value}) v*) - (if-let [msg (me/humanize (mu/explain-data schema v))] - (let [msg' (str "\"" k-name "\"" " " (if (coll? msg) (first msg) msg))] - (notification/show! msg' :warning)) - (do - ;; FIXME: what if the block already have a block/type, e.g. whiteboard? - (when (and property (nil? (:block/type property))) - (db/transact! repo [(outliner-core/block-with-updated-at - {:block/schema {:type property-type} - :block/uuid property-uuid - :block/type "property"})])) - (when (nil? property) ;if property not exists yet - (db/transact! repo [(outliner-core/block-with-timestamps - {:block/schema {:type property-type} - :block/original-name k-name - :block/name (util/page-name-sanity-lc k-name) - :block/uuid property-uuid - :block/type "property"})])) - (let [refs (when (= property-type :default) (extract-page-refs-from-prop-str-value v*)) - refs' (when (seq refs) - (concat (:block/refs (db/pull [:block/uuid (:block/uuid block)])) - refs)) - v' (if (= property-type :default) - (if (seq refs) refs v*) - v*) - new-value (if multiple-values? (vec (distinct (conj value v'))) v') - block-properties (assoc properties property-uuid new-value) - block-properties-text-values - (if (and (not multiple-values?) (= property-type :default)) - (assoc (:block/properties-text-values block) property-uuid v*) - (dissoc (:block/properties-text-values block) property-uuid))] - ;; TODO: fix block/properties-order - (db/transact! repo - [{:block/uuid (:block/uuid block) - :block/properties block-properties - :block/properties-text-values block-properties-text-values - :block/refs refs'}]))))))))) + (let [v* (try + (convert-property-input-string property-type v) + (catch :default e + (notification/show! (str e) :error false) + nil))] + (when (some? v*) + (when-not (contains? (if (set? value) value #{value}) v*) + (if-let [msg (me/humanize (mu/explain-data schema v))] + (let [msg' (str "\"" k-name "\"" " " (if (coll? msg) (first msg) msg))] + (notification/show! msg' :warning)) + (do + ;; FIXME: what if the block already have a block/type, e.g. whiteboard? + (when (and property (nil? (:block/type property))) + (db/transact! repo [(outliner-core/block-with-updated-at + {:block/schema {:type property-type} + :block/uuid property-uuid + :block/type "property"})])) + (when (nil? property) ;if property not exists yet + (db/transact! repo [(outliner-core/block-with-timestamps + {:block/schema {:type property-type} + :block/original-name k-name + :block/name (util/page-name-sanity-lc k-name) + :block/uuid property-uuid + :block/type "property"})])) + (let [refs (when (= property-type :default) (extract-page-refs-from-prop-str-value v*)) + refs' (when (seq refs) + (concat (:block/refs (db/pull [:block/uuid (:block/uuid block)])) + refs)) + v' (if (= property-type :default) + (if (seq refs) refs v*) + v*) + new-value (if multiple-values? (vec (distinct (conj value v'))) v') + block-properties (assoc properties property-uuid new-value) + block-properties-text-values + (if (and (not multiple-values?) (= property-type :default)) + (assoc (:block/properties-text-values block) property-uuid v*) + (dissoc (:block/properties-text-values block) property-uuid))] + ;; TODO: fix block/properties-order + (db/transact! repo + [{:block/uuid (:block/uuid block) + :block/properties block-properties + :block/properties-text-values block-properties-text-values + :block/refs refs'}])))))))))) (defn remove-property! [repo block k-uuid-or-builtin-k-name]