Browse Source

refactor: property/schema.properties

Replace [:block/schema :properties] with ref type property/schema.properties.

This commit also introduces :logseq.class/base so that all the other
classes will be its children.
Tienson Qin 1 year ago
parent
commit
45f14d4b76

+ 4 - 1
deps/db/src/logseq/db/frontend/class.cljs

@@ -3,9 +3,12 @@
 
 
 (def ^:large-vars/data-var built-in-classes
 (def ^:large-vars/data-var built-in-classes
   "Map of built-in classes for db graphs with their :db/ident as keys"
   "Map of built-in classes for db graphs with their :db/ident as keys"
-  {:logseq.class/task
+  {:logseq.class/base {:original-name "Root class"}
+
+   :logseq.class/task
    {:original-name "Task"
    {:original-name "Task"
     :schema {:properties [:logseq.task/status :logseq.task/priority :logseq.task/scheduled :logseq.task/deadline]}}
     :schema {:properties [:logseq.task/status :logseq.task/priority :logseq.task/scheduled :logseq.task/deadline]}}
+
    :logseq.class/card {:original-name "card"
    :logseq.class/card {:original-name "card"
                        ;; :schema {:property []}
                        ;; :schema {:property []}
                        }
                        }

+ 6 - 7
deps/db/src/logseq/db/frontend/malli_schema.cljs

@@ -212,7 +212,8 @@
   [[:db/index {:optional true} :boolean]
   [[:db/index {:optional true} :boolean]
    [:db/valueType {:optional true} [:enum :db.type/ref]]
    [:db/valueType {:optional true} [:enum :db.type/ref]]
    [:db/cardinality {:optional true} [:enum :db.cardinality/many :db.cardinality/one]]
    [:db/cardinality {:optional true} [:enum :db.cardinality/many :db.cardinality/one]]
-   [:block/order {:optional true} :string]])
+   [:block/order {:optional true} :string]
+   [:property/schema.classes {:optional true} [:set :int]]])
 
 
 (def normal-page
 (def normal-page
   (vec
   (vec
@@ -247,9 +248,7 @@
   [;; For any types except for :checkbox :default :template
   [;; For any types except for :checkbox :default :template
    [:cardinality {:optional true} [:enum :one :many]]
    [:cardinality {:optional true} [:enum :one :many]]
    ;; For closed values
    ;; For closed values
-   [:position {:optional true} :string]
-   ;; For :page and :template
-   [:classes {:optional true} [:set [:or :uuid :keyword]]]])
+   [:position {:optional true} :string]])
 
 
 (def property-common-schema-attrs
 (def property-common-schema-attrs
   "Property :schema attributes common to all properties"
   "Property :schema attributes common to all properties"
@@ -458,7 +457,7 @@
 
 
 ;; Keep malli schema in sync with db schema
 ;; Keep malli schema in sync with db schema
 ;; ========================================
 ;; ========================================
-(let [malli-many-ref-attrs (->> (concat class-attrs page-attrs block-attrs page-or-block-attrs (rest closed-value-block))
+(let [malli-many-ref-attrs (->> (concat class-attrs property-attrs page-attrs block-attrs page-or-block-attrs (rest closed-value-block))
                                 (filter #(= (last %) [:set :int]))
                                 (filter #(= (last %) [:set :int]))
                                 (map first)
                                 (map first)
                                 set)]
                                 set)]
@@ -467,7 +466,7 @@
                          (string/join ", " undeclared-ref-attrs))
                          (string/join ", " undeclared-ref-attrs))
                     {}))))
                     {}))))
 
 
-(let [malli-one-ref-attrs (->> (concat class-attrs page-attrs block-attrs page-or-block-attrs (rest normal-page))
+(let [malli-one-ref-attrs (->> (concat class-attrs property-attrs page-attrs block-attrs page-or-block-attrs (rest normal-page))
                                (filter #(= (last %) :int))
                                (filter #(= (last %) :int))
                                (map first)
                                (map first)
                                set)
                                set)
@@ -477,7 +476,7 @@
                          (string/join ", " undeclared-ref-attrs))
                          (string/join ", " undeclared-ref-attrs))
                     {}))))
                     {}))))
 
 
