Просмотр исходного кода

refactor: set parent for any page, not just tags (#11494)

* refactor: replace :class/parent with :logseq.property/parent

* replace :class/schema.properties with property.class/properties

* enhance: display schema properties directly in Tag properties

* tag properties style tweaks

* fix: properties view context

* fix: limit parent to be classes or pages depends on the current node

* enhance: display ancestors in Parent

* fix: ancestors

* enhance: display tag properties always for tags

* enhance: display tag block container instead of properties area

* enhance: show add property as popup instead of dialog

* fix: tag parent must be a tag

* enhance: display properties in the right sidebar

* fix: tags style

* fix: icon style

* fix: positioned icons

* fix: lint

* fix: tests

* fix: remove properties from rtc watched attrs

* fix: graph-parser tests

* enhance: style tweaks

* enhance: page/create parses tags from user-input title

* enhance(ux): [[ref]] followed by # (no whitespace) to add tags

* fix: tests

* fix: graph-parser lint and tests

* fix: allow tagged nodes to be parents

* enhance(ux): display tag property descriptions

Also, make it editable.
Tienson Qin 1 год назад
Родитель
Сommit
c7677c813e
49 измененных файлов с 720 добавлено и 740 удалено
  1. 4 4
      deps/db/src/logseq/db.cljs
  2. 3 3
      deps/db/src/logseq/db/frontend/malli_schema.cljs
  3. 11 3
      deps/db/src/logseq/db/frontend/property.cljs
  4. 59 7
      deps/db/src/logseq/db/frontend/property/type.cljs
  5. 6 6
      deps/db/src/logseq/db/frontend/rules.cljc
  6. 1 7
      deps/db/src/logseq/db/frontend/schema.cljs
  7. 2 2
      deps/db/src/logseq/db/sqlite/build.cljs
  8. 1 1
      deps/db/src/logseq/db/sqlite/create_graph.cljs
  9. 8 33
      deps/db/src/logseq/db/sqlite/util.cljs
  10. 2 2
      deps/db/test/logseq/db/sqlite/create_graph_test.cljs
  11. 1 0
      deps/graph-parser/src/logseq/graph_parser/block.cljs
  12. 43 34
      deps/graph-parser/src/logseq/graph_parser/exporter.cljs
  13. 1 1
      deps/graph-parser/src/logseq/graph_parser/test/docs_graph_helper.cljs
  14. 1 1
      deps/graph-parser/test/logseq/graph_parser/cli_test.cljs
  15. 3 3
      deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs
  16. 5 5
      deps/outliner/src/logseq/outliner/property.cljs
  17. 2 2
      deps/outliner/test/logseq/outliner/property_test.cljs
  18. 7 7
      scripts/src/logseq/tasks/db_graph/create_graph_with_schema_org.cljs
  19. 1 1
      scripts/src/logseq/tasks/dev/db_and_file_graphs.clj
  20. 2 2
      src/main/frontend/common_keywords.cljs
  21. 79 77
      src/main/frontend/components/block.cljs
  22. 26 2
      src/main/frontend/components/block.css
  23. 7 104
      src/main/frontend/components/class.cljs
  24. 17 81
      src/main/frontend/components/db_based/page.cljs
  25. 1 0
      src/main/frontend/components/editor.cljs
  26. 1 1
      src/main/frontend/components/icon.cljs
  27. 0 1
      src/main/frontend/components/objects.cljs
  28. 8 6
      src/main/frontend/components/page.cljs
  29. 1 43
      src/main/frontend/components/page.css
  30. 69 50
      src/main/frontend/components/property.cljs
  31. 3 7
      src/main/frontend/components/property.css
  32. 15 10
      src/main/frontend/components/property/config.cljs
  33. 2 6
      src/main/frontend/components/property/dialog.cljs
  34. 212 160
      src/main/frontend/components/property/value.cljs
  35. 1 1
      src/main/frontend/components/property/value.css
  36. 1 1
      src/main/frontend/db/async.cljs
  37. 5 5
      src/main/frontend/db/model.cljs
  38. 13 3
      src/main/frontend/handler/common/page.cljs
  39. 6 4
      src/main/frontend/handler/editor.cljs
  40. 10 13
      src/main/frontend/handler/events.cljs
  41. 36 27
      src/main/frontend/handler/page.cljs
  42. 31 1
      src/main/frontend/worker/db/migrate.cljs
  43. 4 2
      src/main/frontend/worker/handler/page/db_based/page.cljs
  44. 1 1
      src/main/frontend/worker/rtc/db_listener.cljs
  45. 0 2
      src/main/frontend/worker/rtc/remote_update.cljs
  46. 0 0
      src/rtc_e2e_test/example.cljs
  47. 4 4
      src/test/frontend/db/db_based_model_test.cljs
  48. 1 1
      src/test/frontend/handler/repo_test.cljs
  49. 3 3
      src/test/frontend/worker/rtc/db_listener_test.cljs

+ 4 - 4
deps/db/src/logseq/db.cljs

@@ -46,7 +46,7 @@
     ;; TODO: remove this in later releases
     :block/heading-level
     :block/file
-    :class/parent
+    :logseq.property/parent
     {:block/page [:db/id :block/name :block/title :block/journal-day]}
     {:block/_parent ...}])
 
@@ -399,7 +399,7 @@
 (defn get-classes-with-property
   "Get classes which have given property as a class property"
   [db property-id]
-  (:class/_schema.properties (d/entity db property-id)))
+  (:logseq.property.class/_properties (d/entity db property-id)))
 
 (defn get-alias-source-page
   "return the source page (page-name) of an alias"
@@ -572,14 +572,14 @@
 (defn get-class-parents
   [class]
   (let [*classes (atom #{})]
-    (when-let [parent (:class/parent class)]
+    (when-let [parent (:logseq.property/parent class)]
       (loop [current-parent parent]
         (when (and
                current-parent
                (class? parent)
                (not (contains? @*classes (:db/id parent))))
           (swap! *classes conj (:db/id current-parent))
-          (recur (:class/parent current-parent)))))
+          (recur (:logseq.property/parent current-parent)))))
     @*classes))
 
 (defn get-all-pages-views

+ 3 - 3
deps/db/src/logseq/db/frontend/malli_schema.cljs

@@ -245,8 +245,8 @@
 
 (def class-attrs
   [[:db/ident class-ident]
-   [:class/parent {:optional true} :int]
-   [:class/schema.properties {:optional true} [:set :int]]])
+   [:logseq.property/parent {:optional true} :int]
+   [:logseq.property.class/properties {:optional true} [:set :int]]])
 
 (def class-page
   (vec
@@ -278,7 +278,7 @@
          [:type (apply vector :enum (into db-property-type/internal-built-in-property-types
                                           db-property-type/user-built-in-property-types))]
          [:public? {:optional true} :boolean]
-         [:view-context {:optional true} [:enum :page :block]]
+         [:view-context {:optional true} [:enum :page :block :class]]
          [:shortcut {:optional true} :string]]
         property-common-schema-attrs))]]
     property-attrs

+ 11 - 3
deps/db/src/logseq/db/frontend/property.cljs

@@ -38,8 +38,16 @@
                                    :cardinality :many
                                    :public? true
                                    :classes #{:logseq.class/Root}}}
+   :logseq.property/parent {:title "Parent"
+                            :schema {:type :node
+                                     :public? true}}
+   :logseq.property.class/properties {:title "Tag properties"
+                                      :schema {:type :property
+                                               :cardinality :many
+                                               :public? true
+                                               :view-context :class}}
    :logseq.property/page-tags {:title "pageTags"
-                               :schema {:type :node
+                               :schema {:type :page
                                         :public? true
                                         :view-context :page
                                         :cardinality :many}}
@@ -223,7 +231,7 @@
 (def logseq-property-namespaces
   #{"logseq.property" "logseq.property.tldraw" "logseq.property.pdf" "logseq.task"
     "logseq.property.linked-references" "logseq.property.asset" "logseq.property.table"
-    "logseq.property.journal"})
+    "logseq.property.journal" "logseq.property.class"})
 
 (defn logseq-property?
   "Determines if keyword is a logseq property"
@@ -314,7 +322,7 @@
 
 (defn get-class-ordered-properties
   [class-entity]
-  (->> (:class/schema.properties class-entity)
+  (->> (:logseq.property.class/properties class-entity)
        (sort-by :block/order)))
 
 (defn property-created-block?

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

@@ -3,7 +3,9 @@
   fns and their allowed schema attributes"
   (:require [datascript.core :as d]
             [clojure.set :as set]
-            [logseq.common.util.macro :as macro-util]))
+            [logseq.common.util.macro :as macro-util]
+            [clojure.string :as string]
+            [logseq.common.config :as common-config]))
 
 ;; Config vars
 ;; ===========
@@ -12,7 +14,7 @@
 
 (def internal-built-in-property-types
   "Valid property types only for use by internal built-in-properties"
-  #{:keyword :map :coll :any :entity :string :raw-number})
+  #{:keyword :map :coll :any :entity :class :page :property :string :raw-number})
 
 (def user-built-in-property-types
   "Valid property types for users in order they appear in the UI"
@@ -45,13 +47,13 @@
         original-value-ref-property-types))
 
 (def ref-property-types
-  "User facing ref types. Property values that users see are stored in either
+  "Ref types. Property values that users see are stored in either
   :property.value/content, :block/title.
   :block/title is for all the page related types"
-  (into #{:date :node} value-ref-property-types))
+  (into #{:date :node :entity :class :page :property} value-ref-property-types))
 
 (assert (set/subset? ref-property-types
-                     (set user-built-in-property-types))
+                     (set/union (set user-built-in-property-types) internal-built-in-property-types))
         "All ref types are valid property types")
 
 
@@ -87,6 +89,53 @@
   [db id]
   (some? (d/entity db id)))
 
+(defn page?
+  [block]
+  (contains? #{"page" "journal" "whiteboard" "class" "property" "hidden"}
+             (:block/type block)))
+
+(defn class?
+  [entity]
+  (= (:block/type entity) "class"))
+
+(defn property?
+  [entity]
+  (= (:block/type entity) "property"))
+
+(defn closed-value?
+  [entity]
+  (= (:block/type entity) "closed value"))
+
+(defn whiteboard?
+  "Given a page entity or map, check if it is a whiteboard page"
+  [page]
+  (= (:block/type page) "whiteboard"))
+
+(defn journal?
+  "Given a page entity or map, check if it is a journal page"
+  [page]
+  (= (:block/type page) "journal"))
+
+(defn hidden?
+  [page]
+  (when page
+    (if (string? page)
+      (or (string/starts-with? page "$$$")
+          (= common-config/favorites-page-name page))
+      (= (:block/type page) "hidden"))))
+
+(defn- class-entity?
+  [db id]
+  (class? (d/entity db id)))
+
+(defn- property-entity?
+  [db id]
+  (property? (d/entity db id)))
+
+(defn- page-entity?
+  [db id]
+  (page? (d/entity db id)))
+
 (defn- number-entity?
   [db id-or-value {:keys [new-closed-value?]}]
   (if new-closed-value?
@@ -138,6 +187,9 @@
    :string   string?
    :raw-number number?
    :entity   entity?
+   :class    class-entity?
+   :property property-entity?
+   :page     page-entity?
    :keyword  keyword?
    :map      map?
    ;; coll elements are ordered as it's saved as a vec
@@ -151,7 +203,7 @@
 
 (def property-types-with-db
   "Property types whose validation fn requires a datascript db"
-  #{:default :url :number :date :node :entity})
+  #{:default :url :number :date :node :entity :class :property :page})
 
 ;; Helper fns
 ;; ==========
@@ -162,4 +214,4 @@
     (number? val) :number
     (url? val) :url
     (contains? #{true false} val) :checkbox
-    :else :default))
+    :else :default))

+ 6 - 6
deps/db/src/logseq/db/frontend/rules.cljc

@@ -11,12 +11,12 @@
       [?t :block/namespace ?p]
       (namespace ?t ?c)]]
 
-   :class-parent
-   '[[(class-parent ?p ?c)
-      [?c :class/parent ?p]]
-     [(class-parent ?p ?c)
-      [?t :class/parent ?p]
-      (class-parent ?t ?c)]]
+   :parent
+   '[[(parent ?p ?c)
+      [?c :logseq.property/parent ?p]]
+     [(parent ?p ?c)
+      [?t :logseq.property/parent ?p]
+      (parent ?t ?c)]]
 
    :alias
    '[[(alias ?e2 ?e1)

