Răsfoiți Sursa

feat: allow tags to be used as properties

Tienson Qin 1 an în urmă
părinte
comite
93ae0047be

+ 6 - 0
deps/db/src/logseq/db/frontend/property.cljs

@@ -474,6 +474,11 @@
   [s]
   (string/includes? s ".property"))
 
+(defn user-class-namespace?
+  "Determines if namespace string is a user class"
+  [s]
+  (string/includes? s ".class"))
+
 (defn property?
   "Determines if ident kw is a property visible to user"
   [k]
@@ -481,6 +486,7 @@
     (and k-name
          (or (contains? logseq-property-namespaces k-name)
              (user-property-namespace? k-name)
+             (user-class-namespace? k-name)
              ;; disallow private db-attribute-properties as they cause unwanted refs
              ;; and appear noisily in debugging contexts
              (and (keyword? k) (contains? public-db-attribute-properties k))))))

+ 49 - 19
src/main/frontend/components/property.cljs

@@ -13,6 +13,7 @@
             [frontend.handler.property.util :as pu]
             [frontend.config :as config]
             [frontend.db :as db]
+            [frontend.db.model :as db-model]
             [frontend.db-mixins :as db-mixins]
             [frontend.db.async :as db-async]
             [frontend.handler.db-based.property :as db-property-handler]
@@ -99,7 +100,8 @@
                       add-class-property? (and (ldb/class? block) class-schema?)]
                 (when *property (reset! *property property))
                 (p/do!
-                 (when *show-new-property-config? (reset! *show-new-property-config? false))
+                 (when *show-new-property-config?
+                   (reset! *show-new-property-config? false))
                  (when (= (:type schema) :node) (reset! *show-class-select? true))
                  (components-pu/update-property! property property-name schema)
                  (cond
@@ -142,30 +144,42 @@
 (rum/defc property-select
   [exclude-properties select-opts]
   (let [[properties set-properties!] (rum/use-state nil)
+        [classes set-classes!] (rum/use-state nil)
         [excluded-properties set-excluded-properties!] (rum/use-state nil)]
     (rum/use-effect!
      (fn []
-       (p/let [properties (db-async/<db-based-get-all-properties (state/get-current-repo))]
+       (p/let [repo (state/get-current-repo)
+               properties (db-async/<db-based-get-all-properties repo)
+               classes (->> (db-model/get-all-classes repo)
+                            (remove ldb/built-in?))]
+         (set-classes! classes)
          (set-properties! (remove exclude-properties properties))
          (set-excluded-properties! (->> properties
                                         (filter exclude-properties)
                                         (map :block/title)
                                         set))))
      [])
-    [:div.ls-property-add.flex.flex-row.items-center.property-key
-     [:div.ls-property-key
-      (select/select (merge
-                      {:items (map (fn [x]
-                                     {:label (:block/title x)
-                                      :value (:block/uuid x)}) properties)
-                       :extract-fn :label
-                       :dropdown? false
-                       :close-modal? false
-                       :new-case-sensitive? true
-                       :show-new-when-not-exact-match? true
-                       :exact-match-exclude-items (fn [s] (contains? excluded-properties s))
-                       :input-default-placeholder "Add or change property"}
-                      select-opts))]]))
+    (let [items (concat
+                 (map (fn [x]
+                        {:label (:block/title x)
+                         :value (:block/uuid x)}) properties)
+                 (map (fn [x]
+                        {:label (:block/title x)
+                         :value (:block/uuid x)
+                         :group "Tags"}) classes))]
+      [:div.ls-property-add.flex.flex-row.items-center.property-key
+       [:div.ls-property-key
+        (select/select (merge
+                        {:items items
+                         :grouped? true
+                         :extract-fn :label
+                         :dropdown? false
+                         :close-modal? false
+                         :new-case-sensitive? true
+                         :show-new-when-not-exact-match? true
+                         :exact-match-exclude-items (fn [s] (contains? excluded-properties s))
+                         :input-default-placeholder "Add or change property"}
+                        select-opts))]])))
 
 (rum/defc property-icon
   [property property-type]
@@ -194,7 +208,7 @@
   (fn [{:keys [value label]}]
     (reset! *property-key (if (uuid? value) label value))
     (let [property (when (uuid? value) (db/entity [:block/uuid value]))]
-      (when (and *show-new-property-config? (not property))
+      (when (and *show-new-property-config? (not (ldb/property? property)))
         (reset! *show-new-property-config? true))
       (reset! *property property)
       (when property
@@ -221,10 +235,26 @@
                  (not (seq (:property/closed-values property))))
             (pv/<create-new-block! block property "")
 
+            ;; using class as property
+            (and property (ldb/class? property))
+            (let [schema (assoc (:block/schema property)
+                                :type :node)]
+              (p/do!
+               (db/transact! (state/get-current-repo)
+                             [{:db/id (:db/id property)
+                               :db/ident (:db/ident property)
+                               :db/cardinality :db.cardinality/one
+                               :db/valueType :db.type/ref
+                               :db/index true
+                               :block/tags :logseq.class/Property
+                               :block/schema schema
+                               :property/schema.classes (:db/id property)}]
+                             {:outliner-op :save-block})
+               (reset! *show-new-property-config? false)))
+
             (or (not= :default type)
                 (and (= :default type) (seq (:property/closed-values property))))
-            (p/do!
-             (reset! *show-new-property-config? false))))))))
+            (reset! *show-new-property-config? false)))))))
 
 (rum/defc property-key-title
   [block property class-schema?]

+ 3 - 2
src/main/frontend/components/select.cljs

@@ -68,7 +68,7 @@
    :will-unmount (fn [state]
                    (shui/dialog-close! :ls-select-modal)
                    state)}
-  [state {:keys [items limit on-chosen empty-placeholder
+  [state {:keys [items limit on-chosen empty-placeholder grouped?
                  prompt-key input-default-placeholder close-modal?
                  extract-fn extract-chosen-fn host-opts on-input input-opts
                  item-cp transform-fn tap-*input-val
@@ -139,7 +139,8 @@
                            [:div.item-results-wrap
                             (ui/auto-complete
                              search-result
-                             {:item-render       (or item-cp (fn [result chosen?]
+                             {:grouped? grouped?
+                              :item-render       (or item-cp (fn [result chosen?]
                                                                (render-item result chosen? multiple-choices? *selected-choices)))
                               :class             "cp__select-results"
                               :on-chosen         (fn [raw-chosen e]

+ 43 - 32
src/main/frontend/ui.cljs

@@ -511,43 +511,54 @@
            empty-placeholder
            item-render
            class
-           header]}]
+           header
+           grouped?]}]
   (let [*current-idx (get state ::current-idx)
-        *groups (atom #{})]
+        *groups (atom #{})
+        render-f (fn [matched]
+                   (for [[idx item] (medley/indexed matched)]
+                     (let [react-key (str idx)
+                           item-cp
+                           [:div.menu-link-wrap
+                            {:key react-key
+                   ;; mouse-move event to indicate that cursor moved by user
+                             :on-mouse-move  #(reset! *current-idx idx)}
+                            (let [chosen? (= @*current-idx idx)]
+                              (menu-link
+                               {:id (str "ac-" react-key)
+                                :tab-index "0"
+                                :class (when chosen? "chosen")
+                       ;; TODO: should have more tests on touch devices
+                       ;:on-pointer-down #(util/stop %)
+                                :on-click (fn [e]
+                                            (util/stop e)
+                                            (when-not (:disabled? item)
+                                              (if (and (gobj/get e "shiftKey") on-shift-chosen)
+                                                (on-shift-chosen item)
+                                                (on-chosen item e))))}
+                               (if item-render (item-render item chosen?) item)))]]
+
+                       (let [group-name (and (fn? get-group-name) (get-group-name item))]
+                         (if (and group-name (not (contains? @*groups group-name)))
+                           (do
+                             (swap! *groups conj group-name)
+                             [:div
+                              [:div.ui__ac-group-name group-name]
+                              item-cp])
+                           item-cp)))))]
     [:div#ui__ac {:class class}
      (if (seq matched)
        [:div#ui__ac-inner.hide-scrollbar
         (when header header)
-        (for [[idx item] (medley/indexed matched)]
-          (let [react-key (str idx)
-                item-cp
-                [:div.menu-link-wrap
-                 {:key react-key
-                   ;; mouse-move event to indicate that cursor moved by user
-                  :on-mouse-move  #(reset! *current-idx idx)}
-                 (let [chosen? (= @*current-idx idx)]
-                   (menu-link
-                    {:id (str "ac-" react-key)
-                     :tab-index "0"
-                     :class (when chosen? "chosen")
-                       ;; TODO: should have more tests on touch devices
-                       ;:on-pointer-down #(util/stop %)
-                     :on-click (fn [e]
-                                 (util/stop e)
-                                 (when-not (:disabled? item)
-                                   (if (and (gobj/get e "shiftKey") on-shift-chosen)
-                                     (on-shift-chosen item)
-                                     (on-chosen item e))))}
-                    (if item-render (item-render item chosen?) item)))]]
-
-            (let [group-name (and (fn? get-group-name) (get-group-name item))]
-              (if (and group-name (not (contains? @*groups group-name)))
-                (do
-                  (swap! *groups conj group-name)
-                  [:div
-                   [:div.ui__ac-group-name group-name]
-                   item-cp])
-                item-cp))))]
+        (if grouped?
+          (for [[group matched] (group-by :group matched)]
+            (if group
+              [:div
+               [:div.ui__ac-group-name group]
+               (render-f matched)]
+              (render-f matched))
+            (render-f matched))
+          (render-f matched))]
        (when empty-placeholder
          empty-placeholder))]))
 

+ 1 - 2
src/main/frontend/worker/export.cljs

@@ -6,8 +6,7 @@
             [logseq.graph-parser.property :as gp-property]
             [logseq.outliner.tree :as otree]
             [cljs-bean.core :as bean]
-            [logseq.db.sqlite.util :as sqlite-util]
-            [clojure.string :as string]))
+            [logseq.db.sqlite.util :as sqlite-util]))
 
 (defn- safe-keywordize
   [block]