-(let [malli-non-ref-attrs (->> (concat class-attrs page-attrs block-attrs page-or-block-attrs (rest normal-page))
+(let [malli-non-ref-attrs (->> (concat class-attrs property-attrs page-attrs block-attrs page-or-block-attrs (rest normal-page))
                                (concat (rest file-block) (rest asset-block)
                                (concat (rest file-block) (rest asset-block)
                                        db-ident-keys (rest class-page))
                                        db-ident-keys (rest class-page))
                                (remove #(= (last %) [:set :int]))
                                (remove #(= (last %) [:set :int]))

+ 1 - 1
deps/db/src/logseq/db/frontend/property.cljs

@@ -35,7 +35,7 @@
                           :schema {:type :page
                           :schema {:type :page
                                    :cardinality :many
                                    :cardinality :many
                                    :public? true
                                    :public? true
-                                   :classes #{:logseq.class}}}
+                                   :classes #{:logseq.class/base}}}
    :logseq.property/page-tags {:original-name "pageTags"
    :logseq.property/page-tags {:original-name "pageTags"
                                :schema {:type :page
                                :schema {:type :page
                                         :public? true
                                         :public? true

+ 2 - 2
deps/db/src/logseq/db/frontend/property/type.cljs

@@ -43,8 +43,8 @@
                :number #{:cardinality}
                :number #{:cardinality}
                :date #{:cardinality}
                :date #{:cardinality}
                :url #{:cardinality}
                :url #{:cardinality}
-               :page #{:cardinality :classes}
-               :template #{:classes}
+               :page #{:cardinality}
+               :template #{}
                :checkbox #{}}))
                :checkbox #{}}))
 
 
 (assert (= (set user-built-in-property-types) (set (keys user-built-in-allowed-schema-attributes)))
 (assert (= (set user-built-in-property-types) (set (keys user-built-in-allowed-schema-attributes)))

+ 2 - 0
deps/db/src/logseq/db/frontend/schema.cljs

@@ -131,6 +131,8 @@
     ;; closed value
     ;; closed value
     :block/closed-value-property {:db/valueType :db.type/ref
     :block/closed-value-property {:db/valueType :db.type/ref
                                   :db/cardinality :db.cardinality/many}
                                   :db/cardinality :db.cardinality/many}
+    :property/schema.classes {:db/valueType :db.type/ref
+                              :db/cardinality :db.cardinality/many}
 
 
     :file/last-modified-at {}
     :file/last-modified-at {}
     :asset/uuid {:db/unique :db.unique/identity}
     :asset/uuid {:db/unique :db.unique/identity}

+ 5 - 3
deps/db/src/logseq/db/sqlite/create_graph.cljs

@@ -42,6 +42,7 @@
              (->> (keep :db/ident tx)
              (->> (keep :db/ident tx)
                   frequencies
                   frequencies
                   (keep (fn [[k v]] (when (> v 1) k)))
                   (keep (fn [[k v]] (when (> v 1) k)))
+                  (remove #{:logseq.class/base})
                   seq)]
                   seq)]
     (throw (ex-info (str "The following :db/idents are not unique and clobbered each other: "
     (throw (ex-info (str "The following :db/idents are not unique and clobbered each other: "
                          (vec conflicting-idents))
                          (vec conflicting-idents))
@@ -54,7 +55,8 @@
   (let [initial-data [(kv :db/type "db")
   (let [initial-data [(kv :db/type "db")
                       (kv :schema/version db-schema/version)
                       (kv :schema/version db-schema/version)
                       ;; Empty property value used by db.type/ref properties
                       ;; Empty property value used by db.type/ref properties
-                      {:db/ident :logseq.property/empty-placeholder}]
+                      {:db/ident :logseq.property/empty-placeholder}
+                      {:db/ident :logseq.class/base}]
         initial-files [{:block/uuid (d/squuid)
         initial-files [{:block/uuid (d/squuid)
                         :file/path (str "logseq/" "config.edn")
                         :file/path (str "logseq/" "config.edn")
                         :file/content config-content
                         :file/content config-content
@@ -92,7 +94,7 @@
                                    (seq properties)
                                    (seq properties)
                                    (assoc :class/schema.properties properties)))))))
                                    (assoc :class/schema.properties properties)))))))
                          db-class/built-in-classes)
                          db-class/built-in-classes)
-        tx (vec (concat default-properties default-classes
-                        initial-data initial-files default-pages))]
+        tx (vec (concat initial-data default-properties default-classes
+                        initial-files default-pages))]
     (validate-tx-for-duplicate-idents tx)
     (validate-tx-for-duplicate-idents tx)
     tx))
     tx))

+ 10 - 4
deps/db/src/logseq/db/sqlite/util.cljs

@@ -83,13 +83,14 @@
                      db-ident
                      db-ident
                      (db-property/create-user-property-ident-from-name (name db-ident)))
                      (db-property/create-user-property-ident-from-name (name db-ident)))
          prop-name (or original-name (name db-ident'))
          prop-name (or original-name (name db-ident'))
-         block-order (when-not from-ui-thread? (db-order/gen-key nil))]
+         block-order (when-not from-ui-thread? (db-order/gen-key nil))
+         classes (:classes prop-schema)]
      (block-with-timestamps
      (block-with-timestamps
       (cond->
       (cond->
        {:db/ident db-ident'
        {:db/ident db-ident'
         :block/type "property"
         :block/type "property"
         :block/format :markdown
         :block/format :markdown
-        :block/schema (merge {:type :default} prop-schema)
+        :block/schema (merge {:type :default} (dissoc prop-schema :classes))
         :block/name (common-util/page-name-sanity-lc (name prop-name))
         :block/name (common-util/page-name-sanity-lc (name prop-name))
         :block/uuid (or block-uuid (d/squuid))
         :block/uuid (or block-uuid (d/squuid))
         :block/original-name (name prop-name)
         :block/original-name (name prop-name)
@@ -99,6 +100,8 @@
                           :db.cardinality/one)}
                           :db.cardinality/one)}
         block-order
         block-order
         (assoc :block/order block-order)
         (assoc :block/order block-order)
+        (seq classes)
+        (assoc :property/schema.classes classes)
         (or ref-type? (contains? (conj db-property-type/ref-property-types :entity) (:type prop-schema)))
         (or ref-type? (contains? (conj db-property-type/ref-property-types :entity) (:type prop-schema)))
         (assoc :db/valueType :db.type/ref))))))
         (assoc :db/valueType :db.type/ref))))))
 
 