+ 1 - 7
deps/db/src/logseq/db/frontend/schema.cljs

@@ -2,7 +2,7 @@
   "Main datascript schemas for the Logseq app"
   (:require [clojure.set :as set]))
 
-(def version 13)
+(def version 15)
 ;; A page is a special block, a page can corresponds to multiple files with the same ":block/name".
 (def ^:large-vars/data-var schema
   {:db/ident        {:db/unique :db.unique/identity}
@@ -113,12 +113,6 @@
            :block/properties :block/properties-order :block/repeated? :block/deadline :block/scheduled :block/priority
            :block/marker :block/macros)
    {:block/name {:db/index true}        ; remove db/unique for :block/name
-    ;; class properties
-    :class/parent {:db/valueType :db.type/ref
-                   :db/index true}
-    :class/schema.properties {:db/valueType :db.type/ref
-                              :db/cardinality :db.cardinality/many
-                              :db/index true}
     ;; closed value
     :block/closed-value-property {:db/valueType :db.type/ref
                                   :db/cardinality :db.cardinality/many}

+ 2 - 2
deps/db/src/logseq/db/sqlite/build.cljs

@@ -205,11 +205,11 @@
                              (when-let [props (not-empty (:build/properties class-m))]
                                (->block-properties (merge props (db-property-build/build-properties-with-ref-values pvalue-tx-m)) uuid-maps all-idents))
                              (when class-parent
-                               {:class/parent
+                               {:logseq.property/parent
                                 (or (class-db-ids class-parent)
                                     (throw (ex-info (str "No :db/id for " class-parent) {})))})
                              (when schema-properties
-                               {:class/schema.properties
+                               {:logseq.property.class/properties
                                 (mapv #(hash-map :db/ident (get-ident all-idents %))
                                       schema-properties)}))))))
                      classes))]

+ 1 - 1
deps/db/src/logseq/db/sqlite/create_graph.cljs

@@ -99,7 +99,7 @@
              :db/ident db-ident
              :block/uuid (common-uuid/gen-uuid :db-ident-block-uuid db-ident)}
              (seq schema-properties)
-             (assoc :class/schema.properties schema-properties)
+             (assoc :logseq.property.class/properties schema-properties)
              (seq properties)
              (merge properties)))))))
    built-in-classes))

+ 8 - 33
deps/db/src/logseq/db/sqlite/util.cljs

@@ -8,7 +8,6 @@
             [datascript.transit :as dt]
             [logseq.common.util :as common-util]
             [logseq.common.uuid :as common-uuid]
-            [logseq.common.config :as common-config]
             [logseq.db.frontend.order :as db-order]
             [logseq.db.frontend.property :as db-property]
             [logseq.db.frontend.property.type :as db-property-type]
@@ -117,7 +116,7 @@
                   {:block/type "class"
                    :block/format :markdown})
      (not= (:db/ident block) :logseq.class/Root)
-     (assoc :class/parent :logseq.class/Root))))
+     (assoc :logseq.property/parent :logseq.class/Root))))
 
 (defn build-new-page
   "Builds a basic page to be transacted. A minimal version of gp-block/page-name->map"
@@ -129,34 +128,10 @@
     :block/format :markdown
     :block/type "page"}))
 
-(defn page?
-  [block]
-  (contains? #{"page" "journal" "whiteboard" "class" "property" "hidden"}
-             (:block/type block)))
-
-(defn class?
-  [entity]
-  (= (:block/type entity) "class"))
-(defn property?
-  [entity]
-  (= (:block/type entity) "property"))
-(defn closed-value?
-  [entity]
-  (= (:block/type entity) "closed value"))
-(defn whiteboard?
-  "Given a page entity or map, check if it is a whiteboard page"
-  [page]
-  (= (:block/type page) "whiteboard"))
-
-(defn journal?
-  "Given a page entity or map, check if it is a journal page"
-  [page]
-  (= (:block/type page) "journal"))
-
-(defn hidden?
-  [page]
-  (when page
-    (if (string? page)
-      (or (string/starts-with? page "$$$")
-          (= common-config/favorites-page-name page))
-      (= (:block/type page) "hidden"))))
+(def page? db-property-type/page?)
+(def class? db-property-type/class?)
+(def property? db-property-type/property?)
+(def closed-value? db-property-type/closed-value?)
+(def whiteboard? db-property-type/whiteboard?)
+(def journal? db-property-type/journal?)
+(def hidden? db-property-type/hidden?)

+ 2 - 2
deps/db/test/logseq/db/sqlite/create_graph_test.cljs

@@ -63,9 +63,9 @@
         task (d/entity @conn :logseq.class/Task)]
     (is (ldb/class? task)
         "Task class has correct type")
-    (is (= 3 (count (:class/schema.properties task)))
+    (is (= 3 (count (:logseq.property.class/properties task)))
         "Has correct number of task properties")
-    (is (every? ldb/property? (:class/schema.properties task))
+    (is (every? ldb/property? (:logseq.property.class/properties task))
         "Each task property has correct type")))
 
 (deftest new-graph-is-valid

+ 1 - 0
deps/graph-parser/src/logseq/graph_parser/block.cljs

@@ -310,6 +310,7 @@
   (when-not (and db (common-util/uuid-string? original-page-name)
                  (not (ldb/page? (d/entity db [:block/uuid (uuid original-page-name)]))))
     (let [db-based? (ldb/db-based-graph? db)
+          original-page-name (string/trim original-page-name)
           [page _page-entity] (cond
                                (and original-page-name (string? original-page-name))
                                (let [original-page-name (common-util/remove-boundary-slashes original-page-name)

+ 43 - 34
deps/graph-parser/src/logseq/graph_parser/exporter.cljs

@@ -554,7 +554,7 @@
               (seq parent-classes-from-properties)
               (merge (find-or-create-class db (:block/title block) (:all-idents import-state)))
               (seq parent-classes-from-properties)
-              (assoc :class/parent
+              (assoc :logseq.property/parent
                      (let [new-class (first parent-classes-from-properties)
                            class-m (find-or-create-class db new-class (:all-idents import-state))]
                        (when (> (count parent-classes-from-properties) 1)
@@ -670,6 +670,14 @@
       (dissoc :block/whiteboard?)
       (update-page-tags db tag-classes page-names-to-uuids all-idents)))
 
+(defn- user-parent->built-in-property
+  [pages]
+  (map (fn [p]
+         (-> (if (set? (:user.property/parent p))
+               (assoc p :logseq.property/parent (first (:user.property/parent p)))
+               p)
+             (dissoc :user.property/parent))) pages))
+
 (defn- build-pages-tx
   "Given all the pages and blocks parsed from a file, return a map containing
   all non-whiteboard pages to be transacted, pages' properties and additional
@@ -702,41 +710,43 @@
                                    (into {} (map (juxt :block/name :block/uuid) new-pages)))
         all-pages-m (mapv #(handle-page-properties % @conn page-names-to-uuids all-pages options)
                           all-pages)
-        pages-tx (keep (fn [m]
-                         (if-let [page-uuid (existing-page-names-to-uuids (:block/name m))]
-                           (let [;; These attributes are not allowed to be transacted because they must not change across files
-                                 disallowed-attributes [:block/name :block/uuid :block/format :block/title :block/journal-day
-                                                        :block/created-at :block/updated-at]
-                                 allowed-attributes (into [:block/tags :block/alias :class/parent :block/type :db/ident]
-                                                          (keep #(when (db-malli-schema/user-property? (key %)) (key %))
-                                                                m))
-                                 block-changes (cond-> (select-keys m allowed-attributes)
+        pages-tx (->>
+                  (keep (fn [m]
+                          (if-let [page-uuid (existing-page-names-to-uuids (:block/name m))]
+                            (let [;; These attributes are not allowed to be transacted because they must not change across files
+                                  disallowed-attributes [:block/name :block/uuid :block/format :block/title :block/journal-day
+                                                         :block/created-at :block/updated-at]
+                                  allowed-attributes (into [:block/tags :block/alias :logseq.property/parent :block/type :db/ident]
+                                                           (keep #(when (db-malli-schema/user-property? (key %)) (key %))
+                                                                 m))
+                                  block-changes (cond-> (select-keys m allowed-attributes)
                                                  ;; disallow any type -> "page" but do allow any conversion to a non-page type
-                                                 (= (:block/type m) "page")
-                                                 (dissoc :block/type))]
-                             (when-let [ignored-attrs (not-empty (apply dissoc m (into disallowed-attributes allowed-attributes)))]
-                               (notify-user {:msg (str "Import ignored the following attributes on page " (pr-str (:block/title m)) ": "
-                                                       ignored-attrs)}))
-                             (when (seq block-changes)
-                               (cond-> (merge block-changes {:block/uuid page-uuid})
-                                 (seq (:block/alias m))
-                                 (update-page-alias page-names-to-uuids)
-                                 (:block/tags m)
-                                 (update-page-tags @conn tag-classes page-names-to-uuids (:all-idents import-state)))))
-
-                           (when (or (= "class" (:block/type m))
+                                                  (= (:block/type m) "page")
+                                                  (dissoc :block/type))]
+                              (when-let [ignored-attrs (not-empty (apply dissoc m (into disallowed-attributes allowed-attributes)))]
+                                (notify-user {:msg (str "Import ignored the following attributes on page " (pr-str (:block/title m)) ": "
+                                                        ignored-attrs)}))
+                              (when (seq block-changes)
+                                (cond-> (merge block-changes {:block/uuid page-uuid})
+                                  (seq (:block/alias m))
+                                  (update-page-alias page-names-to-uuids)
+                                  (:block/tags m)
+                                  (update-page-tags @conn tag-classes page-names-to-uuids (:all-idents import-state)))))
+
+                            (when (or (= "class" (:block/type m))
                                      ;; Don't build a new page if it overwrites an existing class
-                                     (not (some-> (get @(:all-idents import-state) (keyword (:block/title m)))
-                                                  db-malli-schema/class?)))
-                             (let [m' (if (contains? all-built-in-names (keyword (:block/name m)))
+                                      (not (some-> (get @(:all-idents import-state) (keyword (:block/title m)))
+                                                   db-malli-schema/class?)))
+                              (let [m' (if (contains? all-built-in-names (keyword (:block/name m)))
                                         ;; Use fixed uuid from above
-                                        (cond-> (assoc m :block/uuid (get page-names-to-uuids (:block/name m)))
+                                         (cond-> (assoc m :block/uuid (get page-names-to-uuids (:block/name m)))
                                           ;; only happens for few file built-ins like tags and alias
-                                          (not (:block/type m))
-                                          (assoc :block/type "page"))
-                                        m)]
-                               (build-new-page-or-class m' @conn tag-classes page-names-to-uuids (:all-idents import-state))))))
-                       (map :block all-pages-m))]
+                                           (not (:block/type m))
+                                           (assoc :block/type "page"))
+                                         m)]
+                                (build-new-page-or-class m' @conn tag-classes page-names-to-uuids (:all-idents import-state))))))
+                        (map :block all-pages-m))
+                  user-parent->built-in-property)]
     {:pages-tx pages-tx
      :page-properties-tx (mapcat :properties-tx all-pages-m)
      :existing-pages existing-page-names-to-uuids
@@ -935,7 +945,6 @@
                        (mapcat #(build-block-tx @conn % pre-blocks page-names-to-uuids
                                                 (assoc tx-options :whiteboard? (some? (seq whiteboard-pages)))))
                        vec)
-
         {:keys [property-pages-tx property-page-properties-tx] pages-tx' :pages-tx}
         (split-pages-and-properties-tx pages-tx old-properties existing-pages (:import-state options))
         ;; Necessary to transact new property entities first so that block+page properties can be transacted next
@@ -1076,7 +1085,7 @@
                      {}))
         tx (mapv (fn [[class-id prop-ids]]
                    {:db/id class-id
-                    :class/schema.properties (vec prop-ids)})
+                    :logseq.property.class/properties (vec prop-ids)})
                  class-to-prop-uuids)]
     (ldb/transact! repo-or-conn tx)))
 

+ 1 - 1
deps/graph-parser/src/logseq/graph_parser/test/docs_graph_helper.cljs

@@ -106,7 +106,7 @@
                 (into {})))
         "Task marker counts")
 
-    (is (= {:markdown 7325 :org 500} (get-block-format-counts db))
+    (is (= {:markdown 7322 :org 500} (get-block-format-counts db))
         "Block format counts")
 
     (is (= {:rangeincludes 13, :description 137, :updated-at 46, :tags 5,

+ 1 - 1
deps/graph-parser/test/logseq/graph_parser/cli_test.cljs

@@ -14,7 +14,7 @@
     (docs-graph-helper/docs-graph-assertions @conn graph-dir files)
 
     (testing "Additional counts"
-      (is (= 57882 (count (d/datoms @conn :eavt))) "Correct datoms count"))
+      (is (= 57860 (count (d/datoms @conn :eavt))) "Correct datoms count"))
 
     (testing "Asts"
       (is (seq asts) "Asts returned are non-zero")

+ 3 - 3
deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs

@@ -450,7 +450,7 @@
 
     (is (= #{:user.property/url :user.property/sameas :user.property/rangeincludes}
            (->> (d/entity @conn :user.class/Property)
-                :class/schema.properties
+                :logseq.property.class/properties
                 (map :db/ident)
                 set))
         "Properties are correctly inferred for a class")
@@ -502,7 +502,7 @@
                 set))
         "All classes are correctly defined by :type")
 
-    (is (= "CreativeWork" (get-in (d/entity @conn :user.class/Movie) [:class/parent :block/title]))
+    (is (= "CreativeWork" (get-in (d/entity @conn :user.class/Movie) [:logseq.property/parent :block/title]))
         "Existing page correctly set as class parent")
-    (is (= "Thing" (get-in (d/entity @conn :user.class/CreativeWork) [:class/parent :block/title]))
+    (is (= "Thing" (get-in (d/entity @conn :user.class/CreativeWork) [:logseq.property/parent :block/title]))
         "New page correctly set as class parent")))

+ 5 - 5
deps/outliner/src/logseq/outliner/property.cljs

@@ -346,7 +346,7 @@
   [db class]
   (let [class-parents (get-class-parents db [class])]
     (->> (mapcat (fn [class]
-                   (:class/schema.properties class)) (concat [class] class-parents))
+                   (:logseq.property.class/properties class)) (concat [class] class-parents))
          (common-util/distinct-by :db/id)
          (ldb/sort-by-order))))
 
@@ -359,9 +359,9 @@
         class-parents (get-class-parents db classes)
         all-classes (->> (concat classes class-parents)
                          (filter (fn [class]
-                                   (seq (:class/schema.properties class)))))
+                                   (seq (:logseq.property.class/properties class)))))
         all-properties (-> (mapcat (fn [class]
-                                     (map :db/ident (:class/schema.properties class))) all-classes)
+                                     (map :db/ident (:logseq.property.class/properties class))) all-classes)
                            distinct)]
     {:classes classes
      :all-classes all-classes           ; block own classes + parent classes
@@ -521,7 +521,7 @@
   (when-let [class (d/entity @conn class-id)]
     (if (ldb/class? class)
       (ldb/transact! conn
-                     [[:db/add (:db/id class) :class/schema.properties property-id]]
+                     [[:db/add (:db/id class) :logseq.property.class/properties property-id]]
                      {:outliner-op :save-block})
       (throw (ex-info "Can't add a property to a block that isn't a class"
                       {:class-id class-id :property-id property-id})))))
