enhance(ux): bottom properties

This commit is contained in:
Tienson Qin
2026-04-24 00:02:53 +08:00
parent e09612ab33
commit 19450bcc77
8 changed files with 457 additions and 197 deletions

View File

@@ -767,23 +767,37 @@
(= "logseq.property.class" property-ns)
(contains? db-property/schema-properties property-id))))
(defn- tag-class-page?
[db block]
(or (= :logseq.class/Tag (:db/ident block))
(and db
(ldb/class-instance? (entity-plus/entity-memoized db :logseq.class/Tag) block))))
(defn- bottom-position-property?
[property]
[db block property]
(let [property-id (:db/ident property)
property-type (:logseq.property/type property)]
(and (not= :url property-type)
(or (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))))))
(defn- resolved-property-position
[property]
[db block property]
(let [ui-position (:logseq.property/ui-position property)]
(cond
(contains? #{:block-left :block-right :block-below} ui-position)
ui-position
(bottom-position-property? property)
(bottom-position-property? db block property)
:block-below
:else
@@ -792,7 +806,7 @@
(defn- property-with-position?
[db property-id block position]
(when-let [property (entity-plus/entity-memoized db property-id)]
(let [property-position' (resolved-property-position property)]
(let [property-position' (resolved-property-position db block property)]
(and
(= property-position' position)
(not (and (:logseq.property/hide-empty-value property)
@@ -800,20 +814,28 @@
(not (:logseq.property/hide? property))
(not (and
(= property-position' :block-below)
(nil? (get block property-id))))))))
(nil? (get block property-id))
(not (tag-class-page? db block))))))))
(defn property-with-other-position?
[property]
(not= :properties (resolved-property-position property)))
[db block property]
(not= :properties (resolved-property-position db block property)))
(defn get-block-positioned-properties
[db eid position]
(let [block (d/entity db eid)
own-properties (:block.temp/property-keys block)]
(->> (:classes-properties (get-block-classes-properties db eid))
(map :db/ident)
(concat own-properties)
(distinct)
own-properties (:block.temp/property-keys block)
tag-page-pill-properties (when (tag-class-page? db block)
[:logseq.property.class/extends
:logseq.property.class/enable-bidirectional?])
positioned-property-ids (if (tag-class-page? db block)
(->> (concat own-properties tag-page-pill-properties)
distinct)
(->> (:classes-properties (get-block-classes-properties db eid))
(map :db/ident)
(concat own-properties)
distinct))]
(->> positioned-property-ids
(filter (fn [id] (property-with-position? db id block position)))
(map #(d/entity db %))
(ldb/sort-by-order))))

View File

@@ -350,6 +350,8 @@
(testing "explicit non-properties ui-position remains other-position"
(is (true?
(outliner-property/property-with-other-position?
nil
{}
{:db/ident :user.property/p1
:logseq.property/type :number
:logseq.property/ui-position :block-left}))))
@@ -357,12 +359,16 @@
(testing "number 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 :number}))))
(testing "default property without closed values stays in normal property rows"
(is (false?
(outliner-property/property-with-other-position?
nil
{}
{:db/ident :user.property/p1
:logseq.property/type :default
:property/closed-values []}))))
@@ -370,6 +376,8 @@
(testing "default property with closed values is positioned in bottom row"
(is (true?
(outliner-property/property-with-other-position?
nil
{}
{:db/ident :user.property/p1
:logseq.property/type :default
:property/closed-values [{:db/id 1}]}))))
@@ -377,12 +385,16 @@
(testing "url property without explicit ui-position stays in normal property rows"
(is (false?
(outliner-property/property-with-other-position?
nil
{}
{:db/ident :user.property/p1
:logseq.property/type :url}))))
(testing "explicit left/right ui-position remains in positioned rows"
(is (true?
(outliner-property/property-with-other-position?
nil
{}
{:db/ident :user.property/p1
:logseq.property/type :url
:logseq.property/ui-position :block-left}))))
@@ -390,20 +402,28 @@
(testing "bidirectional config property stays in normal property rows"
(is (false?
(outliner-property/property-with-other-position?
nil
{}
{:db/ident :logseq.property.class/enable-bidirectional?
:logseq.property/type :checkbox}))))
(testing "tag properties and schema-related properties stay in normal property rows"
(is (false?
(outliner-property/property-with-other-position?
nil
{}
{:db/ident :block/tags
:logseq.property/type :class})))
(is (false?
(outliner-property/property-with-other-position?
nil
{}
{:db/ident :logseq.property.class/properties
:logseq.property/type :property})))
(is (false?
(outliner-property/property-with-other-position?
nil
{}
{:db/ident :logseq.property/public?
:logseq.property/type :checkbox}))))

View File

@@ -2585,49 +2585,64 @@
(rum/defc block-positioned-properties
[config block position]
(let [properties (outliner-property/get-block-positioned-properties (db/get-db) (:db/id block) position)
has-viewable-properties? (seq properties)
opts (merge config
{:icon? true
:page-cp page-cp
:block-cp blocks-container
:inline-text inline-text
:other-position? true
:property-position position})]
(when (seq properties)
(case position
:property-position position})
show-page-hidden-properties-toggle? (and (ldb/page? block)
(not config/publishing?))
show-hidden-properties-toggle? (and has-viewable-properties?
show-page-hidden-properties-toggle?
(property-component/has-hidden-properties? block config))
show-page-add-property? (and (ldb/page? block)
(not (ldb/class? block))
(not config/publishing?))
show-add-property-button? (and has-viewable-properties?
show-page-add-property?)]
(case position
:block-below
[:div.positioned-properties.block-below.flex.flex-row.gap-2.items-center.flex-wrap.text-sm.overflow-x-hidden
(for [[idx property] (map-indexed vector properties)]
[:div.bottom-property-pair.flex.flex-row.items-center.gap-1
{:key (str (:db/id block) "-" (:db/id property))}
[:div.flex.flex-row.items-center
(property-component/property-key-cp block property opts)
[:div.select-none ":"]]
[:div.bottom-property-content.ls-block.property-value-container
{:style {:min-height 20}}
(pv/property-value block property opts)
(when (and (contains? #{:date :number} (:logseq.property/type property))
(not config/publishing?))
[:button.bottom-property-edit-icon.select-none
{:type "button"
:on-click (fn [e]
(util/stop e)
(when-let [trigger
(some-> (.-currentTarget e)
(.closest ".bottom-property-content")
(.querySelector ".jtrigger"))]
(.click trigger)
(some-> trigger .focus)))}
(ui/icon "edit" {:size 14})])]
(when (< idx (dec (count properties)))
[:div.bottom-property-separator.select-none ","])])
(when (and (ldb/page? block) (not config/publishing?))
(property-component/hidden-properties-toggle-button block))]
[:div.positioned-properties.flex.flex-row.gap-1.select-none.h-6.self-start
{:class (name position)}
(for [property properties]
(rum/with-key
(pv/property-value block property (assoc opts :show-tooltip? true))
(str (:db/id block) "-" (:db/id property))))]))))
(when has-viewable-properties?
[:div.positioned-properties.block-below.flex.flex-col.gap-1.text-sm.overflow-x-hidden
[:div.bottom-properties-row.flex.flex-row.gap-2.items-center.flex-wrap.overflow-x-hidden
(for [property properties]
[:div.bottom-property-pill
{:key (str (:db/id block) "-" (:db/id property))}
[:div.flex.flex-row.items-center
(property-component/property-key-cp block property opts)
[:div.select-none ":"]]
[:div.bottom-property-content.ls-block.property-value-container
{:style {:min-height 20}}
(pv/property-value block property opts)
(when (contains? #{:number :date :datetime} (:logseq.property/type property))
[:button.bottom-property-edit-icon.select-none
{:type "button"
:on-click (fn [e]
(util/stop e)
(when-let [trigger
(some-> (.-currentTarget e)
(.closest ".bottom-property-content")
(.querySelector ".jtrigger"))]
(.click trigger)
(some-> trigger .focus)))}
(ui/icon "edit" {:size 14})])]])
(when show-hidden-properties-toggle?
(property-component/hidden-properties-toggle-button block {:icon-only? true}))
(when show-add-property-button?
(property-component/new-property block (assoc opts
:property-position :block-below
:icon-only? true)))]])
(when (seq properties)
[:div.positioned-properties.flex.flex-row.gap-1.select-none.h-6.self-start
{:class (name position)}
(for [property properties]
(rum/with-key
(pv/property-value block property (assoc opts :show-tooltip? true))
(str (:db/id block) "-" (:db/id property))))]))))
(rum/defc block-reactions < rum/reactive db-mixins/query
[block]

View File

@@ -1059,29 +1059,58 @@ html.is-mac {
@apply flex;
}
.property-k {
color: var(--ls-primary-text-color);
.bottom-properties-row {
@apply gap-1.5 py-1;
}
.bottom-property-pair {
@apply min-w-0;
.bottom-property-pill {
@apply inline-flex items-center gap-1 rounded-full px-2 py-0.5 h-6 max-w-full;
background-color: var(--ls-secondary-background-color);
box-shadow: inset 0 0 0 1px var(--ls-border-color);
}
.bottom-property-content {
@apply flex items-center gap-1 min-w-0;
}
.bottom-property-separator {
@apply text-muted-foreground;
.bottom-property-pill .property-key-inner {
@apply flex items-center min-w-0;
}
.bottom-property-pill .property-k {
@apply w-auto flex-none whitespace-nowrap leading-[20px];
max-width: none;
color: var(--ls-primary-text-color);
}
.bottom-property-pill a.property-k,
.bottom-property-pill .property-key-inner a {
color: var(--ls-primary-text-color);
}
.ls-new-property {
@apply m-0;
}
.ls-new-property .jtrigger,
.bottom-property-control-btn {
@apply inline-flex items-center rounded-full px-1.5 py-0.5 h-6 text-xs text-muted-foreground;
box-shadow: inset 0 0 0 1px var(--ls-border-color);
}
.bottom-property-edit-icon {
@apply text-muted-foreground inline-flex items-center justify-center p-0 bg-transparent border-0 cursor-pointer;
@apply inline-flex items-center justify-center p-0 bg-transparent border-0 text-muted-foreground cursor-pointer;
}
.ls-block.property-value-container {
min-width: 0;
flex-shrink: 1;
flex: 0 1 auto;
width: auto;
min-height: 20px;
}
.property-value {
@apply pl-0 min-h-[20px];
}
.property-value-inner,
@@ -1096,6 +1125,11 @@ html.is-mac {
@apply self-center;
vertical-align: middle;
}
.bullet-container,
.property-icon {
display: none;
}
}
.block-tags {

View File

@@ -3,7 +3,6 @@
(:require [clojure.set :as set]
[clojure.string :as string]
[frontend.components.dnd :as dnd]
[frontend.components.icon :as icon-component]
[frontend.components.property.config :as property-config]
[frontend.components.property.value :as pv]
[frontend.components.select :as select]
@@ -307,48 +306,13 @@
(db-property/built-in-display-title property t)))
(rum/defc property-key-cp < rum/static
[block property {:keys [other-position? class-schema?]}]
(let [icon (:logseq.property/icon property)]
[:div.property-key-inner.jtrigger-view
;; icon picker
(when-not other-position?
(let [content-fn (fn [{:keys [id]}]
(icon-component/icon-search
{:on-chosen
(fn [_e icon]
(if icon
(db-property-handler/set-block-property! (:db/id property)
:logseq.property/icon icon)
(db-property-handler/remove-block-property! (:db/id property)
:logseq.property/icon))
(shui/popup-hide! id))
:icon-value icon
:del-btn? (boolean icon)}))]
[:div.property-icon
(shui/trigger-as
:button.property-m
(-> (when-not config/publishing?
{:on-click (fn [^js e]
(shui/popup-show! (.-target e) content-fn
{:as-dropdown? true :auto-focus? true
:content-props {:onEscapeKeyDown #(.preventDefault %)}}))})
(assoc :class "flex items-center"))
(if icon
(icon-component/icon icon {:size 15 :color? true})
(property-icon property nil)))]))
(if config/publishing?
[:a.property-k.flex.select-none.jtrigger
{:on-click #(route-handler/redirect-to-page! (:block/uuid property))}
(db-property/built-in-display-title property t)]
(property-key-title block property class-schema?))]))
(defn- bidirectional-property-icon-cp
[property]
(if-let [icon (:logseq.property/icon property)]
(icon-component/icon icon {:size 15 :color? true})
(ui/icon "letter-b" {:class "opacity-50" :size 15})))
[block property {:keys [class-schema?]}]
[:div.property-key-inner.jtrigger-view
(if config/publishing?
[:a.property-k.flex.select-none.jtrigger
{:on-click #(route-handler/redirect-to-page! (:block/uuid property))}
(db-property/built-in-display-title property t)]
(property-key-title block property class-schema?))])
(rum/defcs bidirectional-values-cp < rum/static
{:init (fn [state]
@@ -370,11 +334,9 @@
[bidirectional-properties]
(when (seq bidirectional-properties)
(for [{:keys [class title entities]} bidirectional-properties]
[:div.property-pair.items-start {:key (str "bidirectional-" title)}
[:div.property-key
[:div.property-pair.property-panel-row {:key (str "bidirectional-" title)}
[:div.property-key-panel
[:div.property-key-inner
[:div.property-icon
(bidirectional-property-icon-cp class)]
(if class
[:a.property-k.flex.select-none.w-full.jtrigger
{:on-click (fn [e]
@@ -382,8 +344,11 @@
(route-handler/redirect-to-page! (:block/uuid class)))}
title]
[:div.property-k.flex.select-none.w-full title])]]
[:div.ls-block.property-value-container.flex.flex-row.gap-1.items-start
[:div.property-value.flex.flex-1
[:div.ls-block.property-value-container.property-value-panel
[:div.property-panel-bullet {:aria-hidden true}
[:span.bullet-container
[:span.bullet]]]
[:div.property-value.property-value-panel-inner.flex.flex-1
(bidirectional-values-cp entities)]]])))
(rum/defcs ^:large-vars/cleanup-todo property-input < rum/reactive
@@ -481,21 +446,25 @@
(rum/defcs new-property < rum/reactive
[state block opts]
(when-not config/publishing?
(let [add-new-property! (fn [e]
(let [icon-only? (:icon-only? opts)
add-new-property! (fn [e]
(state/pub-event! [:editor/new-property (merge opts {:block block
:target (.-target e)})]))]
[:div.ls-new-property {:style {:margin-left 7 :margin-top 1 :font-size 15}}
[:a.flex.jtrigger
{:tab-index 0
:on-click add-new-property!
:on-key-press (fn [e]
(when (contains? #{"Enter" " "} (util/ekey e))
(.preventDefault e)
(add-new-property! e)))}
[:div.flex.flex-row.items-center.shrink-0
(ui/icon "plus" {:size 15 :class "opacity-50"})
[:div.ml-1 {:style {:margin-top 1}}
(t :property/add-new)]]]])))
[:div.ls-new-property
(shui/button
{:variant :outline
:size :small
:class "jtrigger flex !px-2 !py-1"
:tab-index 0
:title (t :property/add-new)
:on-click add-new-property!
:on-key-press (fn [e]
(when (contains? #{"Enter" " "} (util/ekey e))
(.preventDefault e)
(add-new-property! e)))}
(ui/icon "plus")
(when-not icon-only?
(t :property/add-new)))])))
(defn- resolve-linked-block-if-exists
"Properties will be updated for the linked page instead of the refed block.
@@ -511,59 +480,64 @@
(db/sub-block (:db/id linked-block))
(db/sub-block (:db/id block))))
(defn- empty-panel-property-value?
[value]
(or (nil? value)
(and (map? value)
(= :logseq.property/empty-placeholder (:db/ident value)))
(and (coll? value)
(or (empty? value)
(every? (fn [item]
(and (map? item)
(= :logseq.property/empty-placeholder (:db/ident item))))
value)))))
(rum/defc property-cp <
rum/reactive
db-mixins/query
[block k v {:keys [inline-text page-cp sortable-opts] :as opts}]
[block k v {:keys [sortable-opts] :as opts}]
(when (keyword? k)
(when-let [property (db/sub-block (:db/id (db/entity k)))]
(let [type (get property :logseq.property/type :default)
closed-values? (seq (:property/closed-values property))
block? (and v
(not closed-values?)
(or (and (map? v) (:block/page v))
(and (coll? v)
(map? (first v))
(or (:block/page (first v))
(= :logseq.property/empty-placeholder (:db/ident (first v))))))
(contains? #{:default :url} type))
date? (= type :date)
datetime? (= type :datetime)
checkbox? (= type :checkbox)
number-type? (= type :number)
property-key-cp' (property-key-cp block property (assoc (select-keys opts [:class-schema?])
:block? block?
:inline-text inline-text
:page-cp page-cp))]
default-or-url? (contains? #{:default :url} type)
empty-value? (empty-panel-property-value? v)
show-panel-bullet? (or (not default-or-url?)
empty-value?)
property-key-cp' (property-key-cp block property (select-keys opts [:class-schema?]))]
[:div {:key (str "property-pair-" (:db/id block) "-" (:db/id property))
:class (cond
(or date? datetime? checkbox? number-type?)
"property-pair items-center"
:else
"property-pair items-start")
:class (util/classnames ["property-pair property-panel-row"
{:property-panel-row-empty empty-value?}])
:data-property-title (:block/title property)
:data-property-type (name type)}
(if (seq sortable-opts)
(dnd/sortable-item (assoc sortable-opts :class "property-key") property-key-cp')
[:div.property-key property-key-cp'])
(dnd/sortable-item
(assoc sortable-opts :class "property-key-panel")
property-key-cp')
[:div.property-key-panel
property-key-cp'])
(let [property-desc (when-not (= (:db/ident property) :logseq.property/description)
(:logseq.property/description property))
block' (assoc block (:db/ident property) v)]
[:div.ls-block.property-value-container.flex.flex-row.gap-1
{:class (if (contains? #{:checkbox :date :datetime} type)
"items-center"
"items-start")}
(when-not (or block? (and property-desc (:class-schema? opts)))
[:div.flex.items-center {:style {:height 28}}
[:div {:class "pl-1.5 -mr-[3px] opacity-60"}
[:span.bullet-container [:span.bullet]]]])
[:div.flex.flex-1
[:div.property-value.flex.flex-1
(if (:class-schema? opts)
(pv/property-value property (db/entity :logseq.property/description) opts)
(pv/property-value block' property opts))]]])]))))
(let [block' (assoc block (:db/ident property) v)]
[:div.ls-block.property-value-container.property-value-panel
(when show-panel-bullet?
[:div.property-panel-bullet {:aria-hidden true}
[:span.bullet-container
[:span.bullet]]])
[:div.property-value.property-value-panel-inner.flex.flex-1
(if (:class-schema? opts)
(pv/property-value property (db/entity :logseq.property/description) opts)
(pv/property-value block' property (assoc opts :suppress-inline-edit-icon? true)))]
(when (contains? #{:number :date :datetime} type)
[:button.property-panel-edit-btn.select-none
{:type "button"
:on-click (fn [e]
(util/stop e)
(when-let [trigger
(some-> (.-currentTarget e)
(.closest ".property-value-panel")
(.querySelector ".jtrigger"))]
(.click trigger)
(some-> trigger .focus)))}
(ui/icon "edit" {:size 14})])])]))))
(defn- entity-ref-value?
[value]
@@ -696,24 +670,120 @@
[block-uuid]
(contains? @*show-hidden-properties-block-ids block-uuid))
(defn has-hidden-properties?
[block {:keys [gallery-view?]}]
(let [current-db (db/get-db)
show-empty-and-hidden-state (some-> @state/state
(get :ui/show-empty-and-hidden-properties?)
deref)
{:keys [mode show? ids]} show-empty-and-hidden-state
show-empty-and-hidden-properties? (and show?
(or (= mode :global)
(and (set? ids) (contains? ids (:block/uuid block)))))
properties* (cond-> (:block/properties block)
(and (ldb/class? block)
(not (ldb/built-in? block)))
(assoc :logseq.property.class/enable-bidirectional?
(:logseq.property.class/enable-bidirectional? block)))
{:keys [properties recycled-only-property-ids]}
(sanitize-property-values-for-display properties*)
remove-built-in-or-other-position-properties
(fn [property-pairs show-in-hidden-properties?]
(remove (fn [property]
(let [id (if (vector? property) (first property) property)]
(or
(= id :block/tags)
(when-let [ent (db/entity id)]
(or
;; built-in
(and (not (ldb/public-built-in-property? ent))
(ldb/built-in? ent))
;; other position
(when-not (or
show-empty-and-hidden-properties?
show-in-hidden-properties?)
(outliner-property/property-with-other-position? current-db block ent))
(and gallery-view?
(contains? #{:logseq.property.class/properties} (:db/ident ent))))))))
property-pairs))
{:keys [all-classes classes-properties]} (outliner-property/get-block-classes-properties current-db (:db/id block))
classes-properties-set (set (map :db/ident classes-properties))
block-own-properties (->> properties
(remove (fn [[id _]] (contains? recycled-only-property-ids id)))
(remove (fn [[id _]] (classes-properties-set id))))
state-hide-empty-properties? (:ui/hide-empty-properties? (state/get-config))
hide-with-property-id (fn [property-id]
(let [property (db/entity property-id)]
(boolean
(cond
show-empty-and-hidden-properties?
false
state-hide-empty-properties?
(nil? (get properties property-id))
(and (:logseq.property/hide-empty-value property)
(nil? (get properties property-id)))
true
:else
(boolean (:logseq.property/hide? property))))))
property-hide-f (cond
config/publishing?
;; Publishing is read only so hide all blank properties as they
;; won't be edited and distract from properties that have values
(fn [[property-id property-value]]
(or (nil? property-value)
(hide-with-property-id property-id)))
state-hide-empty-properties?
(fn [[property-id property-value]]
;; User's selection takes precedence over config
(if (:logseq.property/hide? (db/entity property-id))
(hide-with-property-id property-id)
(nil? property-value)))
:else
(comp hide-with-property-id first))
{block-hidden-properties true
block-own-properties' false} (group-by property-hide-f block-own-properties)
class-properties (loop [classes all-classes
existing-properties (set (map first block-own-properties'))
result []]
(if-let [class (first classes)]
(let [cur-properties (->> (db-property/get-class-ordered-properties class)
(map :db/ident)
(remove existing-properties))]
(recur (rest classes)
(set/union existing-properties (set cur-properties))
(if (seq cur-properties)
(into result cur-properties)
result)))
result))
class-property-pairs (->> class-properties
(map (fn [p] [p (get properties p)]))
(remove (fn [[property-id _]]
(contains? recycled-only-property-ids property-id))))
hidden-properties (-> (concat block-hidden-properties
(filter property-hide-f class-property-pairs))
(remove-built-in-or-other-position-properties true))]
(boolean (seq hidden-properties))))
(rum/defc hidden-properties-toggle-button
[block]
[block {:keys [icon-only?] :as _opts}]
(let [block-uuid (:block/uuid block)]
(when block-uuid
(shui/button
{:variant :ghost
:size :sm
:class "px-1 text-muted-foreground h-6 text-xs"
:class "bottom-property-control-btn px-1 text-muted-foreground h-6 text-xs"
:title (t :property/hidden-properties)
:on-click (fn [e]
(util/stop e)
(toggle-hidden-properties-visibility! block-uuid))}
(t :property/hidden-properties)))))
(if icon-only?
[:span.select-none "⋯"]
(t :property/hidden-properties))))))
(rum/defc hidden-properties-cp
[block hidden-properties {:keys [show-hidden-properties?] :as opts}]
(when (and show-hidden-properties? (seq hidden-properties))
[:div.mt-1
(properties-section block hidden-properties opts)]))
(properties-section block hidden-properties opts)))
(rum/defc load-bidirectional-properties < rum/static
[block root-block? set-bidirectional-properties!]
@@ -737,6 +807,7 @@
(let [*bidirectional-properties (::bidirectional-properties state)
bidirectional-properties @*bidirectional-properties
id (::id state)
current-db (db/get-db)
db-id (:db/id (::block state))
block (db/sub-block db-id)
show-properties? (or sidebar-properties? tag-dialog?)
@@ -768,7 +839,7 @@
(when-not (or
show-empty-and-hidden-properties?
show-in-hidden-properties?)
(outliner-property/property-with-other-position? ent))
(outliner-property/property-with-other-position? current-db block ent))
(and (:gallery-view? opts)
(contains? #{:logseq.property.class/properties} (:db/ident ent))))))))
@@ -846,15 +917,12 @@
(not has-bidirectional-properties?))
nil
(and (empty? full-properties) (empty? hidden-properties) (not has-bidirectional-properties?))
(when show-properties?
(rum/with-key (new-property block opts) (str id "-add-property")))
:else
(let [remove-properties #{:logseq.property/icon :logseq.property/query}
properties' (->> (remove (fn [[k _v]] (contains? remove-properties k))
full-properties)
(remove (fn [[k _v]] (= k :logseq.property.class/properties))))
show-properties-panel? (or (seq properties') (seq bidirectional-properties))
page? (entity-util/page? block)
class? (entity-util/class? block)]
[:div.ls-properties-area
@@ -862,16 +930,15 @@
:class (util/classnames [{:ls-page-properties page?}])
:tab-index 0}
[:<>
(properties-section block properties' opts)
(bidirectional-properties-section bidirectional-properties)
(when show-properties-panel?
[:div.properties-panel
(properties-section block properties' opts)
(bidirectional-properties-section bidirectional-properties)])
(when-not class?
(hidden-properties-cp block hidden-properties
(assoc opts :show-hidden-properties? show-hidden-properties?)))
(when (and page? (not class?))
(rum/with-key (new-property block opts) (str id "-add-property")))
(when class?
(let [properties (->> (:logseq.property.class/properties block)
(map (fn [e] [(:db/ident e)])))
@@ -883,8 +950,9 @@
(property-key-cp block (db/entity :logseq.property.class/properties) {})]]
[:div.text-muted-foreground {:style {:margin-left 26}}
(t :class/tag-properties-desc)]]
[:div.ml-4
[:div.ml-4.gap-1.flex.flex-col
(properties-section block properties opts')
(hidden-properties-cp block hidden-properties
(assoc opts :show-hidden-properties? show-hidden-properties?))
(rum/with-key (new-property block opts') (str id "-class-add-property"))]]))]])))]))
[:div.ml-3
(rum/with-key (new-property block opts') (str id "-class-add-property"))]]]))]])))]))

View File

@@ -62,9 +62,70 @@
}
.ls-properties-area {
.property-pair {
@apply flex flex-row flex-wrap space-x-1;
.properties-panel {
@apply rounded-md overflow-hidden;
}
.properties-panel-header {
@apply px-3 py-2 text-xs font-medium text-muted-foreground border-b border-gray-04;
}
.property-pair.property-panel-row {
@apply grid items-start px-3 gap-3 w-full;
grid-template-columns: fit-content(260px) minmax(0, 1fr);
margin-left: 0;
}
.property-key-panel {
@apply flex items-center min-w-0;
min-height: 28px;
min-width: 150px;
font-size: inherit;
color: var(--ls-primary-text-color);
}
.property-key-panel .property-key-inner {
@apply w-full min-w-0;
}
.property-key-panel .property-k {
@apply block min-w-0 max-w-full overflow-hidden text-ellipsis whitespace-nowrap;
}
.property-value-panel.ls-block.property-value-container {
@apply flex flex-row items-start gap-1 w-full;
min-width: 0;
flex-shrink: 1;
min-height: 28px;
}
.property-pair.property-panel-row.property-panel-row-empty .property-value-panel.ls-block.property-value-container {
@apply items-center;
}
.property-pair.property-panel-row[data-property-type=node] .property-value-panel.ls-block.property-value-container,
.property-pair.property-panel-row[data-property-type=node] .property-value.property-value-panel-inner {
@apply items-center;
}
.property-pair.property-panel-row .property-icon {
display: none;
}
.property-panel-bullet {
@apply inline-flex items-center opacity-60;
margin-left: 2px;
}
.property-panel-bullet .bullet-container {
@apply relative left-0 top-0;
}
.property-panel-edit-btn {
@apply inline-flex items-center justify-center p-0 bg-transparent border-0 text-muted-foreground cursor-pointer;
}
.property-pair {
.jtrigger-view {
> .jtrigger-id {
@apply left-[-11px];
@@ -122,6 +183,39 @@
}
}
.property-pair.property-panel-row .property-k {
@apply leading-[20px];
font-size: inherit;
color: var(--ls-primary-text-color);
}
.property-pair.property-panel-row a.property-k,
.property-key-panel a {
color: var(--ls-primary-text-color);
}
.property-value.property-value-panel-inner {
@apply pl-0 min-h-[20px] w-full min-w-0;
color: var(--ls-primary-text-color);
}
.property-value.property-value-panel-inner > .property-value-inner {
@apply w-full min-w-0;
}
.property-value.property-value-panel-inner .jtrigger {
@apply w-full min-w-0;
}
.property-value.property-value-panel-inner .select-item {
@apply min-w-0;
}
.property-pair.property-panel-row[data-property-type=node] .property-value.property-value-panel-inner .page-reference,
.property-pair.property-panel-row[data-property-type=node] .property-value.property-value-panel-inner .page-ref {
line-height: 20px;
}
.add-button-link {
opacity: 0.5;
}
@@ -474,4 +568,4 @@ a.control-link {
@apply py-1;
}
}
}
}

View File

@@ -431,7 +431,7 @@
:else nil)))
(rum/defc datetime-value
[value property-id repeated-task?]
[value property-id repeated-task? {:keys [datetime? other-position? suppress-inline-edit-icon?]}]
(when-let [date (t/to-default-time-zone (tc/from-long value))]
(let [content [:div.ls-datetime.flex.flex-row.gap-1.items-center
(when-let [page-cp (state/get-component :block/page-cp)]
@@ -442,15 +442,17 @@
:label (human-date-label value)}
{:block/name page-title})
page-title)))
(let [date (js/Date. value)
hours (.getHours date)
minutes (.getMinutes date)]
[:span.select-none
(if (= 0 hours minutes)
(ui/icon "edit" {:size 14 :class "text-muted-foreground hover:text-foreground align-middle"})
(str (util/zero-pad hours)
":"
(util/zero-pad minutes)))])]]
(when datetime?
(let [date (js/Date. value)
hours (.getHours date)
minutes (.getMinutes date)]
[:span.select-none
(if (= 0 hours minutes)
(when-not (or other-position? suppress-inline-edit-icon?)
(ui/icon "edit" {:size 14 :class "text-muted-foreground hover:text-foreground align-middle"}))
(str (util/zero-pad hours)
":"
(util/zero-pad minutes)))]))]]
(if (or repeated-task? (contains? #{:logseq.property/deadline :logseq.property/scheduled} property-id))
(overdue date content)
content))))
@@ -462,7 +464,7 @@
(:db/ident property)))
(rum/defc date-picker
[value {:keys [block property datetime? on-change on-delete del-btn? editing? multiple-values? other-position?]}]
[value {:keys [block property datetime? on-change on-delete del-btn? editing? multiple-values? other-position? suppress-inline-edit-icon?]}]
(let [*el (hooks/use-ref nil)
content-fn (fn [{:keys [id]}] (calendar-inner id
{:block block
@@ -526,7 +528,12 @@
content))
(number? value)
(datetime-value value (:db/ident property) repeated-task?)
(datetime-value value
(:db/ident property)
repeated-task?
{:datetime? datetime?
:other-position? other-position?
:suppress-inline-edit-icon? suppress-inline-edit-icon?})
:else
(property-empty-btn-value nil))])))))

View File

@@ -3039,7 +3039,7 @@
(remove (fn [e] (attributes (:db/ident e))))
(remove (fn [k]
(when-not page-title?
(outliner-property/property-with-other-position? k))))
(outliner-property/property-with-other-position? db block k))))
(remove (fn [e] (:logseq.property/hide? e)))
(remove nil?))]
(or (seq properties)