@@ -107,8 +110,11 @@
   "Build a standard new class so that it is is consistent across contexts"
   "Build a standard new class so that it is is consistent across contexts"
   [block]
   [block]
   (block-with-timestamps
   (block-with-timestamps
-   (merge {:block/type "class"
-           :block/format :markdown}
+   (merge (cond->
+           {:block/type "class"
+            :block/format :markdown}
+            (not= (:db/ident block) :logseq.class/base)
+            (assoc :class/parent :logseq.class/base))
           block)))
           block)))
 
 
 (defn build-new-page
 (defn build-new-page

+ 7 - 4
scripts/src/logseq/tasks/db_graph/create_graph_with_schema_org.cljs

@@ -336,9 +336,12 @@
           (let [select-class-uuids (->> select-class-ids (map class-uuids) set)]
           (let [select-class-uuids (->> select-class-ids (map class-uuids) set)]
             (-> properties
             (-> properties
                 (update-vals (fn [m]
                 (update-vals (fn [m]
-                               (if (get-in m [:block/schema :classes])
-                                 (update-in m [:block/schema :classes] (fn [cs] (set (filterv #(contains? select-class-uuids %) cs))))
-                                 m)))))
+                               (let [classes (get-in m [:block/schema :classes])]
+                                 (if (seq classes)
+                                   (assoc m :property/schema.classes
+                                          (set (map (fn [id] [:block/uuid id])
+                                                    (filter #(contains? select-class-uuids %) classes))))
+                                   m))))))
           properties)
           properties)
         classes (generate-classes
         classes (generate-classes
                  (map #(class-map %) select-class-ids)
                  (map #(class-map %) select-class-ids)
@@ -411,4 +414,4 @@
     (println "Created graph" (str db-name "!"))))
     (println "Created graph" (str db-name "!"))))
 
 
 (when (= nbb/*file* (:file (meta #'-main)))
 (when (= nbb/*file* (:file (meta #'-main)))
-  (-main *command-line-args*))
+  (-main *command-line-args*))

+ 14 - 13
src/main/frontend/components/class.cljs

@@ -70,19 +70,20 @@
       [:div.property-configure.grid.gap-2
       [:div.property-configure.grid.gap-2
        (when show-title? [:h1.title.mb-4 "Configure class"])
        (when show-title? [:h1.title.mb-4 "Configure class"])
 
 
-       [:div.grid.grid-cols-5.gap-1.items-center.class-parent
-        [:div.col-span-2 "Parent class:"]
-        (if config/publishing?
-          [:div.col-span-3
-           (if-let [parent-class (some-> (:db/id (:class/parent page))
-                                         db/entity)]
-             [:a {:on-click #(route-handler/redirect-to-page! (:block/uuid parent-class))}
-              (:block/original-name parent-class)]
-             "None")]
-          [:div.col-span-3
-           (let [parent (some-> (:db/id (:class/parent page))
-                                db/entity)]
-             (page-parent page parent))])]
+       (when-not (= (:db/ident page) :logseq.class/base)
+         [:div.grid.grid-cols-5.gap-1.items-center.class-parent
+          [:div.col-span-2 "Parent class:"]
+          (if config/publishing?
+            [:div.col-span-3
+             (if-let [parent-class (some-> (:db/id (:class/parent page))
+                                           db/entity)]
+               [:a {:on-click #(route-handler/redirect-to-page! (:block/uuid parent-class))}
+                (:block/original-name parent-class)]
+               "None")]
+            [:div.col-span-3
+             (let [parent (some-> (:db/id (:class/parent page))
+                                  db/entity)]
+               (page-parent page parent))])])
 
 
        (when (:class/parent page)
        (when (:class/parent page)
          (let [ancestor-pages (loop [parents [page]]
          (let [ancestor-pages (loop [parents [page]]

+ 40 - 60
src/main/frontend/components/property.cljs

@@ -43,25 +43,25 @@
           (:block/uuid page))))))
           (:block/uuid page))))))
 
 
 (rum/defc class-select
 (rum/defc class-select
-  [*property-schema schema-classes {:keys [multiple-choices? save-property-fn disabled?]
-                                    :or {multiple-choices? true}}]
-  [:div.flex.flex-1.col-span-3
-   (let [content-fn
-         (fn [{:keys [id]}]
-           (let [toggle-fn #(shui/popup-hide! id)
-                 classes (model/get-all-classes (state/get-current-repo))
-                 options (cond->> (map (fn [[name id]]
-                                         {:label name :value id})
-                                    classes)
-                           (not= :template (:type @*property-schema))
-                           (concat [{:label "Logseq Class" :value :logseq.class}]))
-                 opts (cond->
-                        {:items options
+  [property {:keys [multiple-choices? disabled?]
+             :or {multiple-choices? true}}]
+  (let [schema-classes (:property/schema.classes property)]
+    [:div.flex.flex-1.col-span-3
+     (let [content-fn
+           (fn [{:keys [id]}]
+             (let [toggle-fn #(shui/popup-hide! id)
+                   classes (model/get-all-classes (state/get-current-repo))
+                   options (cond->> (map (fn [[name id]]
+                                           {:label name :value id})
+                                         classes)
+                             (= :template (get-in property [:block/schema :type]))
+                             (remove (fn [[name _id]] (= name "Root class"))))
+                   opts {:items options
                          :input-default-placeholder (if multiple-choices? "Choose classes" "Choose class")
                          :input-default-placeholder (if multiple-choices? "Choose classes" "Choose class")
                          :dropdown? false
                          :dropdown? false
                          :close-modal? false
                          :close-modal? false
                          :multiple-choices? multiple-choices?
                          :multiple-choices? multiple-choices?
-                         :selected-choices schema-classes
+                         :selected-choices (map :block/uuid schema-classes)
                          :extract-fn :label
                          :extract-fn :label
                          :extract-chosen-fn :value
                          :extract-chosen-fn :value
                          :show-new-when-not-exact-match? true
                          :show-new-when-not-exact-match? true
@@ -73,39 +73,25 @@
                                           (do
                                           (do
                                             (util/stop e)
                                             (util/stop e)
                                             (toggle-fn))
                                             (toggle-fn))
-                                          nil))}}
-                        multiple-choices?
-                        (assoc :on-apply (fn [choices]
-                                           (p/let [choices' (p/all (map (fn [value]
-                                                                          (p/let [result (<create-class-if-not-exists! value)]
-                                                                            (or result value))) choices))
-                                                   _ (swap! *property-schema assoc :classes (set choices'))
-                                                   _ (save-property-fn)]
-                                             (toggle-fn))))
-
-                        (not multiple-choices?)
-                        (assoc :on-chosen (fn [value]
-                                            (p/let [result (<create-class-if-not-exists! value)
-                                                    value' (or result value)
-                                                    _ (swap! *property-schema assoc :classes #{value'})
-                                                    _ (save-property-fn)]
-                                              (toggle-fn)))))]
-
-             (select/select opts)))]
-
-    [:div.flex.flex-1.cursor-pointer
-     {:on-click (if disabled?
-                  (constantly nil)
-                  #(shui/popup-show! (.-target %) content-fn))}
-     (if (seq schema-classes)
-       [:div.flex.flex-1.flex-row.items-center.flex-wrap.gap-2
-        (for [class schema-classes]
-          (if (= class :logseq.class)
-            [:a.text-sm "#Logseq Class"]
-            (when-let [page (db/entity [:block/uuid class])]
-              (let [page-name (:block/original-name page)]
-                [:a.text-sm (str "#" page-name)]))))]
-       (pv/property-empty-value))])])
+                                          nil))}
+                         :on-chosen (fn [value select?]
+                                      (p/let [result (<create-class-if-not-exists! value)
+                                              value' (or result value)
+                                              tx-data [[(if select? :db/add :db/retract) (:db/id property) :property/schema.classes [:block/uuid value']]]]
+                                        (db/transact! (state/get-current-repo) tx-data {:outliner-op :update-property})
+                                        (when-not multiple-choices? (toggle-fn))))}]
+
+               (select/select opts)))]
+
+       [:div.flex.flex-1.cursor-pointer
+        {:on-click (if disabled?
+                     (constantly nil)
+                     #(shui/popup-show! (.-target %) content-fn))}
+        (if (seq schema-classes)
+          [:div.flex.flex-1.flex-row.items-center.flex-wrap.gap-2
+           (for [class schema-classes]
+             [:a.text-sm (str "#" (:block/original-name class))])]
+          (pv/property-empty-value))])]))
 
 
 (defn- property-type-label
 (defn- property-type-label
   [property-type]
   [property-type]
@@ -156,7 +142,7 @@
                                          (fn [attr]
                                          (fn [attr]
                                            (when-not (db-property-type/property-type-allows-schema-attribute? type attr)
                                            (when-not (db-property-type/property-type-allows-schema-attribute? type attr)
                                              #(dissoc % attr)))
                                              #(dissoc % attr)))
-                                         [:cardinality :classes :position]))]
+                                         [:cardinality :position]))]
             (when *property-schema
             (when *property-schema
               (swap! *property-schema update-schema-fn))
               (swap! *property-schema update-schema-fn))
             (let [schema (or (and *property-schema @*property-schema)
             (let [schema (or (and *property-schema @*property-schema)
@@ -302,24 +288,18 @@
               (when (empty? (:property/closed-values property))
               (when (empty? (:property/closed-values property))
                 [:div.grid.grid-cols-4.gap-1.items-center.leading-8
                 [:div.grid.grid-cols-4.gap-1.items-center.leading-8
                  [:label "Specify classes:"]
                  [:label "Specify classes:"]
-                 (class-select *property-schema
-                               (:classes @*property-schema)
-                               (assoc opts
-                                      :disabled? disabled?
-                                      :save-property-fn save-property-fn))])
+                 (class-select property (assoc opts :disabled? disabled?))])
 
 
               :template
               :template
               [:div.grid.grid-cols-4.gap-1.items-center.leading-8
               [:div.grid.grid-cols-4.gap-1.items-center.leading-8
                [:label "Specify template:"]
                [:label "Specify template:"]
-               (class-select *property-schema (:classes @*property-schema)
-                             (assoc opts
-                                    :multiple-choices? false
-                                    :disabled? disabled?
-                                    :save-property-fn save-property-fn))]
+               (class-select property (assoc opts
+                                             :multiple-choices? false
+                                             :disabled? disabled?))]
 
 
               nil))
               nil))
 
 
-          (when (and enable-closed-values? (empty? (:classes @*property-schema)))
+          (when (and enable-closed-values? (empty? (:property/schema.classes property)))
             [:div.grid.grid-cols-4.gap-1.items-start.leading-8
             [:div.grid.grid-cols-4.gap-1.items-start.leading-8
              [:label.col-span-1 "Available choices:"]
              [:label.col-span-1 "Available choices:"]
              [:div.col-span-3
              [:div.col-span-3

+ 56 - 64
src/main/frontend/components/property/value.cljs

@@ -94,6 +94,20 @@
         (shui/popup-hide!)
         (shui/popup-hide!)
         (exit-edit-property))))))
         (exit-edit-property))))))
 
 
+(defn- add-or-remove-property-value
+  [block property value selected?]
+  (let [many? (= :db/cardinality.many (:db/cardinality property))]
+    (if selected?
+      (<add-property! block (:db/ident property) value
+                      {:exit-edit? (not many?)})
+      (p/do!
+        (db/transact! (state/get-current-repo)
+                     [[:db/retract (:db/id block) (:db/ident property) value]]
+                     {:outliner-op :save-block})
+        (when-not many?
+          (shui/popup-hide!)
+          (exit-edit-property))))))
+
 (defn- navigate-to-date-page
 (defn- navigate-to-date-page
   [value]
   [value]
   (when value
   (when value
@@ -200,9 +214,9 @@
                                                   :create-first-block? false
                                                   :create-first-block? false
                                                   :tags (if inline-class-uuid
                                                   :tags (if inline-class-uuid
                                                           [inline-class-uuid]
                                                           [inline-class-uuid]
-                                                       ;; Only 1st class b/c page normally has
-                                                       ;; one of and not all these classes
-                                                          (take 1 classes))
+                                                          ;; Only 1st class b/c page normally has
+                                                          ;; one of and not all these classes
+                                                          (mapv :block/uuid (take 1 classes)))
                                                   :class? class?})]
                                                   :class? class?})]
           (:db/id page)))
           (:db/id page)))
       id)))
       id)))
@@ -220,14 +234,14 @@
                         items)
                         items)
                   items)
                   items)
                 (remove #(= :logseq.property/empty-placeholder (:value %))))
                 (remove #(= :logseq.property/empty-placeholder (:value %))))
-        k (if multiple-choices? :on-apply :on-chosen)
+        k :on-chosen
         f (get opts k)
         f (get opts k)
-        f' (fn [chosen]
+        f' (fn [chosen selected?]
              (if (or (and (not multiple-choices?) (= chosen clear-value))
              (if (or (and (not multiple-choices?) (= chosen clear-value))
                      (and multiple-choices? (= chosen [clear-value])))
                      (and multiple-choices? (= chosen [clear-value])))
                (property-handler/remove-block-property! (state/get-current-repo) (:block/uuid block)
                (property-handler/remove-block-property! (state/get-current-repo) (:block/uuid block)
                                                         (:db/ident property))
                                                         (:db/ident property))
-               (f chosen)))]
+               (f chosen selected?)))]
     (select/select (assoc opts
     (select/select (assoc opts
                           :selected-choices selected-choices
                           :selected-choices selected-choices
                           :items items'
                           :items items'
@@ -239,8 +253,9 @@
 
 
 (defn select-page
 (defn select-page
   [property
   [property
-   {:keys [block classes multiple-choices? dropdown? input-opts on-chosen] :as opts}]
+   {:keys [block multiple-choices? dropdown? input-opts] :as opts}]
   (let [repo (state/get-current-repo)
   (let [repo (state/get-current-repo)
+        classes (:property/schema.classes property)
         tags? (= :block/tags (:db/ident property))
         tags? (= :block/tags (:db/ident property))
         alias? (= :block/alias (:db/ident property))
         alias? (= :block/alias (:db/ident property))
         tags-or-alias? (or tags? alias?)
         tags-or-alias? (or tags? alias?)
@@ -262,10 +277,10 @@
                  (seq classes)
                  (seq classes)
                  (mapcat
                  (mapcat
                   (fn [class]
                   (fn [class]
-                    (if (= :logseq.class class)
-                      (map first (model/get-all-classes repo))
-                      (some->> (:db/id (db/entity [:block/uuid class]))
-                               (model/get-class-objects repo)
+                    (if (= :logseq.class/base (:db/ident class))
+                      (->> (map first (model/get-all-classes repo))
+                           (remove #{"Root class"}))
+                      (some->> (model/get-class-objects repo (:db/id class))
                                (map #(:block/original-name (db/entity %))))))
                                (map #(:block/original-name (db/entity %))))))
                   classes)
                   classes)
 
 
@@ -277,7 +292,7 @@
                distinct
                distinct
                (remove (fn [p] (or (ldb/hidden-page? p) (util/uuid-string? (str p))))))
                (remove (fn [p] (or (ldb/hidden-page? p) (util/uuid-string? (str p))))))
         options (map (fn [p] {:value p}) pages)
         options (map (fn [p] {:value p}) pages)
-        string-classes (remove #(= :logseq.class %) classes)
+        classes' (remove (fn [class] (= :logseq.class/base (:db/ident class))) classes)
         opts' (cond->
         opts' (cond->
                (merge
                (merge
                 opts
                 opts
@@ -300,32 +315,25 @@
                  :transform-fn (fn [results input]
                  :transform-fn (fn [results input]
                                  (if-let [[_ new-page class-input] (and (empty? results) (re-find #"(.*)#(.*)$" input))]
                                  (if-let [[_ new-page class-input] (and (empty? results) (re-find #"(.*)#(.*)$" input))]
                                    (let [repo (state/get-current-repo)
                                    (let [repo (state/get-current-repo)
-                                         class-ents (map #(db/entity repo [:block/uuid %]) string-classes)
-                                         class-names (map :block/original-name class-ents)
-                                         descendent-classes (->> class-ents
+                                         class-names (map :block/original-name classes')
+                                         descendent-classes (->> classes'
                                                                  (mapcat #(model/get-class-children repo (:db/id %)))
                                                                  (mapcat #(model/get-class-children repo (:db/id %)))
                                                                  (map #(:block/original-name (db/entity repo %))))]
                                                                  (map #(:block/original-name (db/entity repo %))))]
                                      (->> (concat class-names descendent-classes)
                                      (->> (concat class-names descendent-classes)
                                           (filter #(string/includes? % class-input))
                                           (filter #(string/includes? % class-input))
                                           (mapv #(hash-map :value (str new-page "#" %)))))
                                           (mapv #(hash-map :value (str new-page "#" %)))))
                                    results))
                                    results))
-                 :input-opts input-opts})
-                multiple-choices?
-                (assoc :on-apply (fn [choices]
-                                   (p/let [page-ids (p/all (map #(<create-page-if-not-exists! property string-classes %) choices))
-                                           values (set page-ids)]
-                                     (when on-chosen (on-chosen values)))))
-                (not multiple-choices?)
-                (assoc :on-chosen (fn [chosen]
-                                    (let [page* (string/trim (if (string? chosen) chosen (:value chosen)))]
-                                      (when-not (string/blank? page*)
-                                        (p/let [id (<create-page-if-not-exists! property string-classes page*)]
-                                          (when on-chosen (on-chosen id))))))))]
+                 :input-opts input-opts
+                 :on-chosen (fn [chosen selected?]
+                              (let [page* (string/trim (if (string? chosen) chosen (:value chosen)))]
+                                (when-not (string/blank? page*)
+                                  (p/let [id (<create-page-if-not-exists! property classes' page*)]
+                                    (when id
+                                      (add-or-remove-property-value block property id selected?))))))}))]
     (select-aux block property opts')))
     (select-aux block property opts')))
 
 
 (defn property-value-select-page
 (defn property-value-select-page
-  [block property
-   {:keys [on-chosen] :as opts}
+  [block property opts
    {:keys [*show-new-property-config?]}]
    {:keys [*show-new-property-config?]}]
   (let [input-opts (fn [_]
   (let [input-opts (fn [_]
                      {:on-blur (fn []
                      {:on-blur (fn []
@@ -343,11 +351,7 @@
                           nil))})
                           nil))})
         opts' (assoc opts
         opts' (assoc opts
                      :block block
                      :block block
-                     :input-opts input-opts
-                     :on-chosen (fn [values]
-                                  (p/do!
-                                   (<add-property! block (:db/ident property) values)
-                                   (when on-chosen (on-chosen)))))]
+                     :input-opts input-opts)]
     (select-page property opts')))
     (select-page property opts')))
 
 
 (defn <create-new-block!
 (defn <create-new-block!
@@ -407,11 +411,9 @@
                                           (assoc m :label label)))) items)
                                           (assoc m :label label)))) items)
                          items)
                          items)
                        (remove nil?))
                        (remove nil?))
-            add-property-f #(<add-property! block (:db/ident property) %)
-            on-chosen (fn [chosen]
-                        (p/do!
-                         (add-property-f (if (map? chosen) (:value chosen) chosen))
-                         (when-let [f (:on-chosen select-opts)] (f))))
+            on-chosen (fn [chosen selected?]
+                        (let [value (if (map? chosen) (:value chosen) chosen)]
+                          (add-or-remove-property-value block property value selected?)))
             selected-choices' (get block (:db/ident property))
             selected-choices' (get block (:db/ident property))
             selected-choices (if (coll? selected-choices')
             selected-choices (if (coll? selected-choices')
                                (->> selected-choices'
                                (->> selected-choices'
@@ -428,6 +430,7 @@
                       :input-default-placeholder "Select"
                       :input-default-placeholder "Select"
                       :extract-chosen-fn :value
                       :extract-chosen-fn :value
                       :content-props content-props
                       :content-props content-props
+                      :on-chosen on-chosen
                       :input-opts (fn [_]
                       :input-opts (fn [_]
                                     {:on-blur (fn []
                                     {:on-blur (fn []
                                                 (exit-edit-property)
                                                 (exit-edit-property)
@@ -444,11 +447,7 @@
                                            (when-let [f (:on-chosen select-opts)] (f)))
                                            (when-let [f (:on-chosen select-opts)] (f)))
                                          nil))})}
                                          nil))})}
                       closed-values?
                       closed-values?
-                      (assoc :extract-fn :label)
-                      multiple-choices?
-                      (assoc :on-apply on-chosen)
-                      (not multiple-choices?)
-                      (assoc :on-chosen on-chosen)))))))
+                      (assoc :extract-fn :label)))))))
 
 
 (rum/defc property-normal-block-value < rum/reactive db-mixins/query
 (rum/defc property-normal-block-value < rum/reactive db-mixins/query
   {:init (fn [state]
   {:init (fn [state]
@@ -590,11 +589,9 @@
   (let [[open? set-open!] (rum/use-state editing?)
   (let [[open? set-open!] (rum/use-state editing?)
         schema (:block/schema property)
         schema (:block/schema property)
         type (get schema :type :default)
         type (get schema :type :default)
-        select-opts' (cond-> (assoc select-opts
-                                    :multiple-choices? false
-                                    :on-chosen #(set-open! false))
-                       (= type :page)
-                       (assoc :classes (:classes schema)))]
+        select-opts' (assoc select-opts
+                            :multiple-choices? false
+                            :on-chosen #(set-open! false))]
     (shui/dropdown-menu
     (shui/dropdown-menu
      {:open open?}
      {:open open?}
      (shui/dropdown-menu-trigger
      (shui/dropdown-menu-trigger
@@ -658,10 +655,8 @@
   [:div.flex.flex-1
   [:div.flex.flex-1
    (case (:type schema)
    (case (:type schema)
      :template
      :template
-     (let [id (first (:classes schema))
-           template (when id (db/entity [:block/uuid id]))]
-       (when template
-         (<create-new-block-from-template! block property template)))
+     (when-let [template (first (:property/schema.classes property))]
+       (<create-new-block-from-template! block property template))
      :string
      :string
      (let [repo (state/get-current-repo)
      (let [repo (state/get-current-repo)
            config {:editor-opts (new-text-editor-opts repo block property value editor-id)}]
            config {:editor-opts (new-text-editor-opts repo block property value editor-id)}]
@@ -706,14 +701,12 @@
                   (when (= type :string)
                   (when (= type :string)
                     (set-editing! block property editor-id dom-id value opts)))}
                     (set-editing! block property editor-id dom-id value opts)))}
      (if (and (string/blank? value) template?)
      (if (and (string/blank? value) template?)
-       (let [id (first (:classes schema))
-             template (when id (db/entity [:block/uuid id]))]
-         (when template
-           [:a.fade-link.pointer.text-sm.jtrigger
-            {:on-click (fn [e]
-                         (util/stop e)
-                         (<create-new-block-from-template! block property template))}
-            (str "Use template #" (:block/original-name template))]))
+       (when-let [template (first (:property/schema.classes schema))]
+         [:a.fade-link.pointer.text-sm.jtrigger
+          {:on-click (fn [e]
+                       (util/stop e)
+                       (<create-new-block-from-template! block property template))}
+          (str "Use template #" (:block/original-name template))])
        (cond
        (cond
          (= type :template)
          (= type :template)
          (property-template-value {:editor-id editor-id}
          (property-template-value {:editor-id editor-id}
@@ -786,7 +779,7 @@
     (if (= type :default)
     (if (= type :default)
       [:div.property-block-container.content
       [:div.property-block-container.content
        (block-cp (sort-by :block/order v) {:editor-box editor-box
        (block-cp (sort-by :block/order v) {:editor-box editor-box
-                                          :id (str (:block/uuid block))})]
+                                           :id (str (:block/uuid block))})]
       (let [values-cp (fn [toggle-fn]
       (let [values-cp (fn [toggle-fn]
                         (if (seq items)
                         (if (seq items)
                           (concat
                           (concat
@@ -805,8 +798,7 @@
                           [:div.property-select (cond-> {} editing? (assoc :class "h-6"))
                           [:div.property-select (cond-> {} editing? (assoc :class "h-6"))
                            (if (= :page type)
                            (if (= :page type)
                              (property-value-select-page block property
                              (property-value-select-page block property
-                                                         (assoc select-opts
-                                                                :classes (:classes schema))
+                                                         select-opts
                                                          opts)
                                                          opts)
                              (select block property select-opts opts))]))]
                              (select block property select-opts opts))]))]
       ;; why this?
       ;; why this?

+ 11 - 8
src/main/frontend/components/select.cljs

@@ -129,24 +129,27 @@
                                                    (let [chosen (extract-chosen-fn raw-chosen)]
                                                    (let [chosen (extract-chosen-fn raw-chosen)]
                                                      (if multiple-choices?
                                                      (if multiple-choices?
                                                        (if (selected-choices chosen)
                                                        (if (selected-choices chosen)
-                                                         (swap! *selected-choices disj chosen)
-                                                         (swap! *selected-choices conj chosen))
+                                                         (do
+                                                           (swap! *selected-choices disj chosen)
+                                                           (when on-chosen (on-chosen chosen false)))
+                                                         (do
+                                                           (swap! *selected-choices conj chosen)
+                                                           (when on-chosen (on-chosen chosen true))))
                                                        (do
                                                        (do
                                                          (when (and close-modal? (not multiple-choices?))
                                                          (when (and close-modal? (not multiple-choices?))
                                                            (state/close-modal!))
                                                            (state/close-modal!))
                                                          (when on-chosen
                                                          (when on-chosen
-                                                           (on-chosen (if multiple-choices? selected-choices chosen)))))))
+                                                           (on-chosen chosen true))))))
                               :empty-placeholder (empty-placeholder t)})]
                               :empty-placeholder (empty-placeholder t)})]
 
 
-                           (when multiple-choices?
+                           (when (and multiple-choices? (fn? on-apply))
                              [:div.p-4 (ui/button "Apply"
                              [:div.p-4 (ui/button "Apply"
                                                   {:small? true
                                                   {:small? true
                                                    :on-pointer-down (fn [e]
                                                    :on-pointer-down (fn [e]
-                                                                    (util/stop e)
-                                                                    (when @*toggle (@*toggle))
-                                                                    (when (fn? on-apply)
+                                                                      (util/stop e)
+                                                                      (when @*toggle (@*toggle))
                                                                       (on-apply selected-choices)
                                                                       (on-apply selected-choices)
-                                                                      (when close-modal? (state/close-modal!))))})])]]
+                                                                      (when close-modal? (state/close-modal!)))})])]]
     (when (fn? tap-*input-val)
     (when (fn? tap-*input-val)
       (tap-*input-val input))
       (tap-*input-val input))
     [:div.cp__select
     [:div.cp__select

+ 1 - 2
src/main/frontend/db/model.cljs

@@ -801,8 +801,7 @@ independent of format as format specific heading characters are stripped"
   (d/q
   (d/q
    '[:find ?name ?id
    '[:find ?name ?id
      :where
      :where
-     [?page :block/type ?t]
-     [(= ?t "class")]
+     [?page :block/type "class"]
      [?page :block/original-name ?name]
      [?page :block/original-name ?name]
      [?page :block/uuid ?id]]
      [?page :block/uuid ?id]]
     (conn/get-db repo)))
     (conn/get-db repo)))