@@ -532,5 +532,5 @@
     (when (ldb/class? class)
       (when-let [property (d/entity @conn property-id)]
         (when-not (ldb/built-in-class-property? class property)
-          (ldb/transact! conn [[:db/retract (:db/id class) :class/schema.properties property-id]]
+          (ldb/transact! conn [[:db/retract (:db/id class) :logseq.property.class/properties property-id]]
                          {:outliner-op :save-block}))))))

+ 2 - 2
deps/outliner/test/logseq/outliner/property_test.cljs

@@ -280,14 +280,14 @@
         _ (outliner-property/class-add-property! conn :user.class/c1 :user.property/p1)
         _ (outliner-property/class-add-property! conn :user.class/c1 :user.property/p2)]
     (is (= [:user.property/p1 :user.property/p2]
-           (map :db/ident (:class/schema.properties (d/entity @conn :user.class/c1)))))))
+           (map :db/ident (:logseq.property.class/properties (d/entity @conn :user.class/c1)))))))
 
 (deftest class-remove-property!
   (let [conn (create-conn-with-blocks
               {:classes {:c1 {:build/schema-properties [:p1 :p2]}}})
         _ (outliner-property/class-remove-property! conn :user.class/c1 :user.property/p1)]
     (is (= [:user.property/p2]
-           (map :db/ident (:class/schema.properties (d/entity @conn :user.class/c1)))))))
+           (map :db/ident (:logseq.property.class/properties (d/entity @conn :user.class/c1)))))))
 
 (deftest get-block-classes-properties
   (let [conn (create-conn-with-blocks

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

@@ -363,9 +363,9 @@
 (defn- write-debug-file [db]
   (let [ents (remove #(db-malli-schema/internal-ident? (:db/ident %))
                      (d/q '[:find [(pull ?b [*
-                                             {:class/schema.properties [:block/title]}
+                                             {:logseq.property.class/properties [:block/title]}
                                              {:property/schema.classes [:block/title]}
-                                             {:class/parent [:block/title]}
+                                             {:logseq.property/parent [:block/title]}
                                              {:block/refs [:block/title]}]) ...]
                             :in $
                             :where [?b :db/ident ?ident]]
@@ -376,7 +376,7 @@
                             (map (fn [m]
                                    (let [props (db-property/properties m)]
                                      (cond-> (select-keys m [:block/name :block/type :block/title :block/schema :db/ident
-                                                             :class/schema.properties :class/parent
+                                                             :logseq.property.class/properties :logseq.property/parent
                                                              :db/cardinality :property/schema.classes :block/refs])
                                        (seq props)
                                        (assoc :block/properties (-> (update-keys props name)
@@ -384,10 +384,10 @@
                                                                                    (if (:db/id v)
                                                                                      (db-property/property-value-content (d/entity db (:db/id v)))
                                                                                      v)))))
-                                       (seq (:class/schema.properties m))
-                                       (update :class/schema.properties #(set (map :block/title %)))
-                                       (some? (:class/parent m))
-                                       (update :class/parent :block/title)
+                                       (seq (:logseq.property.class/properties m))
+                                       (update :logseq.property.class/properties #(set (map :block/title %)))
+                                       (some? (:logseq.property/parent m))
+                                       (update :logseq.property/parent :block/title)
                                        (seq (:property/schema.classes m))
                                        (update :property/schema.classes #(set (map :block/title %)))
                                        (seq (:block/refs m))

+ 1 - 1
scripts/src/logseq/tasks/dev/db_and_file_graphs.clj

@@ -111,7 +111,7 @@
   []
   (let [db-concepts
         ;; from logseq.db.frontend.schema
-        ["closed-value" "schema.properties" "schema.classes" "class/parent"]
+        ["closed-value" "class/properties" "schema.classes" "property/parent"]
         res (apply shell {:out :string :continue true}
                    "git grep -E" (str "(" (string/join "|" db-concepts) ")")
                    file-graph-paths)]

+ 2 - 2
src/main/frontend/common_keywords.cljs

@@ -38,10 +38,10 @@ but when eval `(:block/raw-title block-entity)`, return raw title of this block"
   "Used to store key-value, the value could be anything, e.g. {:db/ident :logseq.kv/xxx :kv/value value}"
   :any)
 
-(sr/defkeyword :class/parent
+(sr/defkeyword :logseq.property/parent
   "A class's parent class")
 
-(sr/defkeyword :class/schema.properties
+(sr/defkeyword :logseq.property.class/properties
   "Class properties that all of its objects can use, notice that it's different from this class's own properties.")
 
 (sr/defkeyword :block/closed-value-property

+ 79 - 77
src/main/frontend/components/block.cljs

@@ -2188,15 +2188,14 @@
            (let [container-id (or (:container-id (first (:rum/args state)))
                                   (state/get-next-container-id))]
              (assoc state ::initial-container-id container-id)))}
-  [state config block _edit-input-id opts]
+  [state config block opts]
   (property-component/properties-area block
                                       (merge
                                        config
                                        {:inline-text inline-text
                                         :page-cp page-cp
                                         :block-cp blocks-container
-                                        :properties-cp db-properties-cp
-                                        :editor-box (get config :editor-box)
+                                        :editor-box (state/get-component :editor/box)
                                         :container-id (or (:container-id config)
                                                           (::initial-container-id state))
                                         :id (:id config)}
@@ -2400,8 +2399,9 @@
            [:div.block-tag.text-sm
             {:key (str "tag-" (:db/id tag))}
             (page-cp (assoc config
-                              :tag? true
-                              :disable-preview? true) tag)])]))))
+                            :hide-icon? true
+                            :tag? true
+                            :disable-preview? true) tag)])]))))
 
 (rum/defc block-positioned-properties
   [config block position]
@@ -2425,7 +2425,6 @@
                 [:div.select-none ":"]]
                (pv/property-value block property v opts)]))]
          [:div.positioned-properties.right-align.flex.flex-row.gap-1.select-none
-          {:class (if (:page-title? config) "items-center" "items-start")}
           (for [pid properties]
             (when-let [property (db/entity pid)]
               (pv/property-value block property (get block pid) (assoc opts :show-tooltip? true))))]))))
@@ -2596,7 +2595,7 @@
       :on-mouse-leave #(reset! *hover? false)}
      (when (and db-based? (not table?)) (block-positioned-properties config block :block-left))
      [:div.flex.flex-1.flex-col
-      [:div.flex.flex-1.flex-row.gap-1.items-start
+      [:div.flex.flex-1.flex-row.gap-1.items-center
        (if (and edit? editor-box)
          [:div.editor-wrapper.flex.flex-1
           {:id editor-id}
@@ -3075,81 +3074,84 @@
      (when (and top? (not table?))
        (dnd-separator-wrapper block children block-id slide? true false))
 
-     [:div.block-main-container.flex.flex-row.pr-2.gap-1
-      {:data-has-heading (some-> block :block/properties (pu/lookup :logseq.property/heading))
-       :on-touch-start (fn [event uuid] (block-handler/on-touch-start event uuid))
-       :on-touch-move (fn [event]
-                        (block-handler/on-touch-move event block uuid editing? *show-left-menu? *show-right-menu?))
-       :on-touch-end (fn [event]
-                       (block-handler/on-touch-end event block uuid *show-left-menu? *show-right-menu?))
-       :on-touch-cancel (fn [_e]
-                          (block-handler/on-touch-cancel *show-left-menu? *show-right-menu?))
-       :on-mouse-enter (fn [e]
-                         (block-mouse-over e *control-show? block-id doc-mode?))
-       :on-mouse-leave (fn [e]
-                         (block-mouse-leave e *control-show? block-id doc-mode?))}
-
-      (when (and (not slide?) (not in-whiteboard?) (not table?)
-                 (not (:page-title? config)))
-        (let [edit? (or editing?
-                        (= uuid (:block/uuid (state/get-edit-block))))]
-          (block-control config block
-                         {:uuid uuid
-                          :block-id block-id
-                          :collapsed? collapsed?
-                          :*control-show? *control-show?
-                          :edit? edit?})))
-
-      (when (and @*show-left-menu? (not in-whiteboard?) (not table?))
-        (block-left-menu config block))
-
-      (let [icon' (get block (pu/get-pid :logseq.property/icon))]
-        (when-let [icon (and (ldb/page? block)
-                          (or icon'
-                            (or (when (ldb/class? block)
-                                  {:type :tabler-icon
-                                   :id "hash"})
-                              (when (ldb/property? block)
-                                {:type :tabler-icon
-                                 :id "letter-p"}))))]
-          [:div.flex.items-center.page-icon
-           (icon-component/icon-picker icon
-             {:on-chosen (fn [_e icon]
-                           (if icon
-                             (db-property-handler/set-block-property!
-                               (:db/id block)
-                               (pu/get-pid :logseq.property/icon)
-                               (select-keys icon [:id :type :color]))
+     (when-not (:hide-title? config)
+       [:div.block-main-container.flex.flex-row.pr-2.gap-1
+        {:data-has-heading (some-> block :block/properties (pu/lookup :logseq.property/heading))
+         :on-touch-start (fn [event uuid] (block-handler/on-touch-start event uuid))
+         :on-touch-move (fn [event]
+                          (block-handler/on-touch-move event block uuid editing? *show-left-menu? *show-right-menu?))
+         :on-touch-end (fn [event]
+                         (block-handler/on-touch-end event block uuid *show-left-menu? *show-right-menu?))
+         :on-touch-cancel (fn [_e]
+                            (block-handler/on-touch-cancel *show-left-menu? *show-right-menu?))
+         :on-mouse-enter (fn [e]
+                           (block-mouse-over e *control-show? block-id doc-mode?))
+         :on-mouse-leave (fn [e]
+                           (block-mouse-leave e *control-show? block-id doc-mode?))}
+
+        (when (and (not slide?) (not in-whiteboard?) (not table?)
+                   (not (:page-title? config)))
+          (let [edit? (or editing?
+                          (= uuid (:block/uuid (state/get-edit-block))))]
+            (block-control config block
+                           {:uuid uuid
+                            :block-id block-id
+                            :collapsed? collapsed?
+                            :*control-show? *control-show?
+                            :edit? edit?})))
+
+        (when (and @*show-left-menu? (not in-whiteboard?) (not table?))
+          (block-left-menu config block))
+
+        (let [icon' (get block (pu/get-pid :logseq.property/icon))]
+          (when-let [icon (and (ldb/page? block)
+                               (or icon'
+                                   (or (when (ldb/class? block)
+                                         {:type :tabler-icon
+                                          :id "hash"})
+                                       (when (ldb/property? block)
+                                         {:type :tabler-icon
+                                          :id "letter-p"}))))]
+            [:div.ls-page-icon.flex.self-start
+             (icon-component/icon-picker icon
+                                         {:on-chosen (fn [_e icon]
+                                                       (if icon
+                                                         (db-property-handler/set-block-property!
+                                                          (:db/id block)
+                                                          (pu/get-pid :logseq.property/icon)
+                                                          (select-keys icon [:id :type :color]))
                              ;; del
-                             (db-property-handler/remove-block-property!
-                               (:db/id block)
-                               (pu/get-pid :logseq.property/icon))))
-              :del-btn? (boolean icon')
-              :icon-props {:size (if (:page-title? config) 38 18)}})]))
-
-      (if whiteboard-block?
-        (block-reference {} (str uuid) nil)
-        ;; Not embed self
-        [:div.flex.flex-col.w-full
-         (let [block (merge block (block/parse-title-and-body uuid (:block/format block) pre-block? title))
-               hide-block-refs-count? (or (and (:embed? config)
-                                               (= (:block/uuid block) (:embed-id config)))
-                                          table?)]
-           (block-content-or-editor config block
-                                    {:edit-input-id edit-input-id
-                                     :block-id block-id
-                                     :edit? editing?
-                                     :hide-block-refs-count? hide-block-refs-count?}))])
-
-      (when (and @*show-right-menu? (not in-whiteboard?) (not table?))
-        (block-right-menu config block editing?))]
+                                                         (db-property-handler/remove-block-property!
+                                                          (:db/id block)
+                                                          (pu/get-pid :logseq.property/icon))))
+                                          :del-btn? (boolean icon')
+                                          :icon-props {:style {:width "1lh"
+                                                               :height "1lh"
+                                                               :font-size (if (:page-title? config) 38 18)}}})]))
+
+        (if whiteboard-block?
+          (block-reference {} (str uuid) nil)
+          ;; Not embed self
+          [:div.flex.flex-col.w-full
+           (let [block (merge block (block/parse-title-and-body uuid (:block/format block) pre-block? title))
+                 hide-block-refs-count? (or (and (:embed? config)
+                                                 (= (:block/uuid block) (:embed-id config)))
+                                            table?)]
+             (block-content-or-editor config block
+                                      {:edit-input-id edit-input-id
+                                       :block-id block-id
+                                       :edit? editing?
+                                       :hide-block-refs-count? hide-block-refs-count?}))])
+
+        (when (and @*show-right-menu? (not in-whiteboard?) (not table?))
+          (block-right-menu config block editing?))])
 
      (when (and db-based? (not table?))
        (block-positioned-properties config block :block-below))
 
      (when (and db-based? (not collapsed?) (not table?))
        [:div (when-not (:page-title? config) {:style {:padding-left 45}})
-        (db-properties-cp config block edit-input-id {:in-block-container? true})])
+        (db-properties-cp config block {:in-block-container? true})])
 
      (when-not (or (:hide-children? config) in-whiteboard? table?)
        (let [config' (-> (update config :level inc)
@@ -3184,7 +3186,7 @@
               (assoc state
                      ::control-show? (atom false)
                      ::navigating-block (atom (:block/uuid block)))
-               linked-block?
+               (or linked-block? (nil? (:container-id config)))
                (assoc ::container-id (state/get-next-container-id)))))
    :will-unmount (fn [state]
                    ;; restore root block's collapsed state
@@ -3653,7 +3655,7 @@
               (fn []
                 (when-let [_inst (rum/deref *virtualized-ref)]
                   (when-let [^js target (.-firstElementChild (rum/deref *wrap-ref))]
-                    (let [set-wrap-h! #(set! (.-height (.-style (rum/deref *wrap-ref))) %)
+                    (let [set-wrap-h! #(when-let [ref (rum/deref *wrap-ref)] (set! (.-height (.-style ref)) %))
                           set-wrap-h! (debounce set-wrap-h! 16)
                           ob (js/ResizeObserver.
                                (fn []

+ 26 - 2
src/main/frontend/components/block.css

@@ -865,11 +865,35 @@ html.is-mac {
 }
 
 .block-tags {
-    @apply flex flex-row flex-wrap items-center gap-1;
+    @apply flex flex-row flex-wrap self-start items-center gap-1;
     max-width: 256px;
 }
 .block-tag span {
-    @apply h-6 whitespace-nowrap overflow-hidden;
+    @apply whitespace-nowrap overflow-hidden;
     max-width: 160px;
     text-overflow: ellipsis;
 }
+
+.block-content-wrapper svg {
+    width: 20px;
+    height: 20px;
+}
+
+.ls-page-title .ls-page-icon svg, .ls-page-title .ls-page-icon button {
+    width: 38px;
+    height: 38px;
+}
+
+.ls-page-title .positioned-properties svg {
+    width: 24px;
+    height: 24px;
+}
+
+.ls-page-title .ls-page-icon button {
+    margin-top: 8px;
+}
+
+.ls-page-title .positioned-properties {
+    height: 54px;
+    overflow: hidden;
+}

+ 7 - 104
src/main/frontend/components/class.cljs

@@ -1,115 +1,18 @@
 (ns frontend.components.class
-  (:require [frontend.config :as config]
+  (:require [frontend.components.block :as block]
             [frontend.db.model :as model]
-            [frontend.db :as db]
-            [frontend.handler.route :as route-handler]
             [frontend.state :as state]
             [frontend.ui :as ui]
-            [rum.core :as rum]
-            [frontend.components.block :as block]
-            [logseq.shui.ui :as shui]
-            [frontend.db-mixins :as db-mixins]))
-
-(rum/defc class-select
-  [page class on-select]
-  (let [repo (state/get-current-repo)
-        children-pages (model/get-class-children repo (:db/id page))
-        ;; Disallows cyclic hierarchies
-        exclude-ids (-> (set (map (fn [id] (:block/uuid (db/entity id))) children-pages))
-                        (conj (:block/uuid page))) ; break cycle
-        classes (->> (model/get-all-classes repo)
-                     (remove (fn [e] (contains? exclude-ids (:block/uuid e)))))
-        options (sort-by :label
-                         (map (fn [entity] {:label (:block/title entity)
-                                            :value (:block/uuid entity)
-                                            :selected (= class (:block/uuid entity))})
-                              classes))
-        options (cons (if class
-                        {:label "Choose parent tag"
-                         :value "Choose"}
-                        {:label "Choose parent tag"
-                         :disabled true
-                         :selected true
-                         :value "Choose"})
-                      options)]
-    (shui/select
-     {:on-value-change on-select
-      :default-value (:block/uuid (:class/parent page))}
-     (shui/select-trigger
-      {:class "!px-2 !py-0 !h-8 !border-none"}
-      (shui/select-value
-       {:placeholder "Empty"}))
-     (shui/select-content
-      (shui/select-group
-       (for [{:keys [label value disabled]} options]
-         (shui/select-item {:value value :disabled disabled} label)))))))
-
-(rum/defc page-parent
-  [page parent]
-  (let [repo (state/get-current-repo)
-        parent-id (:block/uuid parent)]
-    (class-select page parent-id (fn [value]
-                                   (if (uuid? value)
-                                     (db/transact!
-                                      repo
-                                      [{:db/id (:db/id page)
-                                        :class/parent [:block/uuid value]}]
-                                      {:outliner-op :save-block})
-                                     (db/transact!
-                                      repo
-                                      [[:db.fn/retractAttribute (:db/id page) :class/parent]]
-                                      {:outliner-op :save-block}))))))
-
-(rum/defcs configure < rum/reactive db-mixins/query
-  "Configure a class page"
-  [state page {:keys [show-title?]
-               :or {show-title? true}}]
-  (let [page-id (:db/id page)
-        page (when page-id (db/sub-block page-id))]
-    (when page
-      [:div.property-configure.grid.gap-2
-       (when show-title? [:h1.title.mb-4 "Configure tag"])
-
-       (when-not (= (:db/ident page) :logseq.class/Root)
-         [:div.grid.grid-cols-5.gap-1.items-center.class-parent
-          [:div.col-span-2 "Parent tag:"]
-          (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/title 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)
-         (let [ancestor-pages (loop [parents [page]]
-                                (if-let [parent (:class/parent (last parents))]
-                                  (recur (conj parents parent))
-                                  parents))
-               class-ancestors (reverse ancestor-pages)]
-           (when (> (count class-ancestors) 2)
-             [:div.grid.grid-cols-5.gap-1.items-center.class-ancestors
-              [:div.col-span-2 "Ancestor tags:"]
-              [:div.col-span-3
-               (interpose [:span.opacity-50.text-sm " > "]
-                          (map (fn [{class-name :block/title :as ancestor}]
-                                 (if (= class-name (:block/title page))
-                                   [:span class-name]
-                                   [:a {:on-click #(route-handler/redirect-to-page! (:block/uuid ancestor))} class-name]))
-                               class-ancestors))]])))])))
+            [rum.core :as rum]))
 
 (defn class-children-aux
   [class {:keys [default-collapsed?] :as opts}]
-  (let [children (:class/_parent class)]
+  (let [children (:logseq.property/_parent class)]
     (when (seq children)
       [:ul
        (for [child (sort-by :block/title children)]
          (let [title [:li.ml-2 (block/page-reference false (:block/title child) {:show-brackets? false} nil)]]
-           (if (seq (:class/_parent child))
+           (if (seq (:logseq.property/_parent child))
              (ui/foldable
               title
               (class-children-aux child opts)
@@ -118,13 +21,13 @@
 
 (rum/defc class-children
   [class]
-  (when (seq (:class/_parent class))
-    (let [children-pages (model/get-class-children (state/get-current-repo) (:db/id class))
+  (when (seq (:logseq.property/_parent class))
+    (let [children-pages (model/get-structured-children (state/get-current-repo) (:db/id class))
           ;; Expand children if there are about a pageful of total blocks to display
           default-collapsed? (> (count children-pages) 30)]
       [:div.mt-4
        (ui/foldable
-        [:h2.font-medium "Child tags (" (count children-pages) ")"]
+        [:h2.font-medium "Children (" (count children-pages) ")"]
         [:div.mt-2.ml-1 (class-children-aux class {:default-collapsed? default-collapsed?})]
         {:default-collapsed? false
          :title-trigger? true})])))

+ 17 - 81
src/main/frontend/components/db_based/page.cljs

@@ -1,88 +1,24 @@
 (ns frontend.components.db-based.page
   "Page components only for DB graphs"
-  (:require [frontend.components.block :as component-block]
-            [frontend.components.class :as class-component]
-            [frontend.components.editor :as editor]
-            [frontend.components.property.config :as property-config]
+  (:require [frontend.components.property.config :as property-config]
             [frontend.db :as db]
             [frontend.db-mixins :as db-mixins]
-            [frontend.util :as util]
-            [logseq.outliner.property :as outliner-property]
             [logseq.shui.ui :as shui]
             [rum.core :as rum]))
 
-(rum/defc page-properties
-  "This component is called by page-inner and within configure/info modal. This should not
-   be displaying properties from both components at the same time"
-  < rum/reactive
-  [page {:keys [mode configure?]}]
-  (let [edit-input-id-prefix (str "edit-block-" (:block/uuid page))
-        configure-opts {:selected? false
-                        :page-configure? configure?}
-        has-viewable-properties? (outliner-property/block-has-viewable-properties? page)]
-    (when (or configure? has-viewable-properties?)
-      [:div.ls-page-properties
-       {:class (util/classnames [{:no-properties (not has-viewable-properties?)}])}
-       (if configure?
-         (cond
-           (= mode :tag)
-           (component-block/db-properties-cp {:editor-box editor/box}
-                                             page
-                                             (str edit-input-id-prefix "-schema")
-                                             (assoc configure-opts :class-schema? true))
-
-           (= mode :page)
-           (component-block/db-properties-cp {:editor-box editor/box}
-                                             page
-                                             (str edit-input-id-prefix "-page")
-                                             (assoc configure-opts :class-schema? false :page? true)))
-         ;; default view for page-inner
-         (component-block/db-properties-cp {:editor-box editor/box}
-                                           page
-                                           (str edit-input-id-prefix "-page")
-                                           (assoc configure-opts :class-schema? false :page? true)))])))
-
-(rum/defcs page-configure < rum/reactive
-  [state page]
-  (let [page-opts {:configure? true}]
-    [:div.flex.flex-col.gap-1.pb-4
-     [:div.mt-2.flex.flex-col.gap-2
-      (class-component/configure page {:show-title? false})
-      (page-properties page (assoc page-opts :mode :tag))]]))
-
-(rum/defcs page-info < rum/reactive db-mixins/query
-  (rum/local false ::show?)
-  [state page]
-  (let [*show? (::show? state)
-        page (db/sub-block (:db/id page))
-        type (:block/type page)]
-    (case type
-      "property"
-      [:div.pb-4.-ml-1
-       (shui/button
-        {:variant "ghost"
-         :class "opacity-50 hover:opacity-90"
-         :size :sm
-         :on-click (fn [^js e]
-                     (shui/popup-show! (.-target e)
-                                       (fn []
-                                         (property-config/dropdown-editor page nil {:debug? (.-altKey e)}))
-                                       {:content-props {:class "ls-property-dropdown-editor as-root"}
-                                        :align "start"
-                                        :as-dropdown? true}))}
-        "Configure property")]
-
-      "class"
-      [:div.pb-4.-ml-1
-       (shui/button
-        {:variant "ghost"
-         :class "opacity-50 hover:opacity-90"
-         :size :sm
-         :on-click (fn [] (swap! *show? not))}
-        "Configure tag")
-       (when @*show?
-         [:div.page-info.border.rounded.my-2
-          [:div.px-4.py-2
-           (page-configure page)]])]
-
-      nil)))
+(rum/defc configure-property < rum/reactive db-mixins/query
+  [page]
+  (let [page (db/sub-block (:db/id page))]
+    [:div.pb-4.-ml-1
+     (shui/button
+      {:variant "ghost"
+       :class "opacity-50 hover:opacity-90"
+       :size :sm
+       :on-click (fn [^js e]
+                   (shui/popup-show! (.-target e)
+                                     (fn []
+                                       (property-config/dropdown-editor page nil {:debug? (.-altKey e)}))
+                                     {:content-props {:class "ls-property-dropdown-editor as-root"}
+                                      :align "start"
+                                      :as-dropdown? true}))}
+      "Configure property")]))

+ 1 - 0
src/main/frontend/components/editor.cljs

@@ -135,6 +135,7 @@
 (rum/defc page-search-aux
   [id format embed? db-tag? q current-pos input pos]
   (let [db? (config/db-based-graph? (state/get-current-repo))
+        q (string/trim q)
         [matched-pages set-matched-pages!] (rum/use-state nil)]
     (rum/use-effect! (fn []
                        (when-not (string/blank? q)

+ 1 - 1
src/main/frontend/components/icon.cljs

@@ -460,5 +460,5 @@
                                          popup-opts))))}
        (if has-icon?
          [:span {:style {:color (or (:color icon-value) "inherit")}}
-          (icon icon-value (merge {:size 18} icon-props))]
+          (icon icon-value icon-props)]
          (or empty-label "Empty"))))))

+ 0 - 1
src/main/frontend/components/objects.cljs

@@ -80,7 +80,6 @@
                                :show-add-property? true
                                :add-property! (fn []
                                                 (state/pub-event! [:editor/new-property {:block class
-                                                                                         :page-configure? true
                                                                                          :class-schema? true}]))
                                :on-delete-rows (fn [table selected-rows]
                                                  (let [pages (filter ldb/page? selected-rows)

+ 8 - 6
src/main/frontend/components/page.cljs

@@ -421,7 +421,7 @@
                        :else title))])]])))))
 
 (rum/defc db-page-title
-  [state repo page whiteboard-page?]
+  [state repo page whiteboard-page? sidebar?]
   [:div.ls-page-title.flex.flex-1.w-full.content.items-start
      {:class (when-not whiteboard-page? "title")
       :on-pointer-down (fn [e]
@@ -442,6 +442,7 @@
 
      [:div.w-full
       (component-block/block-container {:page-title? true
+                                        :hide-title? sidebar?
                                         :hide-children? true
                                         :container-id (:container-id state)} page)]])
 
@@ -561,7 +562,8 @@
          (if (and whiteboard-page? (not sidebar?))
            [:div ((state/get-component :whiteboard/tldraw-preview) (:block/uuid page))] ;; FIXME: this is not reactive
            [:div.relative.grid.gap-2.page-inner
-            (when (and (not sidebar?) (not block?))
+            (when (or (and db-based? (not block?))
+                      (and (not db-based?) (not sidebar?) (not block?)))
               [:div.flex.flex-row.space-between
                (when (and (or (mobile-util/native-platform?) (util/mobile?)) (not db-based?))
                  [:div.flex.flex-row.pr-2
@@ -573,14 +575,14 @@
                   (page-blocks-collapse-control title *control-show? *all-collapsed?)])
                (when (and (not whiteboard?) (ldb/page? page))
                  (if db-based?
-                   (db-page-title state repo page whiteboard-page?)
+                   (db-page-title state repo page whiteboard-page? sidebar?)
                    (page-title-cp page {:journal? journal?
                                         :fmt-journal? fmt-journal?
                                         :preview? preview?})))
                (lsp-pagebar-slot)])
 
-            (when db-based?
-              (db-page/page-info page))
+            (when (and db-based? (ldb/property? page))
+              (db-page/configure-property page))
 
             (when (and db-based? class-page?)
               (objects/class-objects page))
@@ -610,7 +612,7 @@
             (when (and (not block?) (not db-based?))
               (tagged-pages repo page page-title))
 
-            (when (ldb/class? page)
+            (when (and (ldb/page? page) (:logseq.property/_parent page))
               (class-component/class-children page))
 
             ;; referenced blocks

+ 1 - 43
src/main/frontend/components/page.css

@@ -205,48 +205,6 @@ html.is-native-ios {
   user-select: none;
 }
 
-.page-info {
-  padding-left: 1px;
-
-  &-inner {
-    @apply py-2;
-  }
-
-  .configure-wrap {
-    @apply px-2;
-  }
-
-  .ls-new-property {
-    @apply mt-[3px];
-  }
-
-  &.is-collapsed {
-    @apply border-transparent;
-
-    .ls-new-property {
-      @apply hidden;
-    }
-
-    .ls-page-properties {
-      &.no-properties {
-        @apply hidden;
-      }
-    }
-  }
-
-  .info-title {
-    @apply relative min-h-[28px] flex items-center;
-  }
-}
-
-.page-info-title-placeholder {
-  min-height: 28px;
-}
-
-.info-title a.tag:hover, .multi-values a.tag:hover {
-  text-decoration: none;
-}
-
 .no-ring {
   @apply focus:ring-0 focus:ring-offset-0;
 }
@@ -285,4 +243,4 @@ html.is-native-ios {
   .page-linked > .content {
     @apply pt-2;
   }
-}
+}

+ 69 - 50
src/main/frontend/components/property.cljs

@@ -44,10 +44,10 @@
 (defn- <add-property-from-dropdown
   "Adds an existing or new property from dropdown. Used from a block or page context.
    For pages, used to add both schema properties or properties for a page"
-  [entity property-uuid-or-name schema {:keys [class-schema? page-configure?]}]
+  [entity property-uuid-or-name schema {:keys [class-schema?]}]
   (p/let [repo (state/get-current-repo)
           ;; Both conditions necessary so that a class can add its own page properties
-          add-class-property? (and (ldb/class? entity) page-configure? class-schema?)
+          add-class-property? (and (ldb/class? entity) class-schema?)
           result (when (uuid? property-uuid-or-name)
                    (db-async/<get-block repo property-uuid-or-name {:children? false}))
           ;; In block context result is in :block
@@ -77,7 +77,7 @@
   [state property {:keys [*property *property-name *property-schema built-in? disabled?
                           show-type-change-hints? block *show-new-property-config?
                           *show-class-select?
-                          default-open? page-configure? class-schema?]
+                          default-open? class-schema?]
                    :as opts}]
   (let [property-name (or (and *property-name @*property-name) (:block/title property))
         property-schema (or (and *property-schema @*property-schema) (:block/schema property))
@@ -104,7 +104,7 @@
                 (reset! *show-new-property-config? :adding-property))
               (p/let [property' (when block (<add-property-from-dropdown block property-name schema opts))
                       property (or property' property)
-                      add-class-property? (and (ldb/class? block) page-configure? class-schema?)]
+                      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))
@@ -189,7 +189,7 @@
                    :size 15})))
 
 (defn- property-input-on-chosen
-  [block *property *property-key *show-new-property-config? {:keys [class-schema? page-configure?]}]
+  [block *property *property-key *show-new-property-config? {:keys [class-schema?]}]
   (fn [{:keys [value label]}]
     (reset! *property-key (if (uuid? value) label value))
     (let [property (when (uuid? value) (db/entity [:block/uuid value]))]
@@ -202,8 +202,7 @@
           (cond
             add-class-property?
             (p/do!
-             (pv/<add-property! block (:db/ident property) "" {:class-schema? class-schema?
-                                                               :exit-edit? page-configure?})
+             (pv/<add-property! block (:db/ident property) "" {:class-schema? class-schema?})
              (shui/dialog-close!))
 
             (= :checkbox type)
@@ -313,7 +312,7 @@
                        (edit-original-block {:editing-default-property? editing-default-property?})))
                    (state/set-editor-action! nil)
                    state)}
-  [state block *property-key {:keys [class-schema? page?]
+  [state block *property-key {:keys [class-schema?]
                               :as opts}]
   (let [*ref (::ref state)
         *property (::property state)
@@ -324,11 +323,16 @@
                                 (map db-property/built-in-properties)
                                 (keep #(when (get block (:attribute %)) (:title %)))
                                 set)
+        block-type (keyword (get block :block/type :block))
+        page? (ldb/page? block)
         exclude-properties (fn [m]
-                             (or (and (not page?) (contains? existing-tag-alias (:block/title m)))
-                               ;; Filters out properties from being in wrong :view-context
-                                 (and (not page?) (= :page (get-in m [:block/schema :view-context])))
-                                 (and page? (= :block (get-in m [:block/schema :view-context])))))
+                             (let [view-context (get-in m [:block/schema :view-context] :all)
+                                   block-types (if (and page? (not= block-type :page))
+                                                 #{:page block-type}
+                                                 #{block-type})]
+                               (or (and (not page?) (contains? existing-tag-alias (:block/title m)))
+                                   ;; Filters out properties from being in wrong :view-context
+                                   (and (not= view-context :all) (not (contains? block-types view-context))))))
         property (rum/react *property)
         property-key (rum/react *property-key)]
     [:div.ls-property-input.flex.flex-1.flex-row.items-center.flex-wrap.gap-1
@@ -377,14 +381,16 @@
 
 (rum/defcs new-property < rum/reactive
   [state block opts]
-  (when (and (:page-configure? opts) (not config/publishing?))
-    [:div.ls-new-property
-     [:a.fade-link.flex.add-property
-      {:on-click (fn []
-                   (state/pub-event! [:editor/new-property (merge opts {:block block})]))}
+  (when (and (not config/publishing?) (:class-schema? opts))
+    [:div.ls-new-property {:style {:margin-left 6 :margin-top 1}}
+     [:a.fade-link.flex
+      {:tab-index 0
+       :on-click (fn [e]
+                   (state/pub-event! [:editor/new-property (merge opts {:block block
+                                                                        :target (.-target e)})]))}
       [:div.flex.flex-row.items-center
-       (ui/icon "plus" {:size 15})
-       [:div.ml-1.text-sm "Add property"]]]]))
+       (ui/icon "plus" {:size 16})
+       [:div.ml-1 "Add property"]]]]))
 
 (defn- resolve-linked-block-if-exists
   "Properties will be updated for the linked page instead of the refed block.
@@ -422,6 +428,8 @@
                                                                     :inline-text inline-text
                                                                     :page-cp page-cp))]
         [:div {:class (cond
+                        (and (= (:db/ident property) :logseq.property.class/properties) (seq v))
+                        "property-pair !flex flex-col"
                         (or date? checkbox?)
                         "property-pair items-center"
                         :else
@@ -430,15 +438,22 @@
            (dnd/sortable-item (assoc sortable-opts :class "property-key col-span-2") property-key-cp')
            [:div.property-key.col-span-2 property-key-cp'])
 
-         [:div.property-value-container.col-span-3.flex.flex-row.gap-1.items-center
-          (when-not block? [:div.opacity-30 {:style {:margin-left 5}}
-                            [:span.bullet-container.cursor [:span.bullet]]])
-          [:div.flex.flex-1
-           (if (and (:class-schema? opts) (:page-configure? opts))
-             [:div.property-description.text-sm.opacity-70
-              (inline-text {} :markdown (db-property/property-value-content (:logseq.property/description property)))]
+         (let [class-properties? (= (:db/ident property) :logseq.property.class/properties)
+               property-desc (when-not (= (:db/ident property) :logseq.property/description)
+                               (:logseq.property/description property))]
+           [:div.property-value-container.col-span-3.flex.flex-row.gap-1.items-center
+            (cond-> {}
+              class-properties? (assoc :class "ml-2 -mt-1"))
+            (when-not (or block? class-properties? property-desc)
+              [:div.opacity-30 {:style {:margin-left 5}}
+               [:span.bullet-container.cursor [:span.bullet]]])
+            [:div.flex.flex-1
              [:div.property-value.flex.flex-1
-              (pv/property-value block property v opts)])]]]))))
+              (cond-> {}
+                class-properties? (assoc :class :opacity-90))
+              (if (:class-schema? opts)
+                (pv/property-value property (db/entity :logseq.property/description) property-desc opts)
+                (pv/property-value block property v opts))]]])]))))
 
 (rum/defcs ordered-properties < rum/reactive
   {:init (fn [state]
@@ -502,9 +517,11 @@
         classes (concat (:block/tags block) (outliner-property/get-class-parents db (:block/tags block)))]
     (doseq [class classes]
       (db-async/<get-block repo (:db/id class) :children? false))
+    (when (ldb/class? block)
+      (doseq [property (:logseq.property.class/properties block)]
+        (db-async/<get-block repo (:db/id property) :children? false)))
     classes))
 
-;; TODO: Remove :page-configure? as it only ever seems to be set to true
 (rum/defcs ^:large-vars/cleanup-todo properties-area < rum/reactive db-mixins/query
   {:init (fn [state]
            (let [target-block (first (:rum/args state))
@@ -516,17 +533,25 @@
    :will-remount (fn [state]
                    (let [block (db/entity (:db/id (::block state)))]
                      (assoc state ::classes (async-load-classes! block))))}
-  [state _target-block {:keys [page-configure? class-schema?] :as opts}]
+  [state _target-block {:keys [class-schema?] :as opts}]
   (let [id (::id state)
         block (db/sub-block (:db/id (::block state)))
         _ (doseq [class (::classes state)]
             (db/sub-block (:db/id class)))
         page? (db/page? block)
+        class? (ldb/class? block)
         block-properties (:block/properties block)
-        properties (if (and class-schema? page-configure?)
+        properties (cond
+                     class-schema?
                      (->> (db-property/get-class-ordered-properties block)
                           (map :db/ident)
+                          distinct
                           (map #(vector % %)))
+
+                     (and class? (nil? (:logseq.property.class/properties block)))
+                     (assoc block-properties :logseq.property.class/properties nil)
+
+                     :else
                      block-properties)
         remove-built-in-or-other-position-properties
         (fn [properties]
@@ -553,7 +578,7 @@
         ;; This section produces own-properties and full-hidden-properties
         hide-with-property-id (fn [property-id]
                                 (cond
-                                  (or root-block? page-configure?)
+                                  root-block?
                                   false
                                   :else
                                   (boolean (:hide? (:block/schema (db/entity property-id))))))
@@ -591,26 +616,20 @@
                              result))
         full-properties (->> (concat block-own-properties' (map (fn [p] [p (get block p)]) class-properties))
                              remove-built-in-or-other-position-properties)]
-    (when-not (and (empty? full-properties)
-                   (not (:page-configure? opts)))
+    (when-not (and (empty? full-properties) (not (:class-schema? opts)))
       [:div.ls-properties-area
-       (cond-> {:id id}
-         class-schema?
-         (assoc :class "class-properties")
-         true (assoc :tab-index 0
-                     :on-key-up #(when-let [block (and (= "Escape" (.-key %))
-                                                       (.closest (.-target %) "[blockid]"))]
-                                   (let [target (.-target %)]
-                                     (when-not (d/has-class? target "ls-popup-closed")
-                                       (state/set-selection-blocks! [block])
-                                       (some-> js/document.activeElement (.blur)))
-                                     (d/remove-class! target "ls-popup-closed")))))
-       (let [properties' (if (and page? page-configure?)
-                           (concat [[:block/tags (:block/tags block)]
-                                    [:logseq.property/icon (:logseq.property/icon block)]]
-                                   (remove (fn [[k _v]] (contains? #{:block/tags :logseq.property/icon} k)) full-properties))
-
-                           (remove (fn [[k _v]] (contains? #{:logseq.property/icon} k)) full-properties))]
+       {:id id
+        :class (util/classnames [{:class-properties class-schema?
+                                  :ls-page-properties (and page? (not class-schema?))}])
+        :tab-index 0
+        :on-key-up #(when-let [block (and (= "Escape" (.-key %))
+                                          (.closest (.-target %) "[blockid]"))]
+                      (let [target (.-target %)]
+                        (when-not (d/has-class? target "ls-popup-closed")
+                          (state/set-selection-blocks! [block])
+                          (some-> js/document.activeElement (.blur)))
+                        (d/remove-class! target "ls-popup-closed")))}
+       (let [properties' (remove (fn [[k _v]] (contains? #{:logseq.property/icon} k)) full-properties)]
          (properties-section block (if class-schema? properties properties') opts))
 
        (rum/with-key (new-property block opts) (str id "-add-property"))])))

+ 3 - 7
src/main/frontend/components/property.css

@@ -281,12 +281,8 @@ a.control-link {
   }
 }
 
-.ls-page-properties {
-  @apply min-h-[40px];
-
-  &.no-mode {
-    @apply hidden;
-  }
+.ls-page-title .ls-page-properties {
+  @apply mt-4;
 }
 
 .ls-property-add {
@@ -400,4 +396,4 @@ a.control-link {
 
 .ls-property-ui-position-sub-pane {
   @apply w-[240px] p-0;
-}
+}

+ 15 - 10
src/main/frontend/components/property/config.cljs

@@ -508,7 +508,8 @@
                                   :submenu-content (fn [ops]
                                                      (property-type-sub-pane property ops))}))
 
-     (when (= property-type :node)
+     (when (and (= property-type :node)
+                (not (contains? #{:logseq.property/parent} (:db/ident property))))
        (dropdown-editor-menuitem {:icon :hash
                                   :title "Specify node tags"
                                   :desc ""
@@ -517,10 +518,10 @@
                                                       (class-select property {:default-open? false})])}))
 
      (when enable-closed-values?
-           (let [values (:property/closed-values property)]
-             (dropdown-editor-menuitem {:icon :list :title "Available choices"
-                                        :desc (when (seq values) (str (count values) " choices"))
-                                        :submenu-content (fn [] (choices-sub-pane property))})))
+       (let [values (:property/closed-values property)]
+         (dropdown-editor-menuitem {:icon :list :title "Available choices"
+                                    :desc (when (seq values) (str (count values) " choices"))
+                                    :submenu-content (fn [] (choices-sub-pane property))})))
 
      (let [many? (db-property/many? property)]
        (dropdown-editor-menuitem {:icon :checks :title "Multiple values"
@@ -538,10 +539,11 @@
                                         (update-cardinality-fn))))}))
 
      (shui/dropdown-menu-separator)
-     (let [position (:position property-schema)]
-       (dropdown-editor-menuitem {:icon :float-left :title "UI position" :desc (some->> position (get position-labels) (:title))
-                                  :item-props {:class "ui__position-trigger-item"}
-                                  :submenu-content (fn [ops] (ui-position-sub-pane property (assoc ops :position position)))}))
+     (when (not (contains? #{:logseq.property/parent} (:db/ident property)))
+       (let [position (:position property-schema)]
+         (dropdown-editor-menuitem {:icon :float-left :title "UI position" :desc (some->> position (get position-labels) (:title))
+                                    :item-props {:class "ui__position-trigger-item"}
+                                    :submenu-content (fn [ops] (ui-position-sub-pane property (assoc ops :position position)))})))
 
      (dropdown-editor-menuitem {:icon :eye-off :title "Hide by default" :toggle-checked? (boolean (:hide? property-schema))
                                 :on-toggle-checked-change #(db-property-handler/upsert-property! (:db/ident property)
@@ -558,7 +560,10 @@
                                    (shui/popup-hide-all!)
                                    (route-handler/redirect-to-page! (:block/uuid property)))}}))
 
-     (when owner-block
+     (when (and owner-block
+                (not (and
+                      (ldb/class? owner-block)
+                      (contains? #{:logseq.property/parent} (:db/ident property)))))
        (dropdown-editor-menuitem
         {:id :delete-property :icon :x :title "Delete property" :desc "" :disabled? false
          :item-props {:class "opacity-60 focus:!text-red-rx-09 focus:opacity-100"

+ 2 - 6
src/main/frontend/components/property/dialog.cljs

@@ -3,7 +3,6 @@
   (:require [frontend.components.property :as property-component]
             [rum.core :as rum]
             [frontend.modules.shortcut.core :as shortcut]
-            [logseq.db :as ldb]
             [frontend.db :as db]))
 
 (rum/defcs dialog <
@@ -18,9 +17,6 @@
   (when (seq blocks)
     (let [*property-key (::property-key state)
           *property (::property state)
-          block (first blocks)
-          page? (ldb/page? block)]
+          block (first blocks)]
       [:div.ls-property-dialog
-       (property-component/property-input block *property-key (assoc opts
-                                                                     :*property *property
-                                                                     :page? page?))])))
+       (property-component/property-input block *property-key (assoc opts :*property *property))])))

+ 212 - 160
src/main/frontend/components/property/value.cljs

@@ -28,11 +28,21 @@
             [dommy.core :as d]
             [frontend.search :as search]
             [goog.functions :refer [debounce]]
+            [frontend.handler.route :as route-handler]
             [frontend.components.title :as title]))
 
 (rum/defc property-empty-btn-value
-  [& {:as opts}]
-  (shui/button (merge {:class "empty-btn" :variant :text} opts) "Empty"))
+  [& {:keys [property] :as opts}]
+  (let [text (cond
+               (= (:db/ident property) :logseq.property/description)
+               "Add description"
+               :else
+               "Empty")]
+    (if (= text "Empty")
+      (shui/button (merge {:class "empty-btn" :variant :text} opts)
+                  text)
+      (shui/button (merge {:class "empty-btn !text-base" :variant :text} opts)
+                  text))))
 
 (rum/defc property-empty-text-value
   [& {:as opts}]
@@ -67,7 +77,7 @@
 
 (defn- select-type?
   [property type]
-  (or (contains? #{:node :number :url :date} type)
+  (or (contains? #{:node :number :url :date :page :class :property} type)
     ;; closed values
     (seq (:property/closed-values property))))
 
@@ -256,16 +266,18 @@
                       (shui/popup-hide!))}))))
 
 (defn- <create-page-if-not-exists!
-  [property classes page]
+  [block property classes page]
   (let [page* (string/trim page)
         ;; 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)
-        class? (= :block/tags (:db/ident property))
+        class? (or (= :block/tags (:db/ident property))
+                   (and (= :logseq.property/parent (:db/ident property))
+                        (ldb/class? block)))
         ;; Note: property and other types shouldn't be converted to class
         page? (= "page" (:block/type page-entity))]
     (cond
@@ -274,8 +286,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
@@ -298,34 +310,36 @@
 (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?)
+                         (not (and (ldb/class? block) (= (:db/ident property) :logseq.property/parent))))
+                  (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)
@@ -344,7 +358,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]
@@ -357,6 +371,7 @@
         alias? (= :block/alias (:db/ident property))
         tags-or-alias? (or tags? alias?)
         block (db/entity (:db/id block))
+        result (rum/react *result)
         selected-choices (when block
                            (when-let [v (get block (:db/ident property))]
                              (if (every? de/entity? v)
@@ -364,28 +379,43 @@
                                [(: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))
+         (cond
+           (= (:db/ident property) :logseq.property/parent)
+           (let [children-pages (model/get-structured-children repo (:db/id block))
+                 ;; Disallows cyclic hierarchies
+                 exclude-ids (-> (set (map (fn [id] (:block/uuid (db/entity id))) children-pages))
+                                 (conj (:block/uuid block))) ; break cycle
+                 options (if (ldb/class? block) (model/get-all-classes repo)
+                             (->> (model/get-all-pages repo)
+                                  (remove (fn [e] (or (ldb/built-in? e) (ldb/property? e))))))
+                 excluded-options (->>
+                                   (concat options result)
+                                   (util/distinct-by :db/id)
+                                   (remove (fn [e]
+                                             (or (contains? exclude-ids (:block/uuid e))
+                                                 (and (not (db/page? e)) (not (seq (:block/tags e))))))))]
+             excluded-options)
+
+           (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
+           (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))
+                           (and alias? (= (or (:db/id (:block/page block))
+                                              (:db/id block))
                                           (:db/id node)))))
-                  result)))))
+                     result))))
         options (map (fn [node]
                        (let [id (or (:value node) (:db/id node))
                              label (if (integer? id)
@@ -398,60 +428,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! block 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))))]
+                 :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-structured-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 <
@@ -471,14 +501,14 @@
                           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
@@ -591,7 +621,7 @@
       [:div
        {:tabIndex 0
         :on-click (fn [] (<create-new-block! block property ""))}
-       (property-empty-btn-value)])))
+       (property-empty-btn-value {:property property})])))
 
 (rum/defcs property-block-value < rum/reactive db-mixins/query
                                   {:init (fn [state]
@@ -621,7 +651,7 @@
                 (:db/id v-block))
               :else
               invalid-warning)))
-        (property-empty-btn-value)))))
+        (property-empty-btn-value {:property property})))))
 
 (rum/defc closed-value-item < rum/reactive db-mixins/query
   [value {:keys [inline-text icon?]}]
@@ -634,7 +664,7 @@
           (cond
             icon
             (if icon?
-              [:div.h-6.flex.items-center (icon-component/icon icon)]
+              (icon-component/icon icon)
               [:div.flex.flex-row.items-center.gap-2.h-6
                (icon-component/icon icon)
                (when value'
@@ -664,7 +694,7 @@
     [:div.select-item.cursor-pointer
      (cond
        (= value :logseq.property/empty-placeholder)
-       (property-empty-btn-value)
+       (property-empty-btn-value {:property property})
 
        (or (ldb/page? value)
            (and (seq (:block/tags value))
@@ -676,11 +706,10 @@
            (page-cp {:disable-preview? true
                      :tag? tag?
                      :hide-close-button? true
-                     :show-unique-title? true
                      :meta-click? other-position?} value)
            (:db/id value)))
 
-       (= type :node)
+       (contains? #{:node :class :property :page} type)
        (when-let [reference (state/get-component :block/reference)]
          (reference {} (:block/uuid value)))
 
@@ -715,7 +744,7 @@
                              (:number :url :default)
                              (select block property select-opts' opts)
 
-                             (:node :date)
+                             (:node :class :property :page :date)
                              (property-value-select-node block property select-opts' opts))])
           trigger-id (str "trigger-" (:container-id opts) "-" (:db/id block) "-" (:db/id property))
           show! (fn [e]
@@ -761,7 +790,7 @@
                     (<create-new-block! block property "")))}
      (cond
        (and (= type :default) (nil? (:block/title value)))
-       [:div.jtrigger (property-empty-btn-value)]
+       [:div.jtrigger (property-empty-btn-value {:property property})]
 
        (= type :default)
        (property-block-value value block property page-cp)
@@ -830,7 +859,7 @@
                                           select-opts
                                           {:dropdown? false})]
                         [:div.property-select
-                         (if (= :node type)
+                         (if (contains? #{:node :page :class :property} type)
                            (property-value-select-node block property
                              select-opts
                              opts)
@@ -877,53 +906,76 @@
   [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)
+         properties-cp (state/get-component :block/properties-cp)
+         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 :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
+                     (= (:db/ident property) :logseq.property.class/properties)
+                     (properties-cp {} block {:selected? false
+                                              :class-schema? true})
+
+                     (and multiple-values? (= type :default) (not closed-values?))
+                     (property-normal-block-value block property v)
+
+                     multiple-values?
+                     (multiple-values block property opts schema)
+
+                     :else
+                     (let [value-cp (property-scalar-value block property v
+                                                           (merge
+                                                            opts
+                                                            {:editor-id editor-id
+                                                             :dom-id dom-id}))
+                           parent? (= (:db/ident property) :logseq.property/parent)
+                           page-ancestors (when parent?
+                                            (let [ancestor-pages (loop [parents [block]]
+                                                                   (if-let [parent (:logseq.property/parent (last parents))]
+                                                                     (recur (conj parents parent))
+                                                                     parents))]
+                                              (->> (reverse ancestor-pages)
+                                                   (remove (fn [e] (= (:db/id block) (:db/id e))))
+                                                   butlast)))]
+                       (if (seq page-ancestors)
+                         [:div.flex.flex-1.items-center.gap-1
+                          (interpose [:span.opacity-50.text-sm " > "]
+                                     (concat
+                                      (map (fn [{title :block/title :as ancestor}]
+                                             [:a {:on-click #(route-handler/redirect-to-page! (:block/uuid ancestor))} title])
+                                           page-ancestors)
+                                      [value-cp]))]
+                         value-cp)))]]
+     (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))))

+ 1 - 1
src/main/frontend/components/property/value.css

@@ -1,4 +1,4 @@
-.property-value-inner:not([data-type="default"]) {
+.property-value-inner:not([data-type="default"]):not([data-type="property"]) {
   @apply cursor-pointer;
   &:hover, .as-scalar-value-wrap:hover {
     @apply bg-gray-02 rounded transition-[background-color] duration-300;

+ 1 - 1
src/main/frontend/db/async.cljs

@@ -265,7 +265,7 @@
 
 (defn <get-tag-objects
   [graph class-id]
-  (let [class-children (db-model/get-class-children graph class-id)
+  (let [class-children (db-model/get-structured-children graph class-id)
         class-ids (distinct (conj class-children class-id))]
     (<q graph {:transact-db? true}
         '[:find [(pull ?b [*]) ...]

+ 5 - 5
src/main/frontend/db/model.cljs

@@ -779,23 +779,23 @@ independent of format as format specific heading characters are stripped"
       (keep (fn [e] (when-not (= :logseq.class/Root (:db/ident e)) e)) classes)
       classes)))
 
-(defn get-class-children
+(defn get-structured-children
   [repo eid]
   (->>
    (d/q '[:find [?children ...]
           :in $ ?parent %
           :where
-          (class-parent ?parent ?children)]
+          (parent ?parent ?children)]
         (conn/get-db repo)
         eid
-        (:class-parent rules/rules))
+     (:parent rules/rules))
    distinct))
 
 (defn get-class-objects
   [repo class-id]
   (when-let [class (db-utils/entity repo class-id)]
-    (if (first (:class/_parent class))        ; has children classes
-      (let [all-classes (conj (->> (get-class-children repo class-id)
+    (if (first (:logseq.property/_parent class))        ; has children classes
+      (let [all-classes (conj (->> (get-structured-children repo class-id)
                                    (map #(db-utils/entity repo %)))
                               class)]
         (->> (mapcat :block/_tags all-classes)

+ 13 - 3
src/main/frontend/handler/common/page.cljs

@@ -18,7 +18,9 @@
             [frontend.db.conn :as conn]
             [datascript.core :as d]
             [frontend.modules.outliner.ui :as ui-outliner-tx]
-            [frontend.modules.outliner.op :as outliner-op]))
+            [frontend.modules.outliner.op :as outliner-op]
+            [frontend.handler.db-based.editor :as db-editor-handler]
+            [logseq.db.frontend.content :as db-content]))
 
 (defn <create!
   ([title]
@@ -28,13 +30,21 @@
            :as options}]
    (p/let [repo (state/get-current-repo)
            conn (db/get-db repo false)
+           db-based? (config/db-based-graph? repo)
+           parsed-result (when db-based? (db-editor-handler/wrap-parse-block {:block/title title}))
+           title' (if (and db-based? (seq (:block/tags parsed-result)))
+                    (string/trim (first (common-util/split-first (str "#" db-content/page-ref-special-chars) (:block/title parsed-result))))
+                    title)
+           options' (if db-based?
+                      (update options :tags concat (:block/tags parsed-result))
+                      options)
            result (ui-outliner-tx/transact!
                    {:outliner-op :create-page}
-                   (outliner-op/create-page! title options))
+                   (outliner-op/create-page! title' options'))
            [_page-name page-uuid] (ldb/read-transit-str result)]
      (when redirect?
        (route-handler/redirect-to-page! page-uuid))
-     (let [page (db/get-page (or page-uuid title))]
+     (let [page (db/get-page (or page-uuid title'))]
        (when-let [first-block (ldb/get-first-child @conn (:db/id page))]
          (block-handler/edit-block! first-block :max {:container-id :unknown-container}))
        page))))

+ 6 - 4
src/main/frontend/handler/editor.cljs

@@ -1160,7 +1160,7 @@
             (let [value (gobj/get input "value")]
               (extract-nearest-link-from-text value pos url-regex))))))))
 
-(defn- get-nearest-page
+(defn get-nearest-page
   "Return the nearest page-name (not dereferenced, may be an alias), block or tag"
   []
   (when-let [block (state/get-edit-block)]
@@ -1947,8 +1947,10 @@
 
       ;; Open "Search page or New page" auto-complete
       (and (= last-input-char commands/hashtag)
-           ;; Only trigger at beginning of a line or before whitespace
-           (or (re-find #"(?m)^#" (str (.-value input))) (start-of-new-word? input pos)))
+             ;; Only trigger at beginning of a line, before whitespace or after a reference
+             (or (re-find #"(?m)^#" (str (.-value input)))
+                 (start-of-new-word? input pos)
+                 (and db-based? (= page-ref/right-brackets (common-util/safe-subs (str (.-value input)) (- pos 3) (dec pos))))))
       (do
         (state/set-editor-action-data! {:pos (cursor/get-caret-pos input)})
         (state/set-editor-last-pos! pos)
@@ -3397,7 +3399,7 @@
                     (not (db-pu/all-hidden-properties? property-keys)))
                (and db-based? (seq tags)
                     (some (fn [t]
-                            (let [properties (map :db/ident (:class/schema.properties (:block/schema t)))]
+                            (let [properties (map :db/ident (:logseq.property.class/properties (:block/schema t)))]
                               (and (seq properties)
                                    (not (db-pu/all-hidden-properties? properties))))) tags))
                (and

+ 10 - 13
src/main/frontend/handler/events.cljs

@@ -11,8 +11,8 @@
             [clojure.set :as set]
             [clojure.string :as string]
             [frontend.commands :as commands]
-            [frontend.components.class :as class-component]
             [frontend.components.cmdk.core :as cmdk]
+            [frontend.components.block :as block]
             [frontend.components.settings :as settings]
             [frontend.components.diff :as diff]
             [frontend.components.encryption :as encryption]
@@ -23,7 +23,6 @@
             [frontend.components.whiteboard :as whiteboard]
             [frontend.components.user.login :as login]
             [frontend.components.repo :as repo]
-            [frontend.components.db-based.page :as db-page]
             [frontend.components.property.dialog :as property-dialog]
             [frontend.config :as config]
             [frontend.context.i18n :refer [t]]
@@ -845,12 +844,9 @@
 
 (defmethod handle :class/configure [[_ page]]
   (shui/dialog-open!
-    #(vector :<>
-       (class-component/configure page {})
-       (db-page/page-properties page {:configure? true
-                                      :mode :tag}))
-    {:label "page-configure"
-     :align :top}))
+   #(block/block-container {} page)
+   {:label "page-configure"
+    :align :top}))
 
 (defmethod handle :file/alter [[_ repo path content]]
   (p/let [_ (file-handler/alter-file repo path content {:from-disk? true})]
@@ -951,7 +947,7 @@
   (when-let [blocks (and block (db-model/get-block-immediate-children (state/get-current-repo) (:block/uuid block)))]
     (editor-handler/toggle-blocks-as-own-order-list! blocks)))
 
-(defmethod handle :editor/new-property [[_ {:keys [block] :as opts}]]
+(defmethod handle :editor/new-property [[_ {:keys [block target] :as opts}]]
   (p/do!
    (editor-handler/save-current-block!)
    (let [editing-block (state/get-edit-block)
@@ -994,10 +990,11 @@
                                                                 content'
                                                                 (assoc :custom-content content'))))))))))]
      (when (seq blocks)
-       (let [input (some-> (state/get-edit-input-id)
-                           (gdom/getElement))]
-         (if input
-           (shui/popup-show! input
+       (let [target' (or target
+                       (some-> (state/get-edit-input-id)
+                            (gdom/getElement)))]
+         (if target'
+           (shui/popup-show! target'
                              #(property-dialog/dialog blocks opts')
                              {:align "start"
                               :as-dropdown? true

+ 36 - 27
src/main/frontend/handler/page.cljs

@@ -360,7 +360,8 @@
                              (if (= \# (first q))
                                (subs q 1)
                                q))
-              last-pattern (str "#" (when wrapped? page-ref/left-brackets) last-pattern)]
+              last-pattern (str "#" (when wrapped? page-ref/left-brackets) last-pattern)
+              tag-in-page-auto-complete? (= page-ref/right-brackets (common-util/safe-subs edit-content current-pos (+ current-pos 2)))]
           (p/do!
            (editor-handler/insert-command! id
                                            (if (and class? (not inline-tag?)) "" (str "#" wrapped-tag))
@@ -368,49 +369,57 @@
                                            {:last-pattern last-pattern
                                             :end-pattern (when wrapped? page-ref/right-brackets)
                                             :command :page-ref})
-           (when db-based?
+           (when (and db-based? (not tag-in-page-auto-complete?))
              (let [tag (string/trim chosen)
-                   edit-block (state/get-edit-block)]
+                   edit-block (state/get-edit-block)
+                   create-opts {:redirect? false
+                                :create-first-block? false}]
                (when (:block/uuid edit-block)
                  (p/let [result (when-not (de/entity? chosen-result) ; page not exists yet
                                   (if class?
-                                    (<create-class! tag {:redirect? false
-                                                         :create-first-block? false})
-                                    (<create! tag {:redirect? false
-                                                   :create-first-block? false})))]
+                                    (<create-class! tag create-opts)
+                                    (<create! tag create-opts)))]
                    (when class?
-                     (let [tag-entity (or (when (de/entity? chosen-result) chosen-result) result)]
-                       (add-tag (state/get-current-repo) (:block/uuid edit-block) tag-entity)))))))
+                     (let [tag-entity (or (when (de/entity? chosen-result) chosen-result) result)
+                           hash-idx (string/last-index-of (subs edit-content 0 current-pos) last-pattern)
+                           add-tag-to-nearest-node? (= page-ref/right-brackets (common-util/safe-subs edit-content (- hash-idx 2) hash-idx))
+                           nearest-node (some-> (editor-handler/get-nearest-page) string/trim)]
+                       (if (and add-tag-to-nearest-node? (not (string/blank? nearest-node)))
+                         (when-let [e (db/get-page nearest-node)]
+                           (add-tag (state/get-current-repo) (:block/uuid e) tag-entity))
+                         (add-tag (state/get-current-repo) (:block/uuid edit-block) tag-entity))))))))
 
            (when input (.focus input)))))
       (fn [chosen-result e]
         (util/stop e)
         (state/clear-editor-action!)
-        (let [chosen-result (if (:block/uuid chosen-result)
-                              (db/entity [:block/uuid (:block/uuid chosen-result)])
-                              chosen-result)
-              chosen (:block/title chosen-result)
-              chosen' (string/replace-first chosen (str (t :new-page) " ") "")
-              ref-text (if (and (de/entity? chosen-result) (not (ldb/page? chosen-result)))
-                         (cond
-                           db-based?
-                           (page-ref/->page-ref (:block/uuid chosen-result))
-                           :else
-                           (block-ref/->block-ref (:block/uuid chosen-result)))
-                         (get-page-ref-text chosen'))]
+        (p/let [chosen-result (if (:block/uuid chosen-result)
+                                (db/entity [:block/uuid (:block/uuid chosen-result)])
+                                chosen-result)
+                chosen (:block/title chosen-result)
+                chosen' (string/replace-first chosen (str (t :new-page) " ") "")
+                ref-text (if (and (de/entity? chosen-result) (not (ldb/page? chosen-result)))
+                           (cond
+                             db-based?
+                             (page-ref/->page-ref (:block/uuid chosen-result))
+                             :else
+                             (block-ref/->block-ref (:block/uuid chosen-result)))
+                           (get-page-ref-text chosen'))
+                result (when db-based?
+                         (when-not (de/entity? chosen-result)
+                           (<create! chosen'
+                                     {:redirect? false
+                                      :create-first-block? false})))
+                ref-text' (if result (page-ref/->page-ref (:block/title result)) ref-text)]
           (p/do!
            (editor-handler/insert-command! id
-                                           ref-text
+                                           ref-text'
                                            format
                                            {:last-pattern (str page-ref/left-brackets (if (editor-handler/get-selected-text) "" q))
                                             :end-pattern page-ref/right-brackets
                                             :postfix-fn   (fn [s] (util/replace-first page-ref/right-brackets s ""))
                                             :command :page-ref})
-           (p/let [result (when-not (de/entity? chosen-result)
-                            (<create! chosen'
-                                      {:redirect? false
-                                       :create-first-block? false}))
-                   chosen-result (or result chosen-result)]
+           (p/let [chosen-result (or result chosen-result)]
              (when (de/entity? chosen-result)
                (state/conj-block-ref! chosen-result)))))))))
 

+ 31 - 1
src/main/frontend/worker/db/migrate.cljs

@@ -128,6 +128,32 @@
     (d/reset-schema! conn (update schema :block/type #(assoc % :db/cardinality :db.cardinality/one)))
     []))
 
+(defn- deprecate-class-parent
+  [conn _search-db]
+  (let [db @conn
+        datoms (d/datoms db :avet :class/parent)]
+    (->> (set (map :e datoms))
+         (mapcat
+          (fn [id]
+            (let [value (:db/id (:class/parent (d/entity db id)))]
+              [[:db/retract id :class/parent]
+               [:db/add id :logseq.property/parent value]]))))))
+
+(defn- deprecate-class-schema-properties
+  [conn _search-db]
+  (let [db @conn
+        datoms (d/datoms db :avet :class/schema.properties)]
+    (->> (set (map :e datoms))
+         (mapcat
+          (fn [id]
+            (let [values (map :db/id (:class/schema.properties (d/entity db id)))]
+              (concat
+               [[:db/retract id :class/schema.properties]]
+               (map
+                 (fn [value]
+                   [:db/add id :logseq.property.class/properties value])
+                 values))))))))
+
 (defn- add-addresses-in-kvs-table
   [^Object sqlite-db]
   (let [columns (->> (.exec sqlite-db #js {:sql "SELECT NAME FROM PRAGMA_TABLE_INFO('kvs')"
@@ -179,7 +205,11 @@
    [11 {:fix property-checkbox-type-non-ref}]
    [12 {:fix update-block-type-many->one}]
    [13 {:classes [:logseq.class/Journal]
-        :properties [:logseq.property.journal/title-format]}]])
+        :properties [:logseq.property.journal/title-format]}]
+   [14 {:properties [:logseq.property/parent]
+        :fix deprecate-class-parent}]
+   [15 {:properties [:logseq.property.class/properties]
+        :fix deprecate-class-schema-properties}]])
 
 (let [max-schema-version (apply max (map first schema-version->updates))]
   (assert (<= db-schema/version max-schema-version))

+ 4 - 2
src/main/frontend/worker/handler/page/db_based/page.cljs

@@ -19,8 +19,10 @@
                                              (:block/type page) (:block/type page)
                                              :else "page"))
           page' (merge page
-                       (when tags {:block/tags (mapv #(hash-map :db/id
-                                                                (:db/id (d/entity @conn [:block/uuid %])))
+                       (when tags {:block/tags (mapv (fn [tag]
+                                                       (if (uuid? tag)
+                                                         (d/entity @conn [:block/uuid tag])
+                                                         tag))
                                                      tags)}))
           property-vals-tx-m
           ;; Builds property values for built-in properties like logseq.property.pdf/file

+ 1 - 1
src/main/frontend/worker/rtc/db_listener.cljs

@@ -22,7 +22,7 @@
 (def ^:private watched-attrs
   #{:block/title :block/created-at :block/updated-at :block/alias
     :block/tags :block/type :block/schema :block/link :block/journal-day
-    :class/parent :class/schema.properties :property/schema.classes :property.value/content
+    :property/schema.classes :property.value/content
     :db/index :db/valueType :db/cardinality})
 
 (def ^:private watched-attr-ns

+ 0 - 2
src/main/frontend/worker/rtc/remote_update.cljs

@@ -335,8 +335,6 @@
     :block/tags
     :block/link
     :block/journal-day
-    :class/parent
-    :class/schema.properties
     :property/schema.classes
     :property.value/content})
 

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
src/rtc_e2e_test/example.cljs


+ 4 - 4
src/test/frontend/db/db_based_model_test.cljs

@@ -39,7 +39,7 @@
       ;; set class2's parent to class1
       (let [class2 (db/get-case-page "class2")]
         (db/transact! [{:db/id (:db/id class2)
-                        :class/parent (:db/id class)}]))
+                        :logseq.property/parent (:db/id class)}]))
       (test-helper/save-block! repo sbid "Block 2" {:tags ["class2"]})
       (is (= (map :db/id (model/get-class-objects repo (:db/id class)))
              [(:db/id (db/entity [:block/uuid fbid]))
@@ -76,9 +76,9 @@
         class2 (db/get-case-page "class2")
         class3 (db/get-case-page "class3")
         _ (db/transact! [{:db/id (:db/id class2)
-                          :class/parent (:db/id class1)}
+                          :logseq.property/parent (:db/id class1)}
                          {:db/id (:db/id class3)
-                          :class/parent (:db/id class2)}])]
+                          :logseq.property/parent (:db/id class2)}])]
     (is
-     (= (model/get-class-children repo (:db/id (db/get-case-page "class1")))
+     (= (model/get-structured-children repo (:db/id (db/get-case-page "class1")))
         [(:db/id class2) (:db/id class3)]))))

+ 1 - 1
src/test/frontend/handler/repo_test.cljs

@@ -25,7 +25,7 @@
 
     (docs-graph-helper/docs-graph-assertions db graph-dir (map :file/path files))
     (testing "Additional Counts"
-      (is (= 76968 (count (d/datoms db :eavt))) "Correct datoms count")
+      (is (= 76945 (count (d/datoms db :eavt))) "Correct datoms count")
 
       (is (= 7047
              (ffirst

+ 3 - 3
src/test/frontend/worker/rtc/db_listener_test.cljs

@@ -87,7 +87,7 @@
     (let [conn (d/conn-from-db empty-db)
           tx-data [[:db/add 62 :block/uuid #uuid "66856a29-6eb3-4122-af97-8580a853c6a6" 536870954]
                    [:db/add 62 :block/updated-at 1720019497643 536870954]
-                   [:db/add 62 :class/parent 4 536870954]
+                   [:db/add 62 :logseq.property/parent 4 536870954]
                    [:db/add 62 :block/created-at 1720019497643 536870954]
                    [:db/add 62 :block/format :markdown 536870954]
                    [:db/add 62 :db/ident :user.class/zzz 536870954]
@@ -106,8 +106,8 @@
                        [:block/created-at "[\"~#'\",1720019497643]"]
                        [:block/title "[\"~#'\",\"zzz\"]"]
                        [:block/type "[\"~#'\",\"class\"]"]
-                       ;;1. no :class/parent, because db/id 4 block doesn't exist in empty-db
-                       ;;2. shouldn't have :db/ident, :db/ident is special, will be handled later
+                       [:logseq.property/parent "[\"~#'\",4]"]
+                       ;;1. shouldn't have :db/ident, :db/ident is special, will be handled later
                        ]}]]
            (map (fn [[op-type _t op-value]]
                   [op-type (cond-> op-value

Некоторые файлы не были показаны из-за большого количества измененных файлов