wip: property UX

This commit is contained in:
Tienson Qin
2023-07-03 12:02:19 +08:00
parent 045c51cded
commit e0028a39fa
10 changed files with 468 additions and 206 deletions

View File

@@ -2269,7 +2269,8 @@
(rum/defc block-content < rum/reactive
[config {:block/keys [uuid content children properties scheduled deadline format pre-block?] :as block} edit-input-id block-id slide? selected?]
(let [content (property/remove-built-in-properties format content)
(let [repo (state/get-current-repo)
content (property/remove-built-in-properties format content)
{:block/keys [title body] :as block} (if (:block/title block) block
(merge block (block/parse-title-and-body uuid format pre-block? content)))
collapsed? (util/collapsed? block)
@@ -2335,15 +2336,17 @@
(when-let [scheduled-ast (block-handler/get-scheduled-ast block)]
(timestamp-cp block "SCHEDULED" scheduled-ast)))
(when-let [invalid-properties (:block/invalid-properties block)]
(invalid-properties-cp invalid-properties))
(when-not (config/db-based-graph? repo)
(when-let [invalid-properties (:block/invalid-properties block)]
(invalid-properties-cp invalid-properties)))
(when (and (seq properties)
(let [hidden? (property-edit/properties-hidden? properties)]
(not hidden?))
(not (and block-ref? (or (seq title) (seq body))))
(not (:slide? config))
(not= block-type :whiteboard-shape))
(not= block-type :whiteboard-shape)
(not (config/db-based-graph? repo)))
(properties-cp config block))
(block-content-inner config block body plugin-slotted? collapsed? block-ref-with-title?)
@@ -2843,20 +2846,21 @@
(if whiteboard-block?
(block-reference {} (str uuid) nil)
;; Not embed self
(let [block (merge block (block/parse-title-and-body uuid (:block/format block) pre-block? content))
hide-block-refs-count? (and (:embed? config)
(= (:block/uuid block) (:embed-id config)))]
(block-content-or-editor config block edit-input-id block-id edit? hide-block-refs-count? selected?)))
[:div.flex.flex-col.w-full
(let [block (merge block (block/parse-title-and-body uuid (:block/format block) pre-block? content))
hide-block-refs-count? (and (:embed? config)
(= (:block/uuid block) (:embed-id config)))]
(block-content-or-editor config block edit-input-id block-id edit? hide-block-refs-count? selected?))
(when (config/db-based-graph? repo)
(property-component/properties-area block
(:block/properties block)
(:block/properties-text-values block)
edit-input-id {:inline-text inline-text
:editor-box (get config :editor-box)}))])
(when @*show-right-menu?
(block-right-menu config block edit?))]
(when (config/db-based-graph? repo)
(property-component/properties-area block
(:block/properties block)
(:block/properties-text-values block)
edit-input-id))
(when-not (:hide-children? config)
(let [children (db/sort-by-left (:block/_parent block) block)]
(block-children config block children collapsed?)))

View File

@@ -5,6 +5,7 @@
[frontend.components.block :as block]
[frontend.components.datetime :as datetime-comp]
[frontend.components.search :as search]
[frontend.components.search.highlight :as highlight]
[frontend.components.svg :as svg]
[frontend.context.i18n :refer [t]]
[frontend.db :as db]
@@ -157,7 +158,7 @@
(when (db-model/whiteboard-page? page-name) [:span.mr-1 (ui/icon "whiteboard" {:extension? true})])
[:div.flex.space-x-1
[:div (when-not (db/page-exists? page-name) (t :new-page))]
(search/highlight-exact-query page-name q)]]
(highlight/highlight-exact-query page-name q)]]
:open? chosen?
:manual? true
:fixed-position? true

View File

@@ -5,23 +5,29 @@
[frontend.handler.property :as property-handler]
[frontend.handler.ui :as ui-handler]
[frontend.db :as db]
[frontend.config :as config]
[rum.core :as rum]
[frontend.state :as state]
[frontend.mixins :as mixins]
[clojure.edn :as edn]
[clojure.string :as string]))
[clojure.string :as string]
[goog.dom :as gdom]
[frontend.search :as search]
[frontend.components.search.highlight :as highlight]
[frontend.components.svg :as svg]
[frontend.modules.shortcut.core :as shortcut]
[medley.core :as medley]))
(rum/defcs property-config <
rum/static
(rum/local nil ::property-name)
(rum/local nil ::property-schema)
{:will-mount (fn [state]
(let [[repo property-uuid] (:rum/args state)
property (db/pull repo '[*] [:block/uuid property-uuid])]
(let [[repo property] (:rum/args state)]
(reset! (::property-name state) (:block/name property))
(reset! (::property-schema state) (:block/schema property))
state))}
[state repo property-uuid]
[state repo property]
(let [*property-name (::property-name state)
*property-schema (::property-schema state)]
[:div.property-configure
@@ -29,40 +35,117 @@
[:div.grid.gap-2.p-1
[:div.grid.grid-cols-4.gap-1.items-center.leading-8
[:label.cols-1 "Name:"]
[:input.form-input
{:on-change #(reset! *property-name (util/evalue %))
:value @*property-name}]]
[:label.cols-1 "Name:"]
[:input.form-input
{:on-change #(reset! *property-name (util/evalue %))
:value @*property-name}]]
[:div.grid.grid-cols-4.gap-1.leading-8
[:label.cols-1 "Schema type:"]
(let [schema-types (->> (keys property-handler/builtin-schema-types)
(map (comp string/capitalize name))
(map (fn [type]
{:label type
:value type
:selected (= (keyword (string/lower-case type))
(:type @*property-schema))})))]
(ui/select schema-types
(fn [_e v]
(let [type (keyword (string/lower-case v))]
(swap! *property-schema assoc :type type)))))]
[:div.grid.grid-cols-4.gap-1.leading-8
[:label.cols-1 "Schema type:"]
(let [schema-types (->> (keys property-handler/builtin-schema-types)
(map (comp string/capitalize name))
(map (fn [type]
{:label type
:value type
:selected (= (keyword (string/lower-case type))
(:type @*property-schema))})))]
(ui/select schema-types
(fn [_e v]
(let [type (keyword (string/lower-case v))]
(swap! *property-schema assoc :type type)))))]
[:div.grid.grid-cols-4.gap-1.items-center.leading-8
[:label.cols-1 "Multiple values:"]
(ui/checkbox {:checked (= :many (:cardinality @*property-schema))
:on-change (fn [v]
(swap! *property-schema assoc :cardinality (if (= "on" (util/evalue v)) :many :one)))})]
[:div.grid.grid-cols-4.gap-1.items-center.leading-8
[:label.cols-1 "Multiple values:"]
(let [many? (boolean (= :many (:cardinality @*property-schema)))]
(ui/checkbox {:checked many?
:on-change (fn []
(swap! *property-schema assoc :cardinality (if many? :one :many)))}))]
[:div
(ui/button
"Save"
:on-click (fn []
(property-handler/update-property!
repo property-uuid
{:property-name @*property-name
:property-schema @*property-schema})
(state/close-modal!)))]]]))
[:div
(ui/button
"Save"
:on-click (fn []
(property-handler/update-property!
repo (:block/uuid property)
{:property-name @*property-name
:property-schema @*property-schema})
(state/close-modal!)))]
(when config/dev?
[:div {:style {:max-width 900}}
[:hr]
[:p "Debug data:"]
[:code
(str property)]])]]))
(rum/defc search-item-render
[search-q content]
[:div.font-medium
(highlight/highlight-exact-query content search-q)])
(defn- exit-edit-property
[*property-key *property-value]
(reset! *property-key nil)
(reset! *property-value nil)
(property-handler/set-editing-new-property! nil))
(defn- add-property!
[block *property-key *property-value]
(let [repo (state/get-current-repo)]
(when (and @*property-key @*property-value)
(property-handler/add-property! repo block @*property-key @*property-value))
(exit-edit-property *property-key *property-value)))
(rum/defcs property-key-input < rum/reactive
(rum/local true ::search?)
shortcut/disable-all-shortcuts
[state entity *property-key *property-value]
(let [*search? (::search? state)
result (when-not (string/blank? @*property-key)
(search/property-search @*property-key))]
[:div
[:div.ls-property-add.grid.grid-cols-4.gap-1.flex.flex-row.items-center
[:input#add-property.form-input.simple-input.block.col-span-1.focus:outline-none
{:placeholder "Property key"
:value (rum/react *property-key)
:auto-focus true
:on-change (fn [e]
(reset! *property-key (util/evalue e))
(reset! *search? true))
:on-key-down (fn [e]
(case (util/ekey e)
"Escape"
(exit-edit-property *property-key *property-value)
"Enter"
(do
(reset! *search? false)
(.focus (js/document.getElementById "add-property-value")))
nil))}]
[:input#add-property-value.block-properties
{:on-change #(reset! *property-value (util/evalue %))
:on-key-down (fn [e]
(case (util/ekey e)
"Enter"
(do
(add-property! entity *property-key *property-value)
(reset! *search? false))
nil))}]
[:a.close {:on-mouse-down #(exit-edit-property *property-key *property-value)}
svg/close]]
(when @*search?
(ui/auto-complete
result
{:class "search-results"
:on-chosen (fn [chosen]
(reset! *property-key chosen)
(reset! *search? false)
(.focus (js/document.getElementById "add-property-value")))
:item-render #(search-item-render @*property-key %)}))]))
(rum/defcs new-property < rum/reactive
(rum/local nil ::property-key)
@@ -74,22 +157,13 @@
:on-hide (fn []
(property-handler/set-editing-new-property! nil))
:node (js/document.getElementById "edit-new-property"))))
[state repo block edit-input-id properties]
(let [new-property? (= edit-input-id (state/sub :ui/new-property-input-id))
*property-key (::property-key state)
[state repo block edit-input-id properties new-property?]
(let [*property-key (::property-key state)
*property-value (::property-value state)]
(cond
new-property?
[:div#edit-new-property
[:input.block-properties {:on-change #(reset! *property-key (util/evalue %))}]
[:input.block-properties {:on-change #(reset! *property-value (util/evalue %))}]
[:a {:on-click (fn []
(when (and @*property-key @*property-value)
(property-handler/add-property! repo block @*property-key @*property-value))
(reset! *property-key nil)
(reset! *property-value nil)
(property-handler/set-editing-new-property! nil))}
"Save"]]
(property-key-input block *property-key *property-value)]
(seq properties)
[:a {:title "Add another value"
@@ -99,26 +173,150 @@
(reset! *property-value nil))}
(ui/icon "circle-plus")])))
(rum/defc properties-area < rum/static
[block properties properties-text-values edit-input-id]
(let [repo (state/get-current-repo)]
[:div.ls-properties-area.pl-6
(when (seq properties)
[:div
(for [[prop-uuid-or-built-in-prop v] properties]
(if (uuid? prop-uuid-or-built-in-prop)
(when-let [property (db/pull [:block/uuid prop-uuid-or-built-in-prop])]
[:div
[:a.mr-2
{:on-click (fn [] (state/set-modal! #(property-config repo prop-uuid-or-built-in-prop)))}
(:block/name property)]
[:span (or (get properties-text-values prop-uuid-or-built-in-prop) (str v))]
[:a.ml-8 {:on-click
(fn []
(property-handler/remove-property! repo block prop-uuid-or-built-in-prop))}
"DEL"]])
;; builtin
[:div
[:a.mr-2 (str prop-uuid-or-built-in-prop)]
[:span v]]))])
(new-property repo block edit-input-id properties)]))
(rum/defcs property-key < (rum/local false ::show-close?)
[state block property]
(let [repo (state/get-current-repo)
*show-close? (::show-close? state)]
[:div.relative
{:on-mouse-over (fn [_] (reset! *show-close? true))
:on-mouse-out (fn [_] (reset! *show-close? false))}
[:a.mr-2
{:on-click (fn [] (state/set-modal! #(property-config repo property)))}
(:block/name property)]
(when @*show-close?
[:div.absolute.top-0.right-0
[:a.fade-link.fade-in.py-2.px-1
{:title "Remove this property"
:on-click (fn [_e]
(property-handler/remove-property! repo block (:block/uuid property)))}
(ui/icon "x")]])]))
(rum/defcs multiple-value-item < (rum/local false ::show-close?)
[state entity property item dom-id' editor-id' {:keys [edit-fn page-cp inline-text]}]
(let [*show-close? (::show-close? state)
object? (= :object (:type (:block/schema property)))
block (when object? (db/pull [:block/uuid item]))]
[:div.flex.flex-1.flex-row {:on-mouse-over #(reset! *show-close? true)
:on-mouse-out #(reset! *show-close? false)}
[:div.flex.flex-1.property-value-content
{:id dom-id'
:on-click (fn []
;; (edit-fn editor-id' dom-id' item)
)}
(if block
;; TODO: page/block
(str block)
(inline-text {} :markdown (str item)))]
(when @*show-close?
[:a.close.fade-in
{:title "Delete this value"
:on-mouse-down
(fn []
(property-handler/delete-property-value! (state/get-current-repo)
entity
(:block/uuid property)
item))}
svg/close])]))
;; (add-property! block *property-key *property-value)
(rum/defcs property-value < rum/reactive
[state block property {:keys [inline-text editor-box page-cp]}]
(let [k (:block/uuid property)
v (get (:block/properties-text-values block)
k
(get (:block/properties block) k))
dom-id (str "ls-property-" k)
editor-id (str "property-" (:db/id block) "-" k)
editing? (state/sub [:editor/editing? editor-id])
schema (:block/schema property)
edit-fn (fn [editor-id id v]
(let [v (str v)
cursor-range (util/caret-range (gdom/getElement (or id dom-id)))]
(state/set-editing! editor-id v block cursor-range)
(js/setTimeout
(fn []
(state/set-editor-action-data! {:block block
:property property
:pos 0})
(state/set-editor-action! :property-value-search)
(state/set-state! :ui/editing-property property))
50)))
multiple-values? (= :many (:cardinality schema))
type (:type schema)]
(cond
multiple-values?
(let [v' (if (coll? v) v (when v [v]))
v' (if (seq v') v' [""])
editor-id' (str editor-id (count v'))
new-editing? (state/sub [:editor/editing? editor-id'])]
[:div.flex.flex-1.flex-col
[:div.flex.flex-1.flex-col
(for [[idx item] (medley/indexed v')]
(let [dom-id' (str dom-id "-" idx)
editor-id' (str editor-id idx)
editing? (state/sub [:editor/editing? editor-id'])]
(if editing?
(editor-box {:format :markdown
:block block} editor-id' {})
(multiple-value-item block property item dom-id' editor-id' {:page-cp page-cp
:edit-fn edit-fn
:inline-text inline-text}))))
(let [fv (first v')]
(when (and (not new-editing?)
fv
(or (and (string? fv) (not (string/blank? fv)))
(and (not (string? fv)) (some? fv))))
[:div.rounded-sm.ml-1
{:on-click (fn []
(edit-fn (str editor-id (count v')) nil ""))}
[:div.flex.flex-row
[:div.block {:style {:height 20
:width 20}}
[:a.add-button-link.block {:title "Add another value"
:style {:margin-left -4}}
(ui/icon "circle-plus")]]]]))]
(when new-editing?
(editor-box {:format :markdown
:block block} editor-id' {}))])
editing?
(editor-box {:format :markdown
:block block} editor-id {})
:else
[:div.flex.flex-1.property-value-content
{:id dom-id
:on-click (fn []
(edit-fn editor-id nil v))}
(cond
(and (= type :date) (string/blank? v))
[:div "TBD (date icon)"]
:else
(when-not (string/blank? (str v))
(inline-text {} :markdown (str v))))])))
(rum/defc properties-area < rum/reactive
[block properties properties-text-values edit-input-id block-components-m]
(let [repo (state/get-current-repo)
new-property? (= edit-input-id (state/sub :ui/new-property-input-id))]
(when (or (seq properties) new-property?)
[:div.ls-properties-area
(when (seq properties)
[:div
(for [[prop-uuid-or-built-in-prop v] properties]
(if (uuid? prop-uuid-or-built-in-prop)
(when-let [property (db/pull [:block/uuid prop-uuid-or-built-in-prop])]
[:div.grid.grid-cols-4.gap-1
[:div.property-key.col-span-1
(property-key block property)]
[:div.property-value.col-span-3
(property-value block property block-components-m)]])
;; TODO: built in properties should have UUID and corresponding schema
;; builtin
[:div
[:a.mr-2 (str prop-uuid-or-built-in-prop)]
[:span v]]))])
(new-property repo block edit-input-id properties new-property?)])))

View File

@@ -0,0 +1,34 @@
.property-value-content {
@apply px-1 rounded-sm;
cursor: text;
min-height: 24px;
}
.property-value-content:hover {
background: var(--ls-secondary-background-color);
}
.ls-properties-area {
.add-button-link {
opacity: 0.5;
}
tr:nth-child(even), tr:nth-child(odd) {
background: none;
}
.editor-inner {
@apply px-1;
}
}
input.simple-input {
@apply px-1;
border-radius: 0;
border: none;
border-bottom: 1px solid;
}
input.simple-input:focus {
box-shadow: none;
}

View File

@@ -4,6 +4,7 @@
[frontend.util :as util]
[frontend.components.block :as block]
[frontend.components.svg :as svg]
[frontend.components.search.highlight :as highlight]
[frontend.handler.route :as route-handler]
[frontend.handler.editor :as editor-handler]
[frontend.handler.page :as page-handler]
@@ -25,44 +26,6 @@
[frontend.modules.shortcut.core :as shortcut]
[frontend.util.text :as text-util]))
(defn highlight-exact-query
[content q]
(if (or (string/blank? content) (string/blank? q))
content
(when (and content q)
(let [q-words (string/split q #" ")
lc-content (util/search-normalize content (state/enable-search-remove-accents?))
lc-q (util/search-normalize q (state/enable-search-remove-accents?))]
(if (and (string/includes? lc-content lc-q)
(not (util/safe-re-find #" " q)))
(let [i (string/index-of lc-content lc-q)
[before after] [(subs content 0 i) (subs content (+ i (count q)))]]
[:div
(when-not (string/blank? before)
[:span before])
[:mark.p-0.rounded-none (subs content i (+ i (count q)))]
(when-not (string/blank? after)
[:span after])])
(let [elements (loop [words q-words
content content
result []]
(if (and (seq words) content)
(let [word (first words)
lc-word (util/search-normalize word (state/enable-search-remove-accents?))
lc-content (util/search-normalize content (state/enable-search-remove-accents?))]
(if-let [i (string/index-of lc-content lc-word)]
(recur (rest words)
(subs content (+ i (count word)))
(vec
(concat result
[[:span (subs content 0 i)]
[:mark.p-0.rounded-none (subs content i (+ i (count word)))]])))
(recur nil
content
result)))
(conj result [:span content])))]
[:p {:class "m-0"} elements]))))))
(defn highlight-page-content-query
"Return hiccup of highlighted page content FTS result"
[content q]
@@ -113,7 +76,7 @@
(clojure.core/uuid uuid)
{:indent? false})])
[:div {:class "font-medium" :key "content"}
(highlight-exact-query content q)]]))
(highlight/highlight-exact-query content q)]]))
(defonce search-timeout (atom nil))
@@ -282,12 +245,12 @@
(search-result-item {:name (if (model/whiteboard-page? data) "whiteboard" "page")
:extension? true
:title (t (if (model/whiteboard-page? data) :search-item/whiteboard :search-item/page))}
(highlight-exact-query data search-q))]
(highlight/highlight-exact-query data search-q))]
:file
(search-result-item {:name "file"
:title (t :search-item/file)}
(highlight-exact-query data search-q))
(highlight/highlight-exact-query data search-q))
:block
(let [{:block/keys [page uuid content]} data ;; content here is normalized

View File

@@ -0,0 +1,43 @@
(ns frontend.components.search.highlight
"Search highlight component"
(:require [frontend.util :as util]
[frontend.state :as state]
[clojure.string :as string]))
(defn highlight-exact-query
[content q]
(if (or (string/blank? content) (string/blank? q))
content
(when (and content q)
(let [q-words (string/split q #" ")
lc-content (util/search-normalize content (state/enable-search-remove-accents?))
lc-q (util/search-normalize q (state/enable-search-remove-accents?))]
(if (and (string/includes? lc-content lc-q)
(not (util/safe-re-find #" " q)))
(let [i (string/index-of lc-content lc-q)
[before after] [(subs content 0 i) (subs content (+ i (count q)))]]
[:div
(when-not (string/blank? before)
[:span before])
[:mark.p-0.rounded-none (subs content i (+ i (count q)))]
(when-not (string/blank? after)
[:span after])])
(let [elements (loop [words q-words
content content
result []]
(if (and (seq words) content)
(let [word (first words)
lc-word (util/search-normalize word (state/enable-search-remove-accents?))
lc-content (util/search-normalize content (state/enable-search-remove-accents?))]
(if-let [i (string/index-of lc-content lc-word)]
(recur (rest words)
(subs content (+ i (count word)))
(vec
(concat result
[[:span (subs content 0 i)]
[:mark.p-0.rounded-none (subs content i (+ i (count word)))]])))
(recur nil
content
result)))
(conj result [:span content])))]
[:p {:class "m-0"} elements]))))))

View File

@@ -1213,6 +1213,20 @@ independent of format as format specific heading characters are stripped"
(distinct)
(sort))))
(defn get-block-property-values
"Get blocks which have this property."
[property-uuid]
(->>
(d/q
'[:find ?b ?v
:in $ ?property-uuid
:where
[?b :block/properties ?p]
[(get ?p ?property-uuid) ?v]
[(some? ?v)]]
(conn/get-db)
property-uuid)))
(defn get-template-by-name
[name]
(when (string? name)

View File

@@ -27,6 +27,7 @@
[frontend.handler.route :as route-handler]
[frontend.handler.db-based.editor :as db-editor-handler]
[frontend.handler.file-based.editor :as file-editor-handler]
[frontend.handler.property :as property-handler]
[frontend.mobile.util :as mobile-util]
[frontend.modules.outliner.core :as outliner-core]
[frontend.modules.outliner.transaction :as outliner-tx]
@@ -1161,10 +1162,17 @@
(defn save-block-aux!
[block value opts]
(let [value (string/trim value)]
;; FIXME: somehow frontend.components.editor's will-unmount event will loop forever
;; maybe we shouldn't save the block/file in "will-unmount" event?
(save-block-if-changed! block value opts)))
(let [entity (db/entity [:block/uuid (:block/uuid block)])
editing-property (:ui/editing-property @state/state)]
(when (:db/id entity)
(let [value (string/trim value)]
(if editing-property
(property-handler/add-property! (state/get-current-repo) entity
(:block/name editing-property)
value)
;; FIXME: somehow frontend.components.editor's will-unmount event will loop forever
;; maybe we shouldn't save the block/file in "will-unmount" event?
(save-block-if-changed! block value opts))))))
(defn save-block!
([repo block-or-uuid content]

View File

@@ -4,6 +4,7 @@
[clojure.string :as string]
[clojure.set :as set]
[frontend.db :as db]
[frontend.db.model :as model]
[frontend.format.block :as block]
[frontend.handler.notification :as notification]
[frontend.modules.outliner.core :as outliner-core]
@@ -46,7 +47,10 @@
refs' (->> refs
(remove string/blank?)
distinct)]
refs'))
(-> (map #(if (util/uuid-string? %)
{:block/uuid (uuid %)}
(block/page-name->map % true)) refs')
set)))
(defn- infer-schema-from-input-string
[v-str]
@@ -84,41 +88,48 @@
[repo block k-name v]
(let [property (db/pull repo '[*] [:block/name k-name])
property-uuid (or (:block/uuid property) (random-uuid))
existing-schema (:block/schema property)
property-type (:type existing-schema)
{:keys [type cardinality]} (:block/schema property)
multiple-values? (= cardinality :many)
infer-schema (infer-schema-from-input-string v)
property-type (or property-type infer-schema :default)
schema (get builtin-schema-types property-type)]
property-type (or type infer-schema :default)
schema (get builtin-schema-types property-type)
properties (:block/properties block)
value (get properties property-uuid)]
(when-let [v* (try
(convert-property-input-string property-type v)
(catch :default e
(notification/show! (str e) :error false)
nil))]
(if-let [msg (me/humanize (mu/explain-data schema v*))]
(notification/show! msg :error false)
(do (when (nil? property) ;if property not exists yet
(when-not (contains? (if (set? value) value #{value}) v*)
(if-let [msg (me/humanize (mu/explain-data schema v*))]
(notification/show! msg :error false)
(do
(when (nil? property) ;if property not exists yet
(db/transact! repo [(outliner-core/block-with-timestamps
{:block/schema {:type property-type}
:block/original-name k-name
:block/name (util/page-name-sanity-lc k-name)
:block/uuid property-uuid
:block/type "property"})]))
(let [block-properties (assoc (:block/properties block)
property-uuid
(if (= property-type :default)
(let [refs (extract-page-refs-from-prop-str-value v*)]
(if (seq refs) (set refs) v*))
v*))
(let [refs (when (= property-type :default) (extract-page-refs-from-prop-str-value v*))
refs' (when (seq refs)
(concat (:block/refs (db/pull [:block/uuid (:block/uuid block)]))
refs))
v' (if (= property-type :default)
(if (seq refs) refs v*)
v*)
new-value (if multiple-values? (vec (distinct (conj value v'))) v')
block-properties (assoc properties property-uuid new-value)
block-properties-text-values
(if (= property-type :default)
(if (and (not multiple-values?) (= property-type :default))
(assoc (:block/properties-text-values block) property-uuid v*)
(dissoc (:block/properties-text-values block) property-uuid))]
(outliner-tx/transact!
{:outliner-op :save-block}
(outliner-core/save-block!
{:block/uuid (:block/uuid block)
;; TODO: fix block/properties-order
(db/transact! repo
[{:block/uuid (:block/uuid block)
:block/properties block-properties
:block/properties-text-values block-properties-text-values}))))))))
:block/properties-text-values block-properties-text-values
:block/refs refs'}]))))))))
(defn remove-property!
[repo block k-uuid-or-builtin-k-name]
@@ -131,65 +142,50 @@
:block/properties (dissoc origin-properties k-uuid-or-builtin-k-name)
:block/properties-text-values (dissoc (:block/properties-text-values block) k-uuid-or-builtin-k-name)}])))
(defn- fix-cardinality-many-values!
[property-uuid]
(let [ev (->> (model/get-block-property-values property-uuid)
(remove (fn [[_ v]] (coll? v))))
tx-data (map (fn [[e v]]
(let [entity (db/entity e)
properties (:block/properties entity)]
{:db/id e
:block/properties (assoc properties property-uuid [v])})) ev)]
(when (seq tx-data)
(db/transact! tx-data))))
(defn update-property!
[repo property-uuid {:keys [property-name property-schema]}]
{:pre [(uuid? property-uuid)]}
(let [tx-data (cond-> {:block/uuid property-uuid}
property-name (assoc :block/name property-name)
property-schema (assoc :block/schema property-schema)
true outliner-core/block-with-updated-at)]
(db/transact! repo [tx-data])))
(when-let [property (db/entity [:block/uuid property-uuid])]
(when (and (= :many (:cardinality property-schema))
(not= :many (:cardinality (:block/schema property))))
;; cardinality changed from :one to :many
(fix-cardinality-many-values! property-uuid))
(let [tx-data (cond-> {:block/uuid property-uuid}
property-name (assoc :block/name property-name)
property-schema (assoc :block/schema property-schema)
true outliner-core/block-with-updated-at)]
(db/transact! repo [tx-data]))))
(defn- extract-refs
[entity properties]
(let [property-values (->>
properties
(map (fn [[k v]]
(let [schema (:block/schema (db/pull [:block/uuid k]))
object? (= (:type schema) :object)
f (if object? page-ref/->page-ref identity)]
(->> (if (coll? v)
v
[v])
(map f)))))
(apply concat)
(filter string?))
block-text (string/join " "
(cons
(:block/content entity)
property-values))
ast-refs (gp-mldoc/get-references block-text (gp-mldoc/default-config :markdown))
refs (map #(or (gp-block/get-page-reference % #{})
(gp-block/get-block-reference %)) ast-refs)
refs' (->> refs
(remove string/blank?)
distinct)]
(map #(if (util/uuid-string? %)
[:block/uuid (uuid %)]
(block/page-name->map % true)) refs')))
(comment
(defn delete-property-value!
"Delete value if a property has multiple values"
[entity property-id property-value]
(when (and entity (uuid? property-id))
(when (not= property-id (:block/uuid entity))
(when-let [property (db/pull [:block/uuid property-id])]
(let [schema (:block/schema property)
[success? property-value-or-error] (validate schema property-value)
multiple-values? (:multiple-values? schema)]
(when (and multiple-values? success?)
(let [properties (:block/properties entity)
properties' (update properties property-id disj property-value-or-error)
refs (extract-refs entity properties')]
(outliner-tx/transact!
{:outliner-op :save-block}
(outliner-core/save-block!
{:block/uuid (:block/uuid entity)
:block/properties properties'
:block/refs refs}))))
(state/clear-editor-action!)
(state/clear-edit!)))))))
(defn delete-property-value!
"Delete value if a property has multiple values"
[repo block property-id property-value]
(when (and block (uuid? property-id))
(when (not= property-id (:block/uuid block))
(when-let [property (db/pull [:block/uuid property-id])]
(let [schema (:block/schema property)]
(when (= :many (:cardinality schema))
(let [properties (:block/properties block)
properties' (update properties property-id
(fn [col]
(vec (remove #{property-value} col))))]
(outliner-tx/transact!
{:outliner-op :save-block}
(outliner-core/save-block!
{:block/uuid (:block/uuid block)
:block/properties properties'}))))
(state/clear-editor-action!))))))
(defn set-editing-new-property!
[value]

View File

@@ -1872,7 +1872,8 @@ Similar to re-frame subscriptions"
(assoc
:editor/editing? {edit-input-id true}
:editor/set-timestamp-block nil
:cursor-range cursor-range))))
:cursor-range cursor-range
:ui/editing-property nil))))
(set-state! :editor/block block)
(set-state! :editor/content content :path-in-sub-atom edit-input-id)
(set-state! :editor/last-key-code nil)