fix: checkbox type UX

This commit is contained in:
Tienson Qin
2023-07-03 16:15:25 +08:00
parent 9f758a6f47
commit 2b5f07445f
2 changed files with 133 additions and 108 deletions

View File

@@ -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

View File

@@ -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]