Browse Source

enhance(ux): months/years navigation for shui datepicker

charlie 1 year ago
parent
commit
86722711f3
2 changed files with 377 additions and 349 deletions
  1. 18 2
      resources/css/shui.css
  2. 359 347
      src/main/frontend/components/property/value.cljs

+ 18 - 2
resources/css/shui.css

@@ -369,6 +369,18 @@ div[data-radix-popper-content-wrapper] {
         @apply relative;
       }
     }
+
+    .months-years-nav {
+      @apply flex items-center relative left-2 opacity-80;
+
+      select {
+        @apply px-2 py-0 border-none;
+      }
+
+      input {
+        @apply border-none ml-0.5;
+      }
+    }
   }
 
   .rdp-caption_start {
@@ -378,8 +390,8 @@ div[data-radix-popper-content-wrapper] {
   }
 
   .del-date-btn {
-    @apply absolute right-2 top-4 px-1 opacity-70 hover:opacity-100
-    active:opacity-80 hover:text-red-rx-09;
+    @apply absolute right-[9px] top-[19px] px-1 opacity-70 hover:opacity-100
+    active:opacity-80 hover:text-red-rx-09 rounded-md;
   }
 
   &.has-del-btn {
@@ -389,6 +401,10 @@ div[data-radix-popper-content-wrapper] {
       }
     }
   }
+
+  input[type=number] {
+    appearance: initial;
+  }
 }
 
 .popper-arrow {

+ 359 - 347
src/main/frontend/components/property/value.cljs

@@ -26,6 +26,7 @@
             [frontend.handler.property.util :as pu]
             [logseq.db.frontend.property.type :as db-property-type]
             [dommy.core :as d]
+            [cljs-bean.core :as bean]
             [frontend.search :as search]
             [goog.functions :refer [debounce]]))
 
