mirror of
https://github.com/logseq/logseq.git
synced 2026-02-01 22:47:36 +00:00
Unify default-icon property to store icon type and value together
Replaces the separate default-icon-type (closed values) property with a single
default-icon property that stores a map containing the icon type and data.
This simplifies icon inheritance by storing all icon info in one place.
Changes:
- default-icon now stores {:type :avatar/:text/:tabler-icon/:emoji, :id ..., ...}
- Add :none type to explicitly hide inherited icons on instances
- Add "Set icon" menu item in block context menu for easy access
- Classes sync their icon to default-icon for automatic inheritance
- Include schema migrations 65.23-65.26 for gradual migration
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
24
deps/db/src/logseq/db/frontend/property.cljs
vendored
24
deps/db/src/logseq/db/frontend/property.cljs
vendored
@@ -192,19 +192,17 @@
|
||||
:view-context :class}
|
||||
:properties
|
||||
{:logseq.property/description "When enabled, this tag will show reverse nodes that link to the current node via properties."}}
|
||||
:logseq.property.class/default-icon-type {:title "Default icon type"
|
||||
:schema {:type :default
|
||||
:public? true
|
||||
:view-context :class}
|
||||
:closed-values
|
||||
(mapv (fn [[db-ident value]]
|
||||
{:db-ident db-ident
|
||||
:value value
|
||||
:uuid (common-uuid/gen-uuid :db-ident-block-uuid db-ident)})
|
||||
[[:logseq.property.class/default-icon-type.avatar "avatar"]
|
||||
[:logseq.property.class/default-icon-type.text "text"]])
|
||||
:properties
|
||||
{:logseq.property/description "Set the default icon type for instances of this tag. Icons will be auto-generated from each instance's page title."}}
|
||||
;; Unified default icon property - stores icon data with type inferred from :type field
|
||||
;; For icon: {:type :tabler-icon :id "checkbox"}
|
||||
;; For emoji: {:type :emoji :id "🎯"}
|
||||
;; For avatar: {:type :avatar} (value derived from instance title)
|
||||
;; For text: {:type :text} (value derived from instance title)
|
||||
:logseq.property.class/default-icon {:title "Default Icon"
|
||||
:schema {:type :map
|
||||
:public? true
|
||||
:view-context :class}
|
||||
:properties
|
||||
{:logseq.property/description "Set the default icon for instances of this tag. Avatar/text are auto-generated from title."}}
|
||||
:logseq.property/hide-empty-value {:title "Hide empty value"
|
||||
:schema {:type :checkbox
|
||||
:public? true
|
||||
|
||||
2
deps/db/src/logseq/db/frontend/schema.cljs
vendored
2
deps/db/src/logseq/db/frontend/schema.cljs
vendored
@@ -30,7 +30,7 @@
|
||||
(map (juxt :major :minor)
|
||||
[(parse-schema-version x) (parse-schema-version y)])))
|
||||
|
||||
(def version (parse-schema-version "65.22"))
|
||||
(def version (parse-schema-version "65.26"))
|
||||
|
||||
(defn major-version
|
||||
"Return a number.
|
||||
|
||||
@@ -2518,49 +2518,56 @@
|
||||
;; Inline block icon - displayed BEFORE positioned properties like Status
|
||||
(when-not (or table? (:page-title? config))
|
||||
(let [block-icon (:logseq.property/icon block)
|
||||
tag-icon (some :logseq.property/icon (:block/tags block))
|
||||
;; Check for default-icon-type on tags (for auto-generated icons)
|
||||
;; Check for :none override - explicitly hidden icon
|
||||
icon-hidden? (= :none (:type block-icon))
|
||||
;; Check for default-icon on tags (unified icon inheritance)
|
||||
sorted-tags (sort-by :db/id (:block/tags block))
|
||||
default-icon-type (some (fn [tag]
|
||||
(when-let [dit (:logseq.property.class/default-icon-type tag)]
|
||||
(or (:block/title dit)
|
||||
(:logseq.property/value dit))))
|
||||
sorted-tags)
|
||||
has-icon? (or block-icon tag-icon default-icon-type)]
|
||||
default-icon (some (fn [tag]
|
||||
(:logseq.property.class/default-icon tag))
|
||||
sorted-tags)
|
||||
;; Determine if we have an icon to show
|
||||
has-icon? (and (not icon-hidden?)
|
||||
(or (and block-icon (not= :none (:type block-icon)))
|
||||
default-icon))]
|
||||
(when has-icon?
|
||||
(let [icon (or block-icon
|
||||
(when (and default-icon-type (:block/title block))
|
||||
(case default-icon-type
|
||||
"avatar" {:type :avatar
|
||||
:data {:value (icon-component/derive-avatar-initials (:block/title block))}}
|
||||
"text" {:type :text
|
||||
:data {:value (icon-component/derive-initials (:block/title block))}}
|
||||
nil))
|
||||
tag-icon)]
|
||||
[:div.inline-block-icon.flex.items-center.h-6.select-none
|
||||
(icon-component/icon-picker
|
||||
icon
|
||||
{:on-chosen (fn [_e new-icon]
|
||||
(if new-icon
|
||||
(let [icon-data (cond
|
||||
(= :text (:type new-icon))
|
||||
{:type :text :data (:data new-icon)}
|
||||
(let [icon (or (when (and block-icon (not= :none (:type block-icon)))
|
||||
block-icon)
|
||||
(when default-icon
|
||||
(case (:type default-icon)
|
||||
:avatar (when (:block/title block)
|
||||
{:type :avatar
|
||||
:data {:value (icon-component/derive-avatar-initials (:block/title block))}})
|
||||
:text (when (:block/title block)
|
||||
{:type :text
|
||||
:data {:value (icon-component/derive-initials (:block/title block))}})
|
||||
;; For tabler-icon and emoji, use the stored value directly
|
||||
default-icon)))]
|
||||
(when icon
|
||||
[:div.inline-block-icon.flex.items-center.h-6.select-none
|
||||
(icon-component/icon-picker
|
||||
icon
|
||||
{:on-chosen (fn [_e new-icon]
|
||||
(if new-icon
|
||||
(let [icon-data (cond
|
||||
(= :text (:type new-icon))
|
||||
{:type :text :data (:data new-icon)}
|
||||
|
||||
(= :avatar (:type new-icon))
|
||||
{:type :avatar :data (:data new-icon)}
|
||||
(= :avatar (:type new-icon))
|
||||
{:type :avatar :data (:data new-icon)}
|
||||
|
||||
:else
|
||||
(select-keys new-icon [:id :type :color]))]
|
||||
:else
|
||||
(select-keys new-icon [:id :type :color]))]
|
||||
(db-property-handler/set-block-property!
|
||||
(:db/id block)
|
||||
:logseq.property/icon
|
||||
icon-data))
|
||||
;; Delete = set :none to override inheritance
|
||||
(db-property-handler/set-block-property!
|
||||
(:db/id block)
|
||||
:logseq.property/icon
|
||||
icon-data))
|
||||
;; Delete icon
|
||||
(db-property-handler/remove-block-property!
|
||||
(:db/id block)
|
||||
:logseq.property/icon)))
|
||||
:del-btn? (boolean block-icon)
|
||||
:icon-props {:size 16}})]))))
|
||||
{:type :none})))
|
||||
:del-btn? true ;; Always show delete when icon displayed
|
||||
:icon-props {:size 16}})])))))
|
||||
|
||||
(when-not table?
|
||||
(block-positioned-properties config block :block-left))
|
||||
@@ -3005,23 +3012,22 @@
|
||||
children (ldb/get-children block)
|
||||
page-icon (when (:page-title? config)
|
||||
(let [icon' (get block :logseq.property/icon)
|
||||
;; Check for default-icon-type on tags (for auto-generated icons)
|
||||
;; Check for default-icon on tags (for auto-generated icons)
|
||||
sorted-tags (sort-by :db/id (:block/tags block))
|
||||
default-icon-type (some (fn [tag]
|
||||
(when-let [dit (:logseq.property.class/default-icon-type tag)]
|
||||
(or (:block/title dit)
|
||||
(:logseq.property/value dit))))
|
||||
sorted-tags)]
|
||||
default-icon (some (fn [tag]
|
||||
(:logseq.property.class/default-icon tag))
|
||||
sorted-tags)]
|
||||
(when-let [icon (and (ldb/page? block)
|
||||
(or icon'
|
||||
;; Generate icon from default-icon-type
|
||||
(when (and default-icon-type (:block/title block))
|
||||
(case default-icon-type
|
||||
"avatar" {:type :avatar
|
||||
:data {:value (icon-component/derive-avatar-initials (:block/title block))}}
|
||||
"text" {:type :text
|
||||
:data {:value (icon-component/derive-initials (:block/title block))}}
|
||||
nil))
|
||||
;; Generate icon from default-icon
|
||||
(when (and default-icon (:block/title block))
|
||||
(case (:type default-icon)
|
||||
:avatar {:type :avatar
|
||||
:data {:value (icon-component/derive-avatar-initials (:block/title block))}}
|
||||
:text {:type :text
|
||||
:data {:value (icon-component/derive-initials (:block/title block))}}
|
||||
;; For tabler-icon and emoji, use the stored value
|
||||
default-icon))
|
||||
(some :logseq.property/icon (:block/tags block))
|
||||
(when (ldb/class? block)
|
||||
{:type :tabler-icon
|
||||
@@ -3043,14 +3049,27 @@
|
||||
|
||||
:else
|
||||
(select-keys icon [:id :type :color]))]
|
||||
;; Set the icon on the page
|
||||
(db-property-handler/set-block-property!
|
||||
(:db/id block)
|
||||
:logseq.property/icon
|
||||
icon-data))
|
||||
icon-data)
|
||||
;; For classes, also set default-icon for inheritance
|
||||
(when (ldb/class? block)
|
||||
(db-property-handler/set-block-property!
|
||||
(:db/id block)
|
||||
:logseq.property.class/default-icon
|
||||
icon-data)))
|
||||
;; del
|
||||
(db-property-handler/remove-block-property!
|
||||
(:db/id block)
|
||||
:logseq.property/icon)))
|
||||
(do
|
||||
(db-property-handler/remove-block-property!
|
||||
(:db/id block)
|
||||
:logseq.property/icon)
|
||||
;; For classes, also remove default-icon
|
||||
(when (ldb/class? block)
|
||||
(db-property-handler/remove-block-property!
|
||||
(:db/id block)
|
||||
:logseq.property.class/default-icon)))))
|
||||
:del-btn? (boolean icon')
|
||||
:icon-props {:style {:width "1lh"
|
||||
:height "1lh"
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
[frontend.commands :as commands]
|
||||
[frontend.components.editor :as editor]
|
||||
[frontend.components.export :as export]
|
||||
[frontend.components.icon :as icon-component]
|
||||
[frontend.components.page-menu :as page-menu]
|
||||
[frontend.context.i18n :refer [t]]
|
||||
[frontend.db :as db]
|
||||
@@ -134,6 +135,41 @@
|
||||
#(editor-handler/set-heading! block-id true)
|
||||
#(editor-handler/remove-heading! block-id))
|
||||
|
||||
;; Set icon menu item
|
||||
(let [current-icon (:logseq.property/icon block)]
|
||||
(shui/dropdown-menu-item
|
||||
{:key "Set icon"
|
||||
:on-click (fn [^js e]
|
||||
(shui/popup-show!
|
||||
(.-target e)
|
||||
(fn [{:keys [id]}]
|
||||
(icon-component/icon-search
|
||||
{:on-chosen (fn [_e icon-value]
|
||||
(let [icon-data (when icon-value
|
||||
(cond
|
||||
(= :text (:type icon-value))
|
||||
{:type :text :data (:data icon-value)}
|
||||
|
||||
(= :avatar (:type icon-value))
|
||||
{:type :avatar :data (:data icon-value)}
|
||||
|
||||
:else
|
||||
(select-keys icon-value [:type :id :color])))]
|
||||
(property-handler/set-block-property!
|
||||
block-id
|
||||
:logseq.property/icon
|
||||
icon-data))
|
||||
(shui/popup-hide! id))
|
||||
:icon-value current-icon
|
||||
:del-btn? (some? current-icon)}))
|
||||
{:align :start
|
||||
:id :ls-icon-picker
|
||||
:content-props {:class "ls-icon-picker"
|
||||
:onEscapeKeyDown #(.preventDefault %)}}))}
|
||||
(if current-icon
|
||||
(t :content/change-icon)
|
||||
(t :content/set-icon))))
|
||||
|
||||
(shui/dropdown-menu-separator)
|
||||
|
||||
(shui/dropdown-menu-item
|
||||
|
||||
@@ -124,44 +124,47 @@
|
||||
|
||||
(defn get-node-icon
|
||||
[node-entity]
|
||||
(let [sorted-tags (sort-by :db/id (:block/tags node-entity))
|
||||
first-tag-icon (some :logseq.property/icon sorted-tags)
|
||||
;; Check for default-icon-type on tags (for auto-generated icons)
|
||||
default-icon-type (some (fn [tag]
|
||||
(when-let [dit (:logseq.property.class/default-icon-type tag)]
|
||||
;; dit is a reference to the closed value entity
|
||||
;; closed values store their value in :block/title or :logseq.property/value
|
||||
(or (:block/title dit)
|
||||
(:logseq.property/value dit))))
|
||||
sorted-tags)]
|
||||
(or
|
||||
;; 1. Instance's own icon takes precedence
|
||||
(get node-entity :logseq.property/icon)
|
||||
;; 2. Check for default-icon-type from tags (generates icon from page title)
|
||||
(when (and default-icon-type (:block/title node-entity))
|
||||
(let [title (:block/title node-entity)]
|
||||
(case default-icon-type
|
||||
"avatar" {:type :avatar
|
||||
:data {:value (derive-avatar-initials title)}}
|
||||
"text" {:type :text
|
||||
:data {:value (derive-initials title)}}
|
||||
nil)))
|
||||
;; 3. Fall back to first tag's explicit icon (existing inheritance)
|
||||
(when (some? first-tag-icon)
|
||||
first-tag-icon)
|
||||
;; 4. Type-based defaults
|
||||
(let [asset-type (:logseq.property.asset/type node-entity)]
|
||||
(cond
|
||||
(ldb/class? node-entity)
|
||||
"hash"
|
||||
(ldb/property? node-entity)
|
||||
"letter-p"
|
||||
(ldb/page? node-entity)
|
||||
"file"
|
||||
(= asset-type "pdf")
|
||||
"book"
|
||||
:else
|
||||
"point-filled")))))
|
||||
(let [block-icon (get node-entity :logseq.property/icon)
|
||||
sorted-tags (sort-by :db/id (:block/tags node-entity))
|
||||
;; Check for default-icon on tags (unified icon inheritance)
|
||||
default-icon (some (fn [tag]
|
||||
(:logseq.property.class/default-icon tag))
|
||||
sorted-tags)]
|
||||
(cond
|
||||
;; 1. Explicit "no icon" override - hide icon even if inherited
|
||||
(= :none (:type block-icon))
|
||||
nil
|
||||
|
||||
;; 2. Instance's own icon takes precedence
|
||||
block-icon
|
||||
block-icon
|
||||
|
||||
;; 3. Resolve from tag's default-icon (unified inheritance)
|
||||
default-icon
|
||||
(case (:type default-icon)
|
||||
:avatar (when (:block/title node-entity)
|
||||
{:type :avatar
|
||||
:data {:value (derive-avatar-initials (:block/title node-entity))}})
|
||||
:text (when (:block/title node-entity)
|
||||
{:type :text
|
||||
:data {:value (derive-initials (:block/title node-entity))}})
|
||||
;; For tabler-icon and emoji, use the stored icon value directly
|
||||
default-icon)
|
||||
|
||||
;; 4. Type-based defaults (for classes, properties, pages, etc.)
|
||||
:else
|
||||
(let [asset-type (:logseq.property.asset/type node-entity)]
|
||||
(cond
|
||||
(ldb/class? node-entity)
|
||||
"hash"
|
||||
(ldb/property? node-entity)
|
||||
"letter-p"
|
||||
(ldb/page? node-entity)
|
||||
"file"
|
||||
(= asset-type "pdf")
|
||||
"book"
|
||||
:else
|
||||
"point-filled")))))
|
||||
|
||||
(rum/defc get-node-icon-cp < rum/reactive db-mixins/query
|
||||
[node-entity opts]
|
||||
|
||||
@@ -706,8 +706,13 @@
|
||||
(not (ldb/built-in? block)))
|
||||
(-> (assoc :logseq.property.class/enable-bidirectional?
|
||||
(:logseq.property.class/enable-bidirectional? block))
|
||||
(assoc :logseq.property.class/default-icon-type
|
||||
(:logseq.property.class/default-icon-type block))))
|
||||
(assoc :logseq.property.class/default-icon
|
||||
(:logseq.property.class/default-icon block)))
|
||||
;; Show icon properties on built-in classes too
|
||||
(and (ldb/class? block)
|
||||
(ldb/built-in? block))
|
||||
(assoc :logseq.property.class/default-icon
|
||||
(:logseq.property.class/default-icon block)))
|
||||
remove-built-in-or-other-position-properties
|
||||
(fn [properties show-in-hidden-properties?]
|
||||
(remove (fn [property]
|
||||
@@ -738,7 +743,7 @@
|
||||
;; This section produces own-properties and full-hidden-properties
|
||||
;; Class properties that should always show even when empty
|
||||
always-show-class-properties #{:logseq.property.class/enable-bidirectional?
|
||||
:logseq.property.class/default-icon-type}
|
||||
:logseq.property.class/default-icon}
|
||||
hide-with-property-id (fn [property-id]
|
||||
(let [property (db/entity property-id)]
|
||||
(boolean
|
||||
|
||||
@@ -684,7 +684,7 @@
|
||||
(property-type-sub-pane property ops))}))
|
||||
|
||||
(when (and (= property-type :node)
|
||||
(not (contains? #{:logseq.property.class/extends} (:db/ident property))))
|
||||
(not (contains? #{:logseq.property.class/extends :logseq.property.class/default-icon} (:db/ident property))))
|
||||
(dropdown-editor-menuitem {:icon :hash
|
||||
:disabled? disabled?
|
||||
:title "Specify node tags"
|
||||
@@ -739,7 +739,7 @@
|
||||
(when (and (not= :logseq.property/enable-history? (:db/ident property))
|
||||
(not special-built-in-prop?))
|
||||
(let [property-type (:logseq.property/type property)
|
||||
group' (->> [(when (and (not (contains? #{:logseq.property.class/extends :logseq.property.class/properties} (:db/ident property)))
|
||||
group' (->> [(when (and (not (contains? #{:logseq.property.class/extends :logseq.property.class/properties :logseq.property.class/default-icon} (:db/ident property)))
|
||||
(contains? #{:default :number :date :checkbox :node} property-type)
|
||||
(not
|
||||
(and (= :default property-type)
|
||||
@@ -751,13 +751,13 @@
|
||||
:disabled? config/publishing?
|
||||
:submenu-content (fn [ops] (ui-position-sub-pane property (assoc ops :ui-position position)))})))
|
||||
|
||||
(when (not (contains? #{:logseq.property.class/extends :logseq.property.class/properties} (:db/ident property)))
|
||||
(when (not (contains? #{:logseq.property.class/extends :logseq.property.class/properties :logseq.property.class/default-icon} (:db/ident property)))
|
||||
(dropdown-editor-menuitem {:icon :eye-off :title "Hide by default" :toggle-checked? (boolean (:logseq.property/hide? property))
|
||||
:disabled? config/publishing?
|
||||
:on-toggle-checked-change #(db-property-handler/set-block-property! (:db/id property)
|
||||
:logseq.property/hide?
|
||||
%)}))
|
||||
(when (not (contains? #{:logseq.property.class/extends :logseq.property.class/properties} (:db/ident property)))
|
||||
(when (not (contains? #{:logseq.property.class/extends :logseq.property.class/properties :logseq.property.class/default-icon} (:db/ident property)))
|
||||
(dropdown-editor-menuitem
|
||||
{:icon :eye-off :title "Hide empty value"
|
||||
:toggle-checked? (boolean (:logseq.property/hide-empty-value property))
|
||||
@@ -804,7 +804,7 @@
|
||||
;; Any property should be removable from Tag Properties
|
||||
(if class-schema?
|
||||
(contains? (set (map :db/id (:logseq.property.class/properties owner-block))) (:db/id property))
|
||||
(not (contains? #{:logseq.property.class/extends :logseq.property.class/properties} (:db/ident property)))))
|
||||
(not (contains? #{:logseq.property.class/extends :logseq.property.class/properties :logseq.property.class/default-icon} (:db/ident property)))))
|
||||
|
||||
(dropdown-editor-menuitem
|
||||
{:id :delete-property :icon :x
|
||||
|
||||
@@ -141,6 +141,100 @@
|
||||
:del-btn? (some? icon-value)
|
||||
:on-chosen on-chosen!})])))
|
||||
|
||||
(def ^:private default-icon-type-options
|
||||
[{:value :tabler-icon :label "Icon"}
|
||||
{:value :emoji :label "Emoji"}
|
||||
{:value :avatar :label "Avatar"}
|
||||
{:value :text :label "Text"}])
|
||||
|
||||
(rum/defc default-icon-row < rum/reactive
|
||||
"Renders the unified Default Icon property for classes.
|
||||
Shows a type dropdown and icon picker side by side."
|
||||
[block _editing?]
|
||||
;; Subscribe to block changes for reactivity
|
||||
(let [block (or (model/sub-block (:db/id block)) block)
|
||||
current-value (:logseq.property.class/default-icon block)
|
||||
;; Only show a type if we have a value, otherwise show empty state
|
||||
has-value? (some? current-value)
|
||||
current-type (when has-value? (:type current-value))
|
||||
shows-picker? (contains? #{:tabler-icon :emoji} current-type)
|
||||
set-default-icon! (fn [icon-data]
|
||||
(property-handler/set-block-property!
|
||||
(:db/id block)
|
||||
:logseq.property.class/default-icon
|
||||
icon-data))
|
||||
on-type-change (fn [new-type]
|
||||
;; When changing type, preserve icon data if switching between icon/emoji
|
||||
;; For avatar/text, just store the type marker
|
||||
(cond
|
||||
(contains? #{:avatar :text} new-type)
|
||||
(set-default-icon! {:type new-type})
|
||||
|
||||
;; Switching to icon/emoji - keep existing icon if compatible
|
||||
(and (contains? #{:tabler-icon :emoji} new-type)
|
||||
(contains? #{:tabler-icon :emoji} current-type))
|
||||
(set-default-icon! (assoc current-value :type new-type))
|
||||
|
||||
;; Switching to icon/emoji from avatar/text - clear and let user pick
|
||||
:else
|
||||
(set-default-icon! {:type new-type})))
|
||||
on-icon-chosen (fn [_e icon]
|
||||
(if icon
|
||||
(let [icon-data (cond
|
||||
(= :text (:type icon))
|
||||
{:type :text :data (:data icon)}
|
||||
|
||||
(= :avatar (:type icon))
|
||||
{:type :avatar :data (:data icon)}
|
||||
|
||||
:else
|
||||
(select-keys icon [:type :id :color]))]
|
||||
(set-default-icon! icon-data))
|
||||
;; Delete - remove the property
|
||||
(property-handler/remove-block-property!
|
||||
(:db/id block)
|
||||
:logseq.property.class/default-icon)))
|
||||
type-label (some #(when (= (:value %) current-type) (:label %)) default-icon-type-options)]
|
||||
[:div.col-span-3.flex.flex-row.items-center.gap-2
|
||||
;; Type dropdown
|
||||
(shui/dropdown-menu
|
||||
(shui/dropdown-menu-trigger
|
||||
{:asChild true}
|
||||
(shui/button {:variant :ghost :size :sm :class "h-6 px-2 gap-1"}
|
||||
[:span.text-sm (or type-label "Empty")]
|
||||
(shui/tabler-icon "chevron-down" {:size 14})))
|
||||
(shui/dropdown-menu-content
|
||||
{:align "start"}
|
||||
(doall
|
||||
(for [{:keys [value label]} default-icon-type-options]
|
||||
(shui/dropdown-menu-item
|
||||
{:key (name value)
|
||||
:onSelect (fn [_e] (on-type-change value))}
|
||||
[:span {:class (when (= value current-type) "font-medium")} label])))))
|
||||
;; Icon picker (only for icon/emoji types)
|
||||
(when (and has-value? shows-picker?)
|
||||
[:span.text-muted-foreground.text-sm ">"])
|
||||
(cond
|
||||
;; No value set yet
|
||||
(not has-value?)
|
||||
nil
|
||||
|
||||
;; Icon/emoji type - show picker
|
||||
shows-picker?
|
||||
(icon-component/icon-picker current-value
|
||||
{:disabled? config/publishing?
|
||||
:del-btn? (some? (:id current-value))
|
||||
:on-chosen on-icon-chosen
|
||||
:icon-props {:size 18}})
|
||||
|
||||
;; Avatar/text type - show preview indicator
|
||||
:else
|
||||
[:span.text-muted-foreground.text-xs.italic
|
||||
(case current-type
|
||||
:avatar "(auto from title)"
|
||||
:text "(auto from title)"
|
||||
"")])]))
|
||||
|
||||
(defn select-type?
|
||||
[block property]
|
||||
(let [type (:logseq.property/type property)]
|
||||
@@ -1442,6 +1536,9 @@
|
||||
(= :logseq.property/icon (:db/ident property))
|
||||
(icon-row block editing?)
|
||||
|
||||
(= :logseq.property.class/default-icon (:db/ident property))
|
||||
(default-icon-row block editing?)
|
||||
|
||||
(and (= type :number) (not editing?) (not closed-values?))
|
||||
(single-number-input block property value (:table-view? opts))
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
:logseq.property/page-tags :logseq.property.class/extends
|
||||
:logseq.property.class/bidirectional-property-title
|
||||
:logseq.property.class/enable-bidirectional?
|
||||
:logseq.property.class/default-icon-type
|
||||
:logseq.property.class/default-icon
|
||||
:logseq.property/publishing-public? :logseq.property.user/avatar
|
||||
:logseq.property.user/email :logseq.property.user/name})
|
||||
|
||||
|
||||
@@ -197,6 +197,76 @@
|
||||
updated-icon (assoc icon-value :data updated-data)]
|
||||
[:db/add entity-id :logseq.property/icon updated-icon])))))))))
|
||||
|
||||
(defn- migrate-class-icons-to-default-icon-type-value
|
||||
"Migrate class icons to use default-icon-type/default-icon-value.
|
||||
For classes with tabler-icon or emoji icons, set default-icon-type to 'icon' or 'emoji'
|
||||
and copy the icon to default-icon-value for inheritance to instances.
|
||||
NOTE: This is an intermediate migration step, later consolidated into default-icon."
|
||||
[db]
|
||||
(let [;; Get the closed value entities for default-icon-type
|
||||
icon-type-entity (db-property/get-closed-value-entity-by-name
|
||||
db :logseq.property.class/default-icon-type "icon")
|
||||
emoji-type-entity (db-property/get-closed-value-entity-by-name
|
||||
db :logseq.property.class/default-icon-type "emoji")]
|
||||
(->> (d/datoms db :avet :logseq.property/icon)
|
||||
(mapcat (fn [datom]
|
||||
(let [icon-value (:v datom)
|
||||
entity-id (:e datom)
|
||||
entity (d/entity db entity-id)]
|
||||
;; Only process classes that have icons
|
||||
(when (and (map? icon-value)
|
||||
(ldb/class? entity)
|
||||
;; Don't override if already set
|
||||
(nil? (:logseq.property.class/default-icon-type entity)))
|
||||
(let [icon-type (:type icon-value)]
|
||||
(cond
|
||||
;; Tabler icons -> default-icon-type "icon"
|
||||
(= :tabler-icon icon-type)
|
||||
(when icon-type-entity
|
||||
[[:db/add entity-id :logseq.property.class/default-icon-type (:db/id icon-type-entity)]
|
||||
[:db/add entity-id :logseq.property.class/default-icon-value icon-value]])
|
||||
|
||||
;; Emoji icons -> default-icon-type "emoji"
|
||||
(= :emoji icon-type)
|
||||
(when emoji-type-entity
|
||||
[[:db/add entity-id :logseq.property.class/default-icon-type (:db/id emoji-type-entity)]
|
||||
[:db/add entity-id :logseq.property.class/default-icon-value icon-value]])
|
||||
|
||||
;; Other types (avatar, text) - skip, as they're handled differently
|
||||
:else nil)))))))))
|
||||
|
||||
(defn- migrate-to-unified-default-icon
|
||||
"Migrate from two-property system (default-icon-type + default-icon-value)
|
||||
to unified single property (default-icon).
|
||||
Also migrates classes with :logseq.property/icon to use default-icon."
|
||||
[db]
|
||||
(let [tx-data
|
||||
(->> (d/datoms db :avet :logseq.property/icon)
|
||||
(mapcat (fn [datom]
|
||||
(let [icon-value (:v datom)
|
||||
entity-id (:e datom)
|
||||
entity (d/entity db entity-id)]
|
||||
;; Only process classes that have icons
|
||||
(when (and (map? icon-value)
|
||||
(ldb/class? entity)
|
||||
;; Don't override if already set
|
||||
(nil? (:logseq.property.class/default-icon entity)))
|
||||
;; Set the unified default-icon property
|
||||
[[:db/add entity-id :logseq.property.class/default-icon icon-value]])))))]
|
||||
;; Also migrate any existing default-icon-type avatar/text settings
|
||||
(concat tx-data
|
||||
(->> (d/datoms db :avet :logseq.property.class/default-icon-type)
|
||||
(keep (fn [datom]
|
||||
(let [entity-id (:e datom)
|
||||
entity (d/entity db entity-id)
|
||||
type-entity (:logseq.property.class/default-icon-type entity)
|
||||
type-value (or (:block/title type-entity) (:logseq.property/value type-entity))]
|
||||
;; For avatar/text types, create the marker icon
|
||||
(when (and (contains? #{"avatar" "text"} type-value)
|
||||
(nil? (:logseq.property.class/default-icon entity)))
|
||||
[:db/add entity-id :logseq.property.class/default-icon
|
||||
{:type (keyword type-value)}]))))))))
|
||||
|
||||
(def schema-version->updates
|
||||
"A vec of tuples defining datascript migrations. Each tuple consists of the
|
||||
schema version integer and a migration map. A migration map can have keys of :properties, :classes
|
||||
@@ -219,7 +289,15 @@
|
||||
["65.19" {:properties [:logseq.property/choice-classes :logseq.property/choice-exclusions]}]
|
||||
["65.20" {:properties [:logseq.property.class/bidirectional-property-title :logseq.property.class/enable-bidirectional?]}]
|
||||
["65.21" {:properties [:logseq.property.class/default-icon-type]}]
|
||||
["65.22" {:fix migrate-icon-colors}]])
|
||||
["65.22" {:fix migrate-icon-colors}]
|
||||
["65.23" {:properties [:logseq.property.class/default-icon-value]}]
|
||||
["65.24" {:fix migrate-class-icons-to-default-icon-type-value}]
|
||||
["65.25" {:properties [:logseq.property.class/default-icon]
|
||||
:fix migrate-to-unified-default-icon}]
|
||||
;; 65.26: Update default-icon property type from :default to :map so it can store map values
|
||||
["65.26" {:fix (fn [db]
|
||||
(when-let [property (d/entity db :logseq.property.class/default-icon)]
|
||||
[[:db/add (:db/id property) :logseq.property/type :map]]))}]])
|
||||
|
||||
(let [[major minor] (last (sort (map (comp (juxt :major :minor) db-schema/parse-schema-version first)
|
||||
schema-version->updates)))]
|
||||
|
||||
@@ -159,6 +159,8 @@
|
||||
:content/replace-with-text "Replace with text"
|
||||
:content/replace-with-embed "Replace with embed"
|
||||
:content/open-in-sidebar "Open in sidebar"
|
||||
:content/set-icon "Set icon"
|
||||
:content/change-icon "Change icon"
|
||||
:content/click-to-edit "Click to edit"
|
||||
:context-menu/make-a-flashcard "Make a Flashcard"
|
||||
:context-menu/toggle-number-list "Toggle number list"
|
||||
|
||||
Reference in New Issue
Block a user