From fe651961e40e2ddef7cdc8a43d9ae3b1c3819a27 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Sat, 25 Apr 2026 16:00:48 +0800 Subject: [PATCH] Improve property pill behavior and property editing UX --- deps/db/src/logseq/db.cljs | 3 ++- .../src/logseq/outliner/property.cljs | 21 ++++++++++--------- .../test/logseq/outliner/property_test.cljs | 18 ++++++++++++++++ src/main/frontend/components/block.cljs | 21 ++++++++++++------- src/main/frontend/components/block.css | 19 +++++++++++++++-- src/main/frontend/components/property.css | 4 +++- .../frontend/components/property/value.cljs | 11 ++++++---- .../frontend/components/property/value.css | 3 ++- 8 files changed, 73 insertions(+), 27 deletions(-) diff --git a/deps/db/src/logseq/db.cljs b/deps/db/src/logseq/db.cljs index 7e3758709f..f249a698b2 100644 --- a/deps/db/src/logseq/db.cljs +++ b/deps/db/src/logseq/db.cljs @@ -873,7 +873,8 @@ * :title - pluralized class title * :entities - node entities that reference the target via ref properties" [db target-id] - (when (and db target-id (d/entity db target-id)) + (when (and db target-id + (not (:logseq.property/created-from-property (d/entity db target-id)))) (let [*attr->bidirectional? (volatile! {}) bidirectional-property-attr-cached? (fn [attr] diff --git a/deps/outliner/src/logseq/outliner/property.cljs b/deps/outliner/src/logseq/outliner/property.cljs index a42751db4e..61790771be 100644 --- a/deps/outliner/src/logseq/outliner/property.cljs +++ b/deps/outliner/src/logseq/outliner/property.cljs @@ -776,25 +776,26 @@ (defn- bottom-position-property? [db block property] (let [property-id (:db/ident property) - property-type (:logseq.property/type property)] + property-type (:logseq.property/type property) + node-many? (and (= :node property-type) + (= :db.cardinality/many (:db/cardinality property))) + default-bottom? (and (not= :url property-type) + (or node-many? + (not= :default property-type) + (seq (:property/closed-values property))) + (not (schema-or-tag-related-property? property-id)))] (if (tag-class-page? db block) (or (contains? #{:logseq.property.class/extends :logseq.property.class/enable-bidirectional?} property-id) - (and (not= :url property-type) - (or (not= :default property-type) - (seq (:property/closed-values property))) - (not (schema-or-tag-related-property? property-id)))) - (and (not= :url property-type) - (or (not= :default property-type) - (seq (:property/closed-values property))) - (not (schema-or-tag-related-property? property-id)))))) + default-bottom?) + default-bottom?))) (defn- resolved-property-position [db block property] (let [ui-position (:logseq.property/ui-position property)] (cond - (contains? #{:block-left :block-right :block-below} ui-position) + (contains? #{:properties :block-left :block-right :block-below} ui-position) ui-position (bottom-position-property? db block property) diff --git a/deps/outliner/test/logseq/outliner/property_test.cljs b/deps/outliner/test/logseq/outliner/property_test.cljs index 575fffa350..d67bc3ca90 100644 --- a/deps/outliner/test/logseq/outliner/property_test.cljs +++ b/deps/outliner/test/logseq/outliner/property_test.cljs @@ -399,6 +399,24 @@ :logseq.property/type :url :logseq.property/ui-position :block-left})))) + (testing "explicit properties ui-position keeps property in normal property rows" + (is (false? + (outliner-property/property-with-other-position? + nil + {} + {:db/ident :user.property/p1 + :logseq.property/type :number + :logseq.property/ui-position :properties})))) + + (testing "many node property without explicit ui-position defaults to bottom position" + (is (true? + (outliner-property/property-with-other-position? + nil + {} + {:db/ident :user.property/p1 + :logseq.property/type :node + :db/cardinality :db.cardinality/many})))) + (testing "bidirectional config property stays in normal property rows" (is (false? (outliner-property/property-with-other-position? diff --git a/src/main/frontend/components/block.cljs b/src/main/frontend/components/block.cljs index 1e607b8634..34a6900151 100644 --- a/src/main/frontend/components/block.cljs +++ b/src/main/frontend/components/block.cljs @@ -2692,16 +2692,21 @@ (defn- bottom-property-pill-cp [block property opts] - [:div.bottom-property-pill.bottom-property-pill-focusable - {:key (str (:db/id block) "-" (:db/id property)) - :data-bottom-pill-focusable true - :data-bottom-row-nav true - :tab-index -1 - :on-key-down handle-bottom-pill-key-down!} + (let [many-node? (and (= :node (:logseq.property/type property)) + (= :db.cardinality/many (:db/cardinality property)))] + [:div.bottom-property-pill.bottom-property-pill-focusable + {:key (str (:db/id block) "-" (:db/id property)) + :class (util/classnames [{:bottom-property-pill-wrap many-node?}]) + :data-bottom-pill-focusable true + :data-bottom-row-nav true + :tab-index -1 + :on-key-down handle-bottom-pill-key-down!} [:div.flex.flex-row.items-center (property-component/property-key-cp block property opts) [:div.select-none ":"]] - [:div {:class "bottom-property-content ls-block property-value-container" + [:div {:class (util/classnames + ["bottom-property-content ls-block property-value-container" + {:bottom-property-content-wrap many-node?}]) :style {:min-height 20}} (pv/property-value block property opts) (when (contains? #{:date :datetime} (:logseq.property/type property)) @@ -2715,7 +2720,7 @@ (.querySelector ".jtrigger"))] (.click trigger) (some-> trigger .focus)))} - (ui/icon "edit" {:size 15})])]]) + (ui/icon "edit" {:size 15})])]])) (defn- block-below-positioned-properties-cp [block properties opts show-hidden-properties-toggle? show-add-property-button?] diff --git a/src/main/frontend/components/block.css b/src/main/frontend/components/block.css index 83a594af2d..4930819ce9 100644 --- a/src/main/frontend/components/block.css +++ b/src/main/frontend/components/block.css @@ -1070,12 +1070,19 @@ html.is-mac { } .bottom-property-pill { - @apply inline-flex items-center gap-1 rounded-full px-2 py-0.5 h-6 max-w-full; + @apply inline-flex items-center gap-1 rounded-full px-2 py-0.5 h-6 max-w-full text-base; background-color: var(--ls-secondary-background-color); box-shadow: inset 0 0 0 1px var(--ls-border-color); position: relative; } + .bottom-property-pill.bottom-property-pill-wrap { + @apply h-auto; + min-height: 1.5rem; /* keep default 24px baseline, allow growth only when wrapped */ + padding-top: 0; + padding-bottom: 0; + } + .bottom-property-pill-focusable:focus { outline: none; box-shadow: inset 0 0 0 1px var(--ls-link-text-color); @@ -1090,7 +1097,15 @@ html.is-mac { } .bottom-property-content { - @apply flex items-center gap-1 min-w-0 relative; + @apply flex items-center gap-1 min-w-0 relative text-base; + } + + .bottom-property-content.bottom-property-content-wrap { + @apply flex-1 flex-wrap min-w-0; + } + + .bottom-property-pill-wrap .multi-values.jtrigger { + @apply min-w-0; } .bottom-property-pill .property-key-inner { diff --git a/src/main/frontend/components/property.css b/src/main/frontend/components/property.css index adbd9740b2..66b32b0b33 100644 --- a/src/main/frontend/components/property.css +++ b/src/main/frontend/components/property.css @@ -291,7 +291,9 @@ } .ui__button.empty-btn, .empty-text-btn { - @apply !h-6 !px-0 !text-base opacity-50 font-normal; + @apply !h-6 !px-0 opacity-50 font-normal; + font-size: inherit; + line-height: inherit; } .ui__button.empty-btn:hover, .empty-text-btn:hover { diff --git a/src/main/frontend/components/property/value.cljs b/src/main/frontend/components/property/value.cljs index 4ddd2c375e..7555f686b6 100644 --- a/src/main/frontend/components/property/value.cljs +++ b/src/main/frontend/components/property/value.cljs @@ -1359,7 +1359,8 @@ *ref (hooks/use-ref nil) *input-ref (hooks/use-ref nil) number-value (db-property/property-value-content value-block) - [value set-value!] (hooks/use-state number-value) + number-value-str (if (some? number-value) (str number-value) "") + [value set-value!] (hooks/use-state number-value-str) [*value _] (hooks/use-state (atom value)) set-property-value! (fn [value & {:keys [exit-editing?] :or {exit-editing? true}}] @@ -1385,9 +1386,9 @@ (hooks/use-effect! (fn [] - (set-value! number-value) + (set-value! number-value-str) #()) - [number-value]) + [number-value-str]) [:div.ls-number.flex.flex-1.jtrigger {:ref *ref @@ -1433,7 +1434,9 @@ (.focus (rum/deref *ref))) nil))))}) - value)])) + (if (string/blank? value) + (property-empty-btn-value property) + value))])) (rum/defcs property-scalar-value-aux < rum/static rum/reactive [state block property value* {:keys [editing? on-chosen] diff --git a/src/main/frontend/components/property/value.css b/src/main/frontend/components/property/value.css index 666a4daf85..ba8bd75648 100644 --- a/src/main/frontend/components/property/value.css +++ b/src/main/frontend/components/property/value.css @@ -51,6 +51,7 @@ min-width: 3em; } -.ls-properties-area .empty-btn { +.ls-properties-area .empty-btn, +.ls-properties-area .empty-text-btn { @apply !text-base; }