@@ -43,62 +44,62 @@
   (let [icon-value (:logseq.property/icon block)]
     [:div.col-span-3.flex.flex-row.items-center.gap-2
      (icon-component/icon-picker icon-value
-                                 {:disabled? config/publishing?
-                                  :initial-open? editing?
-                                  :on-chosen (fn [_e icon]
-                                               (p/do!
-                                                (db-property-handler/set-block-property!
-                                                 (:db/id block)
-                                                 :logseq.property/icon
-                                                 (select-keys icon [:type :id :color]))
-                                                (shui/popup-hide!)
-                                                (shui/dialog-close!)))})
+       {:disabled? config/publishing?
+        :initial-open? editing?
+        :on-chosen (fn [_e icon]
+                     (p/do!
+                       (db-property-handler/set-block-property!
+                         (:db/id block)
+                         :logseq.property/icon
+                         (select-keys icon [:type :id :color]))
+                       (shui/popup-hide!)
+                       (shui/dialog-close!)))})
      (when (and icon-value (not config/publishing?))
        [:a.fade-link.flex {:on-click (fn [_e]
                                        (p/do!
-                                        (db-property-handler/remove-block-property!
-                                         (:db/id block)
-                                         :logseq.property/icon)
-                                        (shui/popup-hide!)
-                                        (shui/dialog-close!)))
+                                         (db-property-handler/remove-block-property!
+                                           (:db/id block)
+                                           :logseq.property/icon)
+                                         (shui/popup-hide!)
+                                         (shui/dialog-close!)))
                            :title "Delete this icon"}
         (ui/icon "X")])]))
 
 (defn- select-type?
   [property type]
   (or (contains? #{:node :number :url :date} type)
-      ;; closed values
-      (seq (:property/closed-values property))))
+    ;; closed values
+    (seq (:property/closed-values property))))
 
 (defn <create-new-block!
   [block property value & {:keys [edit-block?]
                            :or {edit-block? true}}]
   (p/let [block
           (if (and (= :default (get-in property [:block/schema :type]))
-                   (not (db-property/many? property)))
+                (not (db-property/many? property)))
             (p/let [existing-value (get block (:db/ident property))
                     existing-value? (and (some? existing-value)
-                                         (not= (:db/ident existing-value) :logseq.property/empty-placeholder))
+                                      (not= (:db/ident existing-value) :logseq.property/empty-placeholder))
                     new-block-id (when-not existing-value? (db/new-block-id))
                     _ (when-not existing-value?
                         (db-property-handler/create-property-text-block!
-                         (:db/id block)
-                         (:db/id property)
-                         value
-                         {:new-block-id new-block-id}))]
+                          (:db/id block)
+                          (:db/id property)
+                          value
+                          {:new-block-id new-block-id}))]
               (if existing-value? existing-value (db/entity [:block/uuid new-block-id])))
             (p/let [new-block-id (db/new-block-id)
                     _ (db-property-handler/create-property-text-block!
-                       (:db/id block)
-                       (:db/id property)
-                       value
-                       {:new-block-id new-block-id})]
+                        (:db/id block)
+                        (:db/id property)
+                        value
+                        {:new-block-id new-block-id})]
               (db/entity [:block/uuid new-block-id])))]
     (when edit-block?
       (p/do!
-       (editor-handler/edit-block! block :max {:container-id :unknown-container})
-       (shui/popup-hide!)
-       (shui/dialog-close!)))
+        (editor-handler/edit-block! block :max {:container-id :unknown-container})
+        (shui/popup-hide!)
+        (shui/dialog-close!)))
     block))
 
 (defn <add-property!
@@ -114,41 +115,51 @@
          checkbox? (= :checkbox (get-in property [:block/schema :type]))]
      (assert (qualified-keyword? property-id) "property to add must be a keyword")
      (p/do!
-      (if (and class? class-schema?)
-        (db-property-handler/class-add-property! (:db/id block) property-id)
-        (if (and (db-property-type/ref-property-types (get-in property [:block/schema :type]))
-                 (string? property-value'))
-          (<create-new-block! block (db/entity property-id) property-value' {:edit-block? false})
-          (property-handler/set-block-property! repo (:block/uuid block) property-id property-value')))
-      (when exit-edit?
-        (ui/hide-popups-until-preview-popup!)
-        (shui/dialog-close!))
-      (when-not (or many? checkbox?)
-        (when-let [input (state/get-input)]
-          (.focus input)))
-      (when checkbox?
-        (state/set-editor-action-data! {:type :focus-property-value
-                                        :property property}))))))
+       (if (and class? class-schema?)
+         (db-property-handler/class-add-property! (:db/id block) property-id)
+         (if (and (db-property-type/ref-property-types (get-in property [:block/schema :type]))
+               (string? property-value'))
+           (<create-new-block! block (db/entity property-id) property-value' {:edit-block? false})
+           (property-handler/set-block-property! repo (:block/uuid block) property-id property-value')))
+       (when exit-edit?
+         (ui/hide-popups-until-preview-popup!)
+         (shui/dialog-close!))
+       (when-not (or many? checkbox?)
+         (when-let [input (state/get-input)]
+           (.focus input)))
+       (when checkbox?
+         (state/set-editor-action-data! {:type :focus-property-value
+                                         :property property}))))))
 
 (defn- add-or-remove-property-value
   [block property value selected? {:keys [refresh-result-f]}]
   (let [many? (db-property/many? property)]
     (p/do!
-     (if selected?
-       (<add-property! block (:db/ident property) value {:exit-edit? (not many?)})
-       (p/do!
-        (db-property-handler/delete-property-value! (:db/id block) (:db/ident property) value)
-        (when (or (not many?)
-                 ;; values will be cleared
+      (if selected?
+        (<add-property! block (:db/ident property) value {:exit-edit? (not many?)})
+        (p/do!
+          (db-property-handler/delete-property-value! (:db/id block) (:db/ident property) value)
+          (when (or (not many?)
+                  ;; values will be cleared
                   (and many? (<= (count (get block (:db/ident property))) 1)))
-          (shui/popup-hide!))))
-     (when (fn? refresh-result-f) (refresh-result-f)))))
+            (shui/popup-hide!))))
+      (when (fn? refresh-result-f) (refresh-result-f)))))
 
 (rum/defc DelDateButton
   [on-delete]
   (shui/button {:variant :outline :size :sm :class "del-date-btn" :on-click on-delete}
     (shui/tabler-icon "trash")))
 
+(rum/defc DateNavSelect
+  [{:keys [name value onChange children]}]
+  [:div.months-years-nav
+   (if (= name "years")
+     [:label [:input.py-0.px-1.w-auto
+              {:type "number" :default-value value :on-change onChange :min 1 :max 9999}]]
+
+     ;; months
+     [:select children])])
+
 (rum/defcs calendar-inner <
   (rum/local (str "calendar-inner-" (js/Date.now)) ::identity)
   {:init (fn [state]
@@ -156,10 +167,10 @@
            state)
    :will-mount (fn [state]
                  (js/setTimeout
-                  #(some-> @(::identity state)
-                           (js/document.getElementById)
-                           (.querySelector "[aria-selected=true]")
-                           (.focus)) 0)
+                   #(some-> @(::identity state)
+                      (js/document.getElementById)
+                      (.querySelector "[aria-selected=true]")
+                      (.focus)) 16)
                  state)
    :will-unmount (fn [state]
                    (shui/dialog-close!)
@@ -177,22 +188,23 @@
             (let [gd (goog.date.Date. (.getFullYear d) (.getMonth d) (.getDate d))]
               (let [journal (date/js-date->journal-title gd)]
                 (p/do!
-                 (when-not (db/get-case-page journal)
-                   (page-handler/<create! journal {:redirect? false
-                                                   :create-first-block? false}))
-                 (when (fn? on-change)
-                   (on-change (db/get-case-page journal)))
-                 (shui/popup-hide! id)
-                 (ui/hide-popups-until-preview-popup!)
-                 (shui/dialog-close!))))))]
+                  (when-not (db/get-case-page journal)
+                    (page-handler/<create! journal {:redirect? false
+                                                    :create-first-block? false}))
+                  (when (fn? on-change)
+                    (on-change (db/get-case-page journal)))
+                  (shui/popup-hide! id)
+                  (ui/hide-popups-until-preview-popup!)
+                  (shui/dialog-close!))))))]
     (shui/calendar
       (cond->
         {:mode "single"
          :initial-focus true
          :caption-layout "dropdown-buttons"
-         :fromYear 1900
-         :toYear 2099
-         :components (when del-btn? {:Head #(DelDateButton on-delete)})
+         :fromYear 2019
+         :toYear 2024
+         :components (cond-> {:Dropdown #(DateNavSelect (bean/bean %))}
+                       del-btn? (assoc :Head #(DelDateButton on-delete)))
          :selected initial-day
          :id @*ident
          :class-names {:months "" :root (when del-btn? "has-del-btn")}
@@ -218,38 +230,38 @@
                         (util/stop e)
                         (when-not config/publishing?
                           (shui/popup-show! (.-target e) content-fn
-                                            {:align "start" :auto-focus? true}))))]
+                            {:align "start" :auto-focus? true}))))]
     (rum/use-effect!
-     (fn []
-       (when editing?
-         (js/setTimeout
-           #(some-> (rum/deref *trigger-ref)
-              (.click)) 32)))
-     [editing?])
+      (fn []
+        (when editing?
+          (js/setTimeout
+            #(some-> (rum/deref *trigger-ref)
+               (.click)) 32)))
+      [editing?])
 
     (if multiple-values?
       (shui/button
-       {:class "jtrigger h-6 empty-btn"
-        :ref *trigger-ref
-        :variant :text
-        :size :sm
-        :on-click open-popup!}
-       (ui/icon "calendar-plus" {:size 16}))
+        {:class "jtrigger h-6 empty-btn"
+         :ref *trigger-ref
+         :variant :text
+         :size :sm
+         :on-click open-popup!}
+        (ui/icon "calendar-plus" {:size 16}))
       (shui/trigger-as
-       :div.flex.flex-1.flex-row.gap-1.items-center.flex-wrap
-       {:tabIndex 0
-        :class "jtrigger min-h-[24px]"  ; FIXME: min-h-6 not works
-        :ref *trigger-ref
-        :on-click open-popup!}
-       (if page
-         (when-let [page-cp (state/get-component :block/page-cp)]
-           (rum/with-key
-             (page-cp {:disable-preview? true
-                       :hide-close-button? true
-                       :meta-click? other-position?} page)
-             (:db/id page)))
-         (when-not multiple-values?
-           (property-empty-btn-value)))))))
+        :div.flex.flex-1.flex-row.gap-1.items-center.flex-wrap
+        {:tabIndex 0
+         :class "jtrigger min-h-[24px]"                     ; FIXME: min-h-6 not works
+         :ref *trigger-ref
+         :on-click open-popup!}
+        (if page
+          (when-let [page-cp (state/get-component :block/page-cp)]
+            (rum/with-key
+              (page-cp {:disable-preview? true
+                        :hide-close-button? true
+                        :meta-click? other-position?} page)
+              (:db/id page)))
+          (when-not multiple-values?
+            (property-empty-btn-value)))))))
 
 (rum/defc property-value-date-picker
   [block property value opts]
@@ -274,7 +286,7 @@
         ;; inline-class is only for input from :transform-fn
         [page inline-class] (if (and (seq classes) (not (contains? db-property/db-attribute-properties (:db/ident property))))
                               (or (seq (map string/trim (rest (re-find #"(.*)#(.*)$" page*))))
-                                  [page* nil])
+                                [page* nil])
                               [page* nil])
         page-entity (ldb/get-case-page (db/get-db) page)
         id (:db/id page-entity)
@@ -287,8 +299,8 @@
       (let [inline-class-uuid
             (when inline-class
               (or (:block/uuid (ldb/get-case-page (db/get-db) inline-class))
-                  (do (log/error :msg "Given inline class does not exist" :inline-class inline-class)
-                      nil)))
+                (do (log/error :msg "Given inline class does not exist" :inline-class inline-class)
+                  nil)))
             create-options {:redirect? false
                             :create-first-block? false
                             :tags (if inline-class-uuid
@@ -311,34 +323,34 @@
 (defn- select-aux
   [block property {:keys [items selected-choices multiple-choices?] :as opts}]
   (let [selected-choices (->> selected-choices
-                              (remove nil?)
-                              (remove #(= :logseq.property/empty-placeholder %)))
+                           (remove nil?)
+                           (remove #(= :logseq.property/empty-placeholder %)))
         clear-value (str "No " (:block/title property))
         clear-value-label [:div.flex.flex-row.items-center.gap-2
                            (ui/icon "x")
                            [:div clear-value]]
         items' (->>
-                (if (and (seq selected-choices) (not multiple-choices?))
-                  (concat items
-                          [{:value clear-value
-                            :label clear-value-label
-                            :clear? true}])
-                  items)
-                (remove #(= :logseq.property/empty-placeholder (:value %))))
+                 (if (and (seq selected-choices) (not multiple-choices?))
+                   (concat items
+                     [{:value clear-value
+                       :label clear-value-label
+                       :clear? true}])
+                   items)
+                 (remove #(= :logseq.property/empty-placeholder (:value %))))
         k :on-chosen
         f (get opts k)
         f' (fn [chosen selected?]
              (if (or (and (not multiple-choices?) (= chosen clear-value))
-                     (and multiple-choices? (= chosen [clear-value])))
+                   (and multiple-choices? (= chosen [clear-value])))
                (p/do!
-                (property-handler/remove-block-property! (state/get-current-repo) (:block/uuid block)
-                                                         (:db/ident property))
-                (shui/popup-hide!))
+                 (property-handler/remove-block-property! (state/get-current-repo) (:block/uuid block)
+                   (:db/ident property))
+                 (shui/popup-hide!))
                (f chosen selected?)))]
     (select/select (assoc opts
-                          :selected-choices selected-choices
-                          :items items'
-                          k f'))
+                     :selected-choices selected-choices
+                     :items items'
+                     k f'))
     ;(shui/multi-select-content
     ;  (map #(let [{:keys [value label]} %]
     ;          {:id value :value label}) items') nil opts)
@@ -357,7 +369,7 @@
     "letter-n"))
 
 (rum/defcs ^:large-vars/cleanup-todo select-node < rum/reactive db-mixins/query
-  (rum/local 0 ::refresh-count)
+                                                   (rum/local 0 ::refresh-count)
   [state property
    {:keys [block multiple-choices? dropdown? input-opts on-input] :as opts}
    *result]
@@ -377,28 +389,28 @@
                                [(:db/id v)])))
         nodes
         (->>
-         (cond
-           (seq classes)
-           (mapcat
-            (fn [class]
-              (if (= :logseq.class/Root (:db/ident class))
-                (model/get-all-classes repo {:except-root-class? true})
-                (model/get-class-objects repo (:db/id class))))
-            classes)
-
-           :else
-           (let [result (rum/react *result)]
-             (if (empty? result)
-               (let [v (get block (:db/ident property))]
-                 (remove #(= :logseq.property/empty-placeholder (:db/ident %))
-                         (if (every? de/entity? v) v [v])))
-               (remove (fn [node]
-                         (or (= (:db/id block) (:db/id node))
-                              ;; A page's alias can't be itself
-                             (and alias? (= (or (:db/id (:block/page block))
-                                                (:db/id block))
-                                            (:db/id node)))))
-                       result)))))
+          (cond
+            (seq classes)
+            (mapcat
+              (fn [class]
+                (if (= :logseq.class/Root (:db/ident class))
+                  (model/get-all-classes repo {:except-root-class? true})
+                  (model/get-class-objects repo (:db/id class))))
+              classes)
+
+            :else
+            (let [result (rum/react *result)]
+              (if (empty? result)
+                (let [v (get block (:db/ident property))]
+                  (remove #(= :logseq.property/empty-placeholder (:db/ident %))
+                    (if (every? de/entity? v) v [v])))
+                (remove (fn [node]
+                          (or (= (:db/id block) (:db/id node))
+                            ;; A page's alias can't be itself
+                            (and alias? (= (or (:db/id (:block/page block))
+                                             (:db/id block))
+                                          (:db/id node)))))
+                  result)))))
         options (map (fn [node]
                        (let [id (or (:value node) (:db/id node))
                              label (if (integer? id)
@@ -411,60 +423,60 @@
                                         [:div title]])
                                      (or (:label node) (:block/title node)))]
                          (assoc node
-                                :label-value (:block/title node)
-                                :label label
-                                :value id))) nodes)
+                           :label-value (:block/title node)
+                           :label label
+                           :value id))) nodes)
         classes' (remove (fn [class] (= :logseq.class/Root (:db/ident class))) classes)
         opts' (cond->
-               (merge
-                opts
-                {:multiple-choices? multiple-choices?
-                 :items options
-                 :selected-choices selected-choices
-                 :dropdown? dropdown?
-                 :input-default-placeholder (cond
-                                              tags?
-                                              "Set tags"
-                                              alias?
-                                              "Set alias"
-                                              multiple-choices?
-                                              "Choose nodes"
-                                              :else
-                                              "Choose node")
-                 :show-new-when-not-exact-match? true
-                 :extract-chosen-fn :value
-                 :extract-fn (fn [x] (or (:label-value x) (:label x)))
-                 :input-opts input-opts
-                 :on-input (debounce on-input 50)
-                 :on-chosen (fn [chosen selected?]
-                              (p/let [[id new?] (if (integer? chosen)
-                                                  [chosen false]
-                                                  (when-not (string/blank? (string/trim chosen))
-                                                    (p/let [result (<create-page-if-not-exists! property classes' chosen)]
-                                                      [result true])))
-                                      _ (when (and (integer? id) (not (ldb/page? (db/entity id))))
-                                          (db-async/<get-block repo id))]
-                                (p/do!
-                                 (if id
-                                   (add-or-remove-property-value block property id selected? {})
-                                   (log/error :msg "No :db/id found or created for chosen" :chosen chosen))
-                                 (when new? (swap! *refresh-count inc)))))})
+                (merge
+                  opts
+                  {:multiple-choices? multiple-choices?
+                   :items options
+                   :selected-choices selected-choices
+                   :dropdown? dropdown?
+                   :input-default-placeholder (cond
+                                                tags?
+                                                "Set tags"
+                                                alias?
+                                                "Set alias"
+                                                multiple-choices?
+                                                "Choose nodes"
+                                                :else
+                                                "Choose node")
+                   :show-new-when-not-exact-match? true
+                   :extract-chosen-fn :value
+                   :extract-fn (fn [x] (or (:label-value x) (:label x)))
+                   :input-opts input-opts
+                   :on-input (debounce on-input 50)
+                   :on-chosen (fn [chosen selected?]
+                                (p/let [[id new?] (if (integer? chosen)
+                                                    [chosen false]
+                                                    (when-not (string/blank? (string/trim chosen))
+                                                      (p/let [result (<create-page-if-not-exists! property classes' chosen)]
+                                                        [result true])))
+                                        _ (when (and (integer? id) (not (ldb/page? (db/entity id))))
+                                            (db-async/<get-block repo id))]
+                                  (p/do!
+                                    (if id
+                                      (add-or-remove-property-value block property id selected? {})
+                                      (log/error :msg "No :db/id found or created for chosen" :chosen chosen))
+                                    (when new? (swap! *refresh-count inc)))))})
 
                 (and (seq classes') (not tags-or-alias?))
                 (assoc
-                 ;; Provides additional completion for inline classes on new pages or objects
-                 :transform-fn (fn [results input]
-                                 (if-let [[_ new-page class-input] (and (empty? results) (re-find #"(.*)#(.*)$" input))]
-                                   (let [repo (state/get-current-repo)
-                                         descendent-classes (->> classes'
-                                                                 (mapcat #(model/get-class-children repo (:db/id %)))
-                                                                 (map #(db/entity repo %)))]
-                                     (->> (concat classes' descendent-classes)
-                                          (filter #(string/includes? (:block/title %) class-input))
-                                          (mapv (fn [p]
-                                                  {:value (str new-page "#" (:block/title p))
-                                                   :label (str new-page "#" (:block/title p))}))))
-                                   results))))]
+                  ;; Provides additional completion for inline classes on new pages or objects
+                  :transform-fn (fn [results input]
+                                  (if-let [[_ new-page class-input] (and (empty? results) (re-find #"(.*)#(.*)$" input))]
+                                    (let [repo (state/get-current-repo)
+                                          descendent-classes (->> classes'
+                                                               (mapcat #(model/get-class-children repo (:db/id %)))
+                                                               (map #(db/entity repo %)))]
+                                      (->> (concat classes' descendent-classes)
+                                        (filter #(string/includes? (:block/title %) class-input))
+                                        (mapv (fn [p]
+                                                {:value (str new-page "#" (:block/title p))
+                                                 :label (str new-page "#" (:block/title p))}))))
+                                    results))))]
     (select-aux block property opts')))
 
 (rum/defcs property-value-select-node <
@@ -484,27 +496,27 @@
                           nil))})
 
         opts' (assoc opts
-                     :block block
-                     :input-opts input-opts
-                     :on-input (fn [v]
-                                 (if (string/blank? v)
-                                   (reset! *result nil)
-                                   (p/let [result (search/block-search (state/get-current-repo) v {:enable-snippet? false
-                                                                                                   :built-in? false})]
-                                     (reset! *result result)))))]
+                :block block
+                :input-opts input-opts
+                :on-input (fn [v]
+                            (if (string/blank? v)
+                              (reset! *result nil)
+                              (p/let [result (search/block-search (state/get-current-repo) v {:enable-snippet? false
+                                                                                              :built-in? false})]
+                                (reset! *result result)))))]
     (select-node property opts' *result)))
 
 (rum/defcs select < rum/reactive db-mixins/query
-  {:init (fn [state]
-           (let [*values (atom :loading)
-                 refresh-result-f (fn []
-                                    (p/let [result (db-async/<get-block-property-values (state/get-current-repo)
-                                                                                        (:db/ident (nth (:rum/args state) 1)))]
-                                      (reset! *values result)))]
-             (refresh-result-f)
-             (assoc state
-                    ::values *values
-                    ::refresh-result-f refresh-result-f)))}
+                    {:init (fn [state]
+                             (let [*values (atom :loading)
+                                   refresh-result-f (fn []
+                                                      (p/let [result (db-async/<get-block-property-values (state/get-current-repo)
+                                                                       (:db/ident (nth (:rum/args state) 1)))]
+                                                        (reset! *values result)))]
+                               (refresh-result-f)
+                               (assoc state
+                                 ::values *values
+                                 ::refresh-result-f refresh-result-f)))}
   [state block property
    {:keys [multiple-choices? dropdown? content-props] :as select-opts}
    {:keys [*show-new-property-config?]}]
@@ -529,55 +541,55 @@
                                :value (:db/id block)
                                :label-value value})) (:property/closed-values property))
                     (->> values
-                         (mapcat (fn [value]
-                                   (if (coll? value)
-                                     (map (fn [v] {:value v}) value)
-                                     [{:value value}])))
-                         (map (fn [{:keys [value]}]
-                                (if (and ref-type? (number? value))
-                                  (when-let [e (db/entity value)]
-                                    {:label (db-property/property-value-content e)
-                                     :value value})
-                                  {:label value
-                                   :value value})))
-                         (distinct)))
+                      (mapcat (fn [value]
+                                (if (coll? value)
+                                  (map (fn [v] {:value v}) value)
+                                  [{:value value}])))
+                      (map (fn [{:keys [value]}]
+                             (if (and ref-type? (number? value))
+                               (when-let [e (db/entity value)]
+                                 {:label (db-property/property-value-content e)
+                                  :value value})
+                               {:label value
+                                :value value})))
+                      (distinct)))
             items (->> (if (= :date type)
                          (map (fn [m] (let [label (:block/title (db/entity (:value m)))]
                                         (when label
                                           (assoc m :label label)))) items)
                          items)
-                       (remove nil?))
+                    (remove nil?))
             on-chosen (fn [chosen selected?]
                         (let [value (if (map? chosen) (:value chosen) chosen)]
                           (add-or-remove-property-value block property value selected?
-                                                        {:refresh-result-f refresh-result-f})))
+                            {:refresh-result-f refresh-result-f})))
             selected-choices' (get block (:db/ident property))
             selected-choices (if (every? de/entity? selected-choices')
                                (map :db/id selected-choices')
                                [selected-choices'])]
         (select-aux block property
-                    {:multiple-choices? multiple-choices?
-                     :items items
-                     :selected-choices selected-choices
-                     :dropdown? dropdown?
-                     :show-new-when-not-exact-match? (not (or closed-values? (= :date type)))
-                     :input-default-placeholder "Select"
-                     :extract-chosen-fn :value
-                     :extract-fn (fn [x] (or (:label-value x) (:label x)))
-                     :content-props content-props
-                     :on-chosen on-chosen
-                     :input-opts (fn [_]
-                                   {:on-blur (fn []
-                                               (when-let [f (:on-chosen select-opts)] (f)))
-                                    :on-click (fn []
-                                                (when *show-new-property-config?
-                                                  (reset! *show-new-property-config? false)))
-                                    :on-key-down
-                                    (fn [e]
-                                      (case (util/ekey e)
-                                        "Escape"
-                                        (when-let [f (:on-chosen select-opts)] (f))
-                                        nil))})})))))
+          {:multiple-choices? multiple-choices?
+           :items items
+           :selected-choices selected-choices
+           :dropdown? dropdown?
+           :show-new-when-not-exact-match? (not (or closed-values? (= :date type)))
+           :input-default-placeholder "Select"
+           :extract-chosen-fn :value
+           :extract-fn (fn [x] (or (:label-value x) (:label x)))
+           :content-props content-props
+           :on-chosen on-chosen
+           :input-opts (fn [_]
+                         {:on-blur (fn []
+                                     (when-let [f (:on-chosen select-opts)] (f)))
+                          :on-click (fn []
+                                      (when *show-new-property-config?
+                                        (reset! *show-new-property-config? false)))
+                          :on-key-down
+                          (fn [e]
+                            (case (util/ekey e)
+                              "Escape"
+                              (when-let [f (:on-chosen select-opts)] (f))
+                              nil))})})))))
 
 (rum/defcs property-normal-block-value <
   {:init (fn [state]
@@ -607,11 +619,11 @@
        (property-empty-btn-value)])))
 
 (rum/defcs property-block-value < rum/reactive db-mixins/query
-  {:init (fn [state]
-           (let [block (first (:rum/args state))]
-             (when-let [block-id (or (:db/id block) (:block/uuid block))]
-               (db-async/<get-block (state/get-current-repo) block-id :children? true)))
-           state)}
+                                  {:init (fn [state]
+                                           (let [block (first (:rum/args state))]
+                                             (when-let [block-id (or (:db/id block) (:block/uuid block))]
+                                               (db-async/<get-block (state/get-current-repo) block-id :children? true)))
+                                           state)}
   [state value block property page-cp]
   (when value
     (if (state/sub-async-query-loading value)
@@ -625,7 +637,7 @@
               (:block/page v-block)
               (property-normal-block-value block property v-block)
 
-                ;; page/class/etc.
+              ;; page/class/etc.
               (:block/name v-block)
               (rum/with-key
                 (page-cp {:disable-preview? true
@@ -673,17 +685,17 @@
                             (shui/button {:variant :ghost
                                           :size :sm
                                           :class "px-0 py-0 h-4"}
-                                         (ui/icon "edit" {:size 14})))])]
+                              (ui/icon "edit" {:size 14})))])]
     [:div.select-item.cursor-pointer
      (cond
        (= value :logseq.property/empty-placeholder)
        (property-empty-btn-value)
 
        (or (ldb/page? value)
-           (and (seq (:block/tags value))
-                ;; FIXME: page-cp should be renamed to node-cp and
-                ;; support this case and maybe other complex cases.
-                (not (string/includes? (:block/title value) "[["))))
+         (and (seq (:block/tags value))
+           ;; FIXME: page-cp should be renamed to node-cp and
+           ;; support this case and maybe other complex cases.
+           (not (string/includes? (:block/title value) "[["))))
        (when value
          (rum/with-key
            (page-cp {:disable-preview? true
@@ -714,10 +726,10 @@
   (let [*el (rum/use-ref nil)]
     ;; Open popover initially when editing a property
     (rum/use-effect!
-     (fn []
-       (when (:editing? opts)
-         (.click (rum/deref *el))))
-     [(:editing? opts)])
+      (fn []
+        (when (:editing? opts)
+          (.click (rum/deref *el))))
+      [(:editing? opts)])
     (let [schema (:block/schema property)
           type (get schema :type :default)
           select-opts' (assoc select-opts :multiple-choices? false)
@@ -733,27 +745,27 @@
           show! (fn [e]
                   (let [target (.-target e)]
                     (when-not (or config/publishing?
-                                  (util/shift-key? e)
-                                  (util/meta-key? e)
-                                  (util/link? target)
-                                  (when-let [node (.closest target "a")]
-                                    (not (or (d/has-class? node "page-ref")
-                                             (d/has-class? node "tag")))))
+                                (util/shift-key? e)
+                                (util/meta-key? e)
+                                (util/link? target)
+                                (when-let [node (.closest target "a")]
+                                  (not (or (d/has-class? node "page-ref")
+                                         (d/has-class? node "tag")))))
 
                       (shui/popup-show! target popup-content
-                                        {:align "start"
-                                         :as-dropdown? true
-                                         :auto-focus? true
-                                         :trigger-id trigger-id}))))]
+                        {:align "start"
+                         :as-dropdown? true
+                         :auto-focus? true
+                         :trigger-id trigger-id}))))]
       (shui/trigger-as
-       (if (:other-position? opts) :div :div.jtrigger.flex.flex-1.w-full)
-       {:ref *el
-        :id trigger-id
-        :tabIndex 0
-        :on-click show!}
-       (if (string/blank? value)
-         (property-empty-text-value)
-         (value-f))))))
+        (if (:other-position? opts) :div :div.jtrigger.flex.flex-1.w-full)
+        {:ref *el
+         :id trigger-id
+         :tabIndex 0
+         :on-click show!}
+        (if (string/blank? value)
+          (property-empty-text-value)
+          (value-f))))))
 
 (defn- property-value-inner
   [block property value {:keys [inline-text page-cp
@@ -761,7 +773,7 @@
   (let [schema (:block/schema property)
         multiple-values? (db-property/many? property)
         class (str (when-not row? "flex flex-1 ")
-                   (when multiple-values? "property-value-content"))
+                (when multiple-values? "property-value-content"))
         type (:type schema)]
     [:div.cursor-text
      {:id (or dom-id (random-uuid))
@@ -789,8 +801,8 @@
         schema (:block/schema property)
         type (get schema :type :default)
         editing? (or editing?
-                     (and (state/sub-editing? [container-id (:block/uuid block)])
-                          (= (:db/id property) (:db/id (:property (state/get-editor-action-data))))))
+                   (and (state/sub-editing? [container-id (:block/uuid block)])
+                     (= (:db/id property) (:db/id (:property (state/get-editor-action-data))))))
         select-type?' (select-type? property type)
         closed-values? (seq (:property/closed-values property))
         select-opts {:on-chosen on-chosen}
@@ -800,11 +812,11 @@
     (if (= :logseq.property/icon (:db/ident property))
       (icon-row block editing?)
       (if (and select-type?'
-               (not (and (not closed-values?) (= type :date))))
+            (not (and (not closed-values?) (= type :date))))
         (single-value-select block property value
-                             (fn [] (select-item property type value opts))
-                             select-opts
-                             (assoc opts :editing? editing?))
+          (fn [] (select-item property type value opts))
+          select-opts
+          (assoc opts :editing? editing?))
         (case type
           :date
           (property-value-date-picker block property value (merge opts {:editing? editing?}))
@@ -820,7 +832,7 @@
                              :on-key-down (fn [e]
                                             (when (= (util/ekey e) "Enter")
                                               (add-property!)))})])
-        ;; :others
+          ;; :others
           [:div.flex.flex-1
            (property-value-inner block property value opts)])))))
 
@@ -831,21 +843,21 @@
         *el (rum/use-ref nil)
         items (if (de/entity? v) #{v} v)]
     (rum/use-effect!
-     (fn []
-       (when editing?
-         (.click (rum/deref *el))))
-     [editing?])
+      (fn []
+        (when editing?
+          (.click (rum/deref *el))))
+      [editing?])
     (let [select-cp (fn [select-opts]
                       (let [select-opts (merge {:multiple-choices? true
                                                 :on-chosen (fn []
                                                              (when on-chosen (on-chosen)))}
-                                               select-opts
-                                               {:dropdown? false})]
+                                          select-opts
+                                          {:dropdown? false})]
                         [:div.property-select
                          (if (= :node type)
                            (property-value-select-node block property
-                                                       select-opts
-                                                       opts)
+                             select-opts
+                             opts)
                            (select block property select-opts opts))]))]
       (let [toggle-fn shui/popup-hide!
             content-fn (fn [{:keys [_id content-props]}]
@@ -857,22 +869,22 @@
                       (let [target (.-target e)]
                         (when-not (or (util/link? target) (.closest target "a") config/publishing?)
                           (shui/popup-show! (rum/deref *el) content-fn
-                                            {:as-dropdown? true :as-content? false
-                                             :align "start" :auto-focus? true}))))
+                            {:as-dropdown? true :as-content? false
+                             :align "start" :auto-focus? true}))))
           :on-key-down (fn [^js e]
                          (case (.-key e)
                            (" " "Enter")
                            (do (some-> (rum/deref *el) (.click))
-                               (util/stop e))
+                             (util/stop e))
                            :dune))
           :class "flex flex-1 flex-row items-center flex-wrap gap-x-2 gap-y-2 pr-4"}
          (let [not-empty-value? (not= (map :db/ident items) [:logseq.property/empty-placeholder])]
            (if (and (seq items) not-empty-value?)
              (concat
-              (for [item items]
-                (rum/with-key (select-item property type item opts) (or (:block/uuid item) (str item))))
-              (when date?
-                [(property-value-date-picker block property nil {:toggle-fn toggle-fn})]))
+               (for [item items]
+                 (rum/with-key (select-item property type item opts) (or (:block/uuid item) (str item))))
+               (when date?
+                 [(property-value-date-picker block property nil {:toggle-fn toggle-fn})]))
              (if date?
                (property-value-date-picker block property nil {:toggle-fn toggle-fn})
                (property-empty-text-value))))]))))
@@ -882,60 +894,60 @@
   (let [block (db/sub-block (:db/id block))
         value (get block (:db/ident property))
         value' (if (coll? value) value
-                   (when (some? value) #{value}))]
+                 (when (some? value) #{value}))]
     (multiple-values-inner block property value' opts schema)))
 
 (rum/defcs property-value < rum/reactive
   [state block property v {:keys [show-tooltip?]
                            :as opts}]
   (ui/catch-error
-   (ui/block-error "Something wrong" {})
-   (let [block-cp (state/get-component :block/blocks-container)
-         opts (merge opts
-                     {:page-cp (state/get-component :block/page-cp)
-                      :inline-text (state/get-component :block/inline-text)
-                      :editor-box (state/get-component :editor/box)
-                      :block-cp block-cp
-                      :properties-cp (state/get-component :block/properties-cp)})
-         dom-id (str "ls-property-" (:db/id block) "-" (:db/id property))
-         editor-id (str dom-id "-editor")
-         schema (:block/schema property)
-         type (some-> schema (get :type :default))
-         multiple-values? (db-property/many? property)
-         v (cond
-             (and multiple-values? (or (set? v) (and (coll? v) (empty? v)) (nil? v)))
-             v
-             multiple-values?
-             #{v}
-             (set? v)
-             (first v)
-             :else
-             v)
-         empty-value? (when (coll? v) (= :logseq.property/empty-placeholder (:db/ident (first v))))
-         closed-values? (seq (:property/closed-values property))
-         value-cp [:div.property-value-inner
-                   {:data-type type
-                    :class (str (when empty-value? "empty-value")
-                                (when-not (:other-position? opts) " w-full"))}
-                   (cond
-                     (and multiple-values? (= type :default) (not closed-values?))
-                     (property-normal-block-value block property v)
-
-                     multiple-values?
-                     (multiple-values block property opts schema)
-
-                     :else
-                     (property-scalar-value block property v
-                                            (merge
-                                             opts
-                                             {:editor-id editor-id
-                                              :dom-id dom-id})))]]
-     (if show-tooltip?
-       (shui/tooltip-provider
-        (shui/tooltip
-         {:delayDuration 1200}
-         (shui/tooltip-trigger
-          {:onFocusCapture #(util/stop-propagation %)} value-cp)
-         (shui/tooltip-content
-          (str "Change " (:block/title property)))))
-       value-cp))))
+    (ui/block-error "Something wrong" {})
+    (let [block-cp (state/get-component :block/blocks-container)
+          opts (merge opts
+                 {:page-cp (state/get-component :block/page-cp)
+                  :inline-text (state/get-component :block/inline-text)
+                  :editor-box (state/get-component :editor/box)
+                  :block-cp block-cp
+                  :properties-cp (state/get-component :block/properties-cp)})
+          dom-id (str "ls-property-" (:db/id block) "-" (:db/id property))
+          editor-id (str dom-id "-editor")
+          schema (:block/schema property)
+          type (some-> schema (get :type :default))
+          multiple-values? (db-property/many? property)
+          v (cond
+              (and multiple-values? (or (set? v) (and (coll? v) (empty? v)) (nil? v)))
+              v
+              multiple-values?
+              #{v}
+              (set? v)
+              (first v)
+              :else
+              v)
+          empty-value? (when (coll? v) (= :logseq.property/empty-placeholder (:db/ident (first v))))
+          closed-values? (seq (:property/closed-values property))
+          value-cp [:div.property-value-inner
+                    {:data-type type
+                     :class (str (when empty-value? "empty-value")
+                              (when-not (:other-position? opts) " w-full"))}
+                    (cond
+                      (and multiple-values? (= type :default) (not closed-values?))
+                      (property-normal-block-value block property v)
+
+                      multiple-values?
+                      (multiple-values block property opts schema)
+
+                      :else
+                      (property-scalar-value block property v
+                        (merge
+                          opts
+                          {:editor-id editor-id
+                           :dom-id dom-id})))]]
+      (if show-tooltip?
+        (shui/tooltip-provider
+          (shui/tooltip
+            {:delayDuration 1200}
+            (shui/tooltip-trigger
+              {:onFocusCapture #(util/stop-propagation %)} value-cp)
+            (shui/tooltip-content
+              (str "Change " (:block/title property)))))
+        value-cp))))