diff --git a/deps/db/src/logseq/db/frontend/malli_schema.cljs b/deps/db/src/logseq/db/frontend/malli_schema.cljs index 9a3045b0f5..5cbdaf4f20 100644 --- a/deps/db/src/logseq/db/frontend/malli_schema.cljs +++ b/deps/db/src/logseq/db/frontend/malli_schema.cljs @@ -130,10 +130,8 @@ [:description {:optional true} :string] ;; For any types except for :checkbox :default :template :enum [:cardinality {:optional true} [:enum :one :many]] - ;; Just for :enum type - [:enum-config {:optional true} - [:map - [:values [:vector :uuid]]]] + ;; closed values + [:values {:optional true} [:vector :uuid]] ;; Just for :enum [:position {:optional true} :string] ;; For :page and :template @@ -239,7 +237,7 @@ (vec (concat [:map] - [[:block/type [:= #{"enum value"}]] + [[:block/type [:= #{"closed value"}]] [:block/schema {:optional true} [:map [:description :string]]] diff --git a/deps/db/src/logseq/db/frontend/property/type.cljs b/deps/db/src/logseq/db/frontend/property/type.cljs index cbb4de03da..08c9aa0926 100644 --- a/deps/db/src/logseq/db/frontend/property/type.cljs +++ b/deps/db/src/logseq/db/frontend/property/type.cljs @@ -8,7 +8,11 @@ (def user-builtin-schema-types "Valid schema :type for users in order they appear in the UI" - [:default :number :date :checkbox :url :page :template :enum]) + [:default :number :date :checkbox :url :page :template]) + +(def closed-values-schema-types + "Valid schema :type for close values" + #{:default :number :date :url :page}) ;; TODO: ;; Validate && list fixes for non-validated values when updating property schema @@ -57,7 +61,6 @@ :template [:fn {:error/message "should has #template"} logseq-template?] - :enum some? ; the value could be anything such as number, text, url, date, page, image, video, etc. ;; internal usage :keyword keyword? :map map? @@ -72,4 +75,4 @@ (assert (= (set (keys builtin-schema-types)) (into internal-builtin-schema-types user-builtin-schema-types)) - "Built-in schema types must be equal") \ No newline at end of file + "Built-in schema types must be equal") diff --git a/src/main/frontend/components/block.cljs b/src/main/frontend/components/block.cljs index 5cbc5f015a..956887a83c 100644 --- a/src/main/frontend/components/block.cljs +++ b/src/main/frontend/components/block.cljs @@ -2303,12 +2303,12 @@ :tag? true :disable-preview? true) tag))])) -(rum/defc block-enum-properties +(rum/defc block-closed-values-properties [block] - (let [enum-properties (property-handler/get-block-enum-other-position-properties (:db/id block))] - (when (seq enum-properties) - [:div.enum-properties.flex.flex-row.items-center.gap-1.select-none.h-full - (for [pid enum-properties] + (let [closed-values-properties (property-handler/get-block-other-position-properties (:db/id block))] + (when (seq closed-values-properties) + [:div.closed-values-properties.flex.flex-row.items-center.gap-1.select-none.h-full + (for [pid closed-values-properties] (when-let [property (db/entity [:block/uuid pid])] (pv/property-value block property (get (:block/properties block) pid) {:icon? true})))]))) @@ -2474,7 +2474,7 @@ repo (state/get-current-repo) db-based? (config/db-based-graph? repo)] [:div.flex.flex-1.flex-row.flex-wrap.gap-1.items-start - (block-enum-properties block) + (block-closed-values-properties block) (if (and edit? editor-box) [:div.editor-wrapper.flex.flex-1 {:id editor-id} diff --git a/src/main/frontend/components/property.cljs b/src/main/frontend/components/property.cljs index 53fe58d8bf..4261899fbf 100644 --- a/src/main/frontend/components/property.cljs +++ b/src/main/frontend/components/property.cljs @@ -26,10 +26,10 @@ [frontend.components.icon :as icon-component] [frontend.components.dnd :as dnd] [dommy.core :as dom] - [frontend.components.property.enum :as enum] + [frontend.components.property.closed-value :as closed-value] [frontend.components.property.util :as components-pu])) -(def icon enum/icon) +(def icon closed-value/icon) (defn- create-class-if-not-exists! [value] @@ -134,7 +134,8 @@ add-new-property?) class? (contains? (:block/type block) "class") property-type (get-in property [:block/schema :type]) - save-property-fn (fn [] (components-pu/update-property! property @*property-name @*property-schema))] + save-property-fn (fn [] (components-pu/update-property! property @*property-name @*property-schema)) + enable-closed-values? (contains? db-property-type/closed-values-schema-types (or property-type :default))] [:div.property-configure.flex.flex-1.flex-col {:on-mouse-down #(state/set-state! :editor/mouse-down-from-property-configure? true) :on-mouse-up #(state/set-state! :editor/mouse-down-from-property-configure? nil)} @@ -154,14 +155,14 @@ [:label.col-span-1 "Icon:"] (let [icon-value (pu/get-property property :icon)] [:div.col-span-3 - (enum/icon icon-value - {:disabled? disabled? - :on-chosen (fn [_e icon] - (let [icon-property-id (pu/get-built-in-property-uuid :icon)] - (property-handler/update-property! - (state/get-current-repo) - (:block/uuid property) - {:properties {icon-property-id icon}})))})])] + (closed-value/icon icon-value + {:disabled? disabled? + :on-chosen (fn [_e icon] + (let [icon-property-id (pu/get-built-in-property-uuid :icon)] + (property-handler/update-property! + (state/get-current-repo) + (:block/uuid property) + {:properties {icon-property-id icon}})))})])] [:div.grid.grid-cols-4.gap-1.items-center.leading-8 [:label.col-span-1 "Schema type:"] @@ -185,15 +186,27 @@ (swap! *property-schema assoc :type type) (components-pu/update-property! property @*property-name @*property-schema))))]))] + (when-not (contains? #{:checkbox :default :template} (:type @*property-schema)) + [:div.grid.grid-cols-4.gap-1.items-center.leading-8 + [:label "Multiple values:"] + (let [many? (boolean (= :many (:cardinality @*property-schema)))] + (ui/checkbox {:checked many? + :disabled disabled? + :on-change (fn [] + (swap! *property-schema assoc :cardinality (if many? :one :many)) + (save-property-fn))}))]) + + (case (:type @*property-schema) :page - [:div.grid.grid-cols-4.gap-1.items-center.leading-8 - [:label "Specify classes:"] - (class-select *property-schema - (:classes @*property-schema) - (assoc opts - :disabled? disabled? - :save-property-fn save-property-fn))] + (when (empty? (:values @*property-schema)) + [:div.grid.grid-cols-4.gap-1.items-center.leading-8 + [:label "Specify classes:"] + (class-select *property-schema + (:classes @*property-schema) + (assoc opts + :disabled? disabled? + :save-property-fn save-property-fn))]) :template [:div.grid.grid-cols-4.gap-1.items-center.leading-8 @@ -204,15 +217,15 @@ :disabled? disabled? :save-property-fn save-property-fn))] - :enum - [:div.grid.grid-cols-4.gap-1.items-start.leading-8 - [:label.col-span-1 "Enum choices:"] - [:div.col-span-3 - (enum/enum-choices property *property-name *property-schema)]] - nil) - (when (= :enum (:type @*property-schema)) + (when (and enable-closed-values? (empty? (:classes @*property-schema))) + [:div.grid.grid-cols-4.gap-1.items-start.leading-8 + [:label.col-span-1 "Available choices:"] + [:div.col-span-3 + (closed-value/choices property *property-name *property-schema)]]) + + (when (and enable-closed-values? (seq (:values @*property-schema))) (let [position (:position @*property-schema) choices (map (fn [item] @@ -226,7 +239,7 @@ ;; {:label "Ending of the block" ;; :value "block-ending"} ])] - [:div.grid.grid-cols-4.gap-1.items-start.leading-8 + [:div.grid.grid-cols-4.gap-1.items-center.leading-8 [:label.col-span-1 "UI position:"] [:div.col-span-3 (ui/select choices @@ -234,16 +247,6 @@ (swap! *property-schema assoc :position v) (save-property-fn)))]])) - (when-not (contains? #{:checkbox :default :template :enum} (:type @*property-schema)) - [:div.grid.grid-cols-4.gap-1.items-center.leading-8 - [:label "Multiple values:"] - (let [many? (boolean (= :many (:cardinality @*property-schema)))] - (ui/checkbox {:checked many? - :disabled disabled? - :on-change (fn [] - (swap! *property-schema assoc :cardinality (if many? :one :many)) - (save-property-fn))}))]) - (let [hide? (:hide? @*property-schema)] [:div.grid.grid-cols-4.gap-1.items-center.leading-8 [:label "Hide by default:"] @@ -538,14 +541,16 @@ (when (uuid? k) (when-let [property (db/sub-block (:db/id (db/entity [:block/uuid k])))] (let [type (get-in property [:block/schema :type] :default) + closed-values? (seq (get-in property [:block/schema :values])) v-block (when (uuid? v) (db/entity [:block/uuid v])) block? (and v-block + (not closed-values?) (:block/page v-block) (contains? #{:default :template} type)) collapsed? (when block? (property-collapsed? block property)) date? (= type :date)] [:div {:class (cond - block? + (and block? (not closed-values?)) "flex flex-1 flex-col gap-1 property-block" date? "property-pair items-center" diff --git a/src/main/frontend/components/property/closed_value.cljs b/src/main/frontend/components/property/closed_value.cljs new file mode 100644 index 0000000000..80ddd49fcf --- /dev/null +++ b/src/main/frontend/components/property/closed_value.cljs @@ -0,0 +1,191 @@ +(ns frontend.components.property.closed-value + "Enum property config" + (:require [rum.core :as rum] + [clojure.string :as string] + [frontend.modules.shortcut.core :as shortcut] + [frontend.util :as util] + [frontend.ui :as ui] + [frontend.components.dnd :as dnd] + [frontend.components.icon :as icon-component] + [frontend.components.property.util :as pu-component] + [frontend.handler.property :as property-handler] + [frontend.components.property.value :as property-value] + [frontend.db :as db] + [frontend.state :as state] + [frontend.handler.property.util :as pu])) + +(defn- upsert-closed-value! + "Create new closed value and returns its block UUID." + [property item] + (let [{:keys [block-id tx-data]} (property-handler/upsert-closed-value property item)] + (when (seq tx-data) (db/transact! tx-data)) + block-id)) + +(rum/defc icon + [icon {:keys [disabled? on-chosen]}] + (ui/dropdown + (fn [{:keys [toggle-fn]}] + [:button.flex {:on-click #(when-not disabled? (toggle-fn))} + (if icon + (icon-component/icon icon) + [:span.bullet-container.cursor [:span.bullet]])]) + (fn [{:keys [toggle-fn]}] + [:div.p-4 + (icon-component/icon-search + {:on-chosen (fn [e icon] + (on-chosen e icon) + (toggle-fn))})]) + {:modal-class (util/hiccup->class + "origin-top-right.absolute.left-0.rounded-md.shadow-lg")})) + +(rum/defc item-value + [type *value] + (case type + ;; :page + :date + (let [value (if (string/blank? @*value) nil @*value)] + (property-value/date-picker value + {:on-change (fn [page] + (reset! *value (:block/uuid page)))})) + [:input.form-input.col-span-3 + {:default-value @*value + :auto-focus true + :on-change #(reset! *value (util/evalue %))}])) + +(rum/defcs item-config < rum/reactive + shortcut/disable-all-shortcuts + {:init (fn [state] + (let [block (second (:rum/args state)) + value (or (str (get-in block [:block/schema :value])) "") + icon (when block (pu/get-property block :icon)) + description (or (get-in block [:block/schema :description]) "")] + (assoc state + ::value (atom value) + ::icon (atom icon) + ::description (atom description))))} + [state property _item {:keys [toggle-fn on-save]}] + (let [*value (::value state) + *icon (::icon state) + *description (::description state) + save-handler (fn [e] + (util/stop e) + (when-not (string/blank? @*value) + (when on-save + (let [value (if (string? @*value) + (string/trim @*value) + @*value)] + (on-save value @*icon @*description))) + (when toggle-fn (toggle-fn)))) + property-type (get-in property [:block/schema :type])] + [:div.flex.flex-col.gap-4.p-4.whitespace-nowrap.w-96 + {:on-key-down (fn [e] + (when (= e.key "Enter") + (save-handler e)))} + [:div.grid.grid-cols-5.gap-1.items-center.leading-8 + [:label.col-span-2 "Value:"] + (item-value property-type *value)] + [:div.grid.grid-cols-5.gap-1.items-center.leading-8 + [:label.col-span-2 "Icon:"] + [:div.col-span-3 + (icon (rum/react *icon) + {:on-chosen (fn [_e icon] + (reset! *icon icon))})]] + [:div.grid.grid-cols-5.gap-1.items-start.leading-8 + [:label.col-span-2 "Description:"] + [:div.col-span-3 + (ui/ls-textarea + {:on-change #(reset! *description (util/evalue %)) + :default-value @*description})]] + [:div + (ui/button + "Save" + {:on-click save-handler})]])) + +(rum/defcs choice-with-close < + (rum/local false ::hover?) + [state item {:keys [toggle-fn delete-choice update-icon]}] + (let [*hover? (::hover? state) + value (or (:block/original-name item) + (get-in item [:block/schema :value]))] + [:div.flex.flex-1.flex-row.items-center.gap-2.justify-between + {:on-mouse-over #(reset! *hover? true) + :on-mouse-out #(reset! *hover? false)} + [:div.flex.flex-row.items-center.gap-2 + (icon (pu/get-property item :icon) + {:on-chosen (fn [_e icon] + (update-icon icon))}) + [:a {:on-click toggle-fn} + value]] + (when @*hover? + [:a.fade-link.flex {:on-click delete-choice + :title "Delete this choice"} + (ui/icon "X")])])) + +(rum/defc choice-item-content + [property block dropdown-opts] + (let [{:block/keys [uuid]} block] + (ui/dropdown + (fn [opts] + (choice-with-close + block + (assoc opts + :delete-choice + (fn [] + (property-handler/delete-closed-value property block)) + :update-icon + (fn [icon] + (property-handler/set-block-property! (state/get-current-repo) (:block/uuid block) :icon icon))))) + (fn [opts] + (item-config + property + block + (assoc opts :on-save + (fn [value icon description] + (upsert-closed-value! property {:id uuid + :value value + :description description + :icon icon}))))) + dropdown-opts))) + +(rum/defc choices < rum/reactive + [property *property-name *property-schema] + (let [schema (:block/schema property) + property-type (:type schema) + values (:values schema) + dropdown-opts {:modal-class (util/hiccup->class + "origin-top-right.absolute.left-0.rounded-md.shadow-lg")}] + [:div.closed-values.flex.flex-col + (let [choices (doall + (keep (fn [id] + (when-let [block (db/sub-block (:db/id (db/entity [:block/uuid id])))] + {:id (str id) + :value id + :content (choice-item-content property block dropdown-opts)})) + values))] + (dnd/items choices + {:on-drag-end (fn [new-values] + (when (seq new-values) + (swap! *property-schema assoc :values new-values) + (pu-component/update-property! property @*property-name @*property-schema)))})) + (ui/dropdown + (fn [{:keys [toggle-fn]}] + [:a.fade-link.flex.flex-row.items-center.gap-1.leading-8 {:on-click toggle-fn} + (ui/icon "plus" {:size 16}) + "Add choice"]) + (fn [opts] + (if (= :page property-type) + (property-value/select-page property + {:multiple-choices? false + :dropdown? false + :close-modal? false + :on-chosen (fn [chosen] + (upsert-closed-value! property {:value chosen}))}) + (item-config + property + nil + (assoc opts :on-save + (fn [value icon description] + (upsert-closed-value! property {:value value + :description description + :icon icon})))))) + dropdown-opts)])) diff --git a/src/main/frontend/components/property/enum.cljs b/src/main/frontend/components/property/enum.cljs deleted file mode 100644 index 3c8a961901..0000000000 --- a/src/main/frontend/components/property/enum.cljs +++ /dev/null @@ -1,196 +0,0 @@ -(ns frontend.components.property.enum - "Enum property config" - (:require [rum.core :as rum] - [clojure.string :as string] - [frontend.modules.shortcut.core :as shortcut] - [frontend.util :as util] - [frontend.ui :as ui] - [frontend.components.dnd :as dnd] - [frontend.components.icon :as icon-component] - [frontend.components.property.util :as components-pu] - [frontend.handler.notification :as notification] - [frontend.handler.property :as property-handler] - [frontend.db :as db] - [frontend.state :as state] - [frontend.handler.property.util :as pu])) - -(defn- upsert-enum-item! - "Create new enum value and returns its block UUID." - [property item] - (let [{:keys [block-id tx-data]} (property-handler/upsert-enum-item property item)] - (when (seq tx-data) (db/transact! tx-data)) - block-id)) - -(rum/defc icon - [icon {:keys [disabled? on-chosen]}] - (ui/dropdown - (fn [{:keys [toggle-fn]}] - [:button.flex {:on-click #(when-not disabled? (toggle-fn))} - (if icon - (icon-component/icon icon) - [:span.bullet-container.cursor [:span.bullet]])]) - (fn [{:keys [toggle-fn]}] - [:div.p-4 - (icon-component/icon-search - {:on-chosen (fn [e icon] - (on-chosen e icon) - (toggle-fn))})]) - {:modal-class (util/hiccup->class - "origin-top-right.absolute.left-0.rounded-md.shadow-lg")})) - -(rum/defcs enum-item-config < rum/reactive - shortcut/disable-all-shortcuts - {:init (fn [state] - (let [block (first (:rum/args state))] - (let [name (or (:block/content block) "") - icon (pu/get-property block :icon) - description (or (get-in block [:block/schema :description]) "")] - (assoc state - ::name (atom name) - ::icon (atom icon) - ::description (atom description)))))} - [state _item {:keys [toggle-fn on-save]}] - (let [*name (::name state) - *icon (::icon state) - *description (::description state)] - [:div.flex.flex-col.gap-4.p-4.whitespace-nowrap.w-96 - [:div.grid.grid-cols-5.gap-1.items-center.leading-8 - [:label.col-span-2 "Name:"] - [:input.form-input.col-span-3 - {:default-value @*name - :on-change #(reset! *name (util/evalue %))}]] - [:div.grid.grid-cols-5.gap-1.items-center.leading-8 - [:label.col-span-2 "Icon:"] - [:div.col-span-3 - (icon (rum/react *icon) - {:on-chosen (fn [_e icon] - (reset! *icon icon))})]] - [:div.grid.grid-cols-5.gap-1.items-start.leading-8 - [:label.col-span-2 "Description:"] - [:div.col-span-3 - (ui/ls-textarea - {:on-change #(reset! *description (util/evalue %)) - :default-value @*description})]] - [:div - (ui/button - "Save" - :on-click (fn [e] - (util/stop e) - (when-not (string/blank? @*name) - (when on-save (on-save (string/trim @*name) @*icon @*description)) - (when toggle-fn (toggle-fn)))))]])) - -(rum/defcs enum-new-item < - (rum/local "" ::name) - (rum/local "" ::icon) - (rum/local "" ::description) - [state {:keys [toggle-fn on-save]}] - (let [*name (::name state) - *icon (::icon state) - *description (::description state)] - [:div.flex.flex-col.gap-4.p-4.whitespace-nowrap.w-96 - [:div.grid.grid-cols-5.gap-1.items-center.leading-8 - [:label.col-span-2 "Name:"] - [:input.form-input.col-span-3 - {:default-value "" - :auto-focus true - :on-change (fn [e] (reset! *name (util/evalue e)))}]] - [:div.grid.grid-cols-5.gap-1.items-center.leading-8 - [:label.col-span-2 "Icon:"] - [:div.col-span-3 - (icon nil {:on-chosen (fn [_e icon] - (reset! *icon icon))})]] - [:div.grid.grid-cols-5.gap-1.items-start.leading-8 - [:label.col-span-2 "Description:"] - [:div.col-span-3 - (ui/ls-textarea - {:on-change #(reset! *description (util/evalue %)) - :default-value @*description})]] - [:div - (ui/button - "Save" - :on-click (fn [e] - (util/stop e) - (when-not (string/blank? @*name) - (let [result (when on-save (on-save (string/trim @*name) - @*icon - @*description))] - (if (= :value-exists result) - (notification/show! (str "Choice already exist") :warning) - (when toggle-fn (toggle-fn)))))))]])) - -(rum/defcs choice-with-close < - (rum/local false ::hover?) - [state item name {:keys [toggle-fn delete-choice update-icon]}] - (let [*hover? (::hover? state)] - [:div.flex.flex-1.flex-row.items-center.gap-2.justify-between - {:on-mouse-over #(reset! *hover? true) - :on-mouse-out #(reset! *hover? false)} - [:div.flex.flex-row.items-center.gap-2 - (icon (pu/get-property item :icon) - {:on-chosen (fn [_e icon] - (update-icon icon))}) - [:a {:on-click toggle-fn} - name]] - (when @*hover? - [:a.fade-link.flex {:on-click delete-choice - :title "Delete this choice"} - (ui/icon "X")])])) - -(rum/defc choice-item-content - [property block dropdown-opts] - (let [{:block/keys [uuid content]} block] - (ui/dropdown - (fn [opts] - (choice-with-close - block - content - (assoc opts - :delete-choice - (fn [] - (property-handler/delete-enum-item property block)) - :update-icon - (fn [icon] - (property-handler/update-property! (state/get-current-repo) - (pu/get-pid "icon") - icon))))) - (fn [opts] - (enum-item-config - block - (assoc opts :on-save - (fn [name icon description] - (upsert-enum-item! property {:id uuid - :name name - :description description - :icon icon}))))) - dropdown-opts))) - -(rum/defc enum-choices - [property *property-name *property-schema] - (let [values (get-in property [:block/schema :enum-config :values]) - dropdown-opts {:modal-class (util/hiccup->class - "origin-top-right.absolute.left-0.rounded-md.shadow-lg")}] - [:div.enum-choices.flex.flex-col - (let [choices (keep (fn [id] - (when-let [block (db/entity [:block/uuid id])] - {:id (str id) - :value id - :content (choice-item-content property block dropdown-opts)})) - values)] - (dnd/items choices - {:on-drag-end (fn [new-values] - (when (seq new-values) - (swap! *property-schema assoc-in [:enum-config :values] new-values) - (components-pu/update-property! property @*property-name @*property-schema)))})) - (ui/dropdown - (fn [{:keys [toggle-fn]}] - [:a.fade-link.flex.flex-row.items-center.gap-1.leading-8 {:on-click toggle-fn} - (ui/icon "plus" {:size 16}) - "Add choice"]) - (fn [opts] - (enum-new-item (assoc opts :on-save - (fn [name icon description] - (upsert-enum-item! property {:name name - :description description - :icon icon}))))) - dropdown-opts)])) diff --git a/src/main/frontend/components/property/value.cljs b/src/main/frontend/components/property/value.cljs index 0ce1b20e94..44b1131578 100644 --- a/src/main/frontend/components/property/value.cljs +++ b/src/main/frontend/components/property/value.cljs @@ -21,8 +21,10 @@ [frontend.handler.property.util :as pu])) (defn- select-type? - [type] - (contains? #{:page :number :url :date :enum} type)) + [property type] + (or (contains? #{:page :number :url :date} type) + ;; closed values + (seq (get-in property [:block/schema :values])))) (defn exit-edit-property ([] @@ -62,9 +64,8 @@ (route-handler/redirect-to-page! (date/js-date->journal-title value)))) (rum/defc date-picker - [block property value opts] - (let [multiple-values? (= :many (:cardinality (:block/schema property))) - title (when (uuid? value) + [value {:keys [multiple-values? on-change] :as opts}] + (let [title (when (uuid? value) (:block/original-name (db/entity [:block/uuid value]))) value (if title (js/Date. (date/journal-title->long title)) @@ -93,15 +94,12 @@ (when-not title (ui/icon "calendar" {:size 15}))]]) (fn [{:keys [toggle-fn]}] (ui/datepicker value' {:on-change (fn [_e date] - (let [repo (state/get-current-repo) - journal (date/js-date->journal-title date)] + (let [journal (date/js-date->journal-title date)] (when-not (db/entity [:block/name (util/page-name-sanity-lc journal)]) (page-handler/create! journal {:redirect? false :create-first-block? false})) - (when-let [page (db/entity [:block/name (util/page-name-sanity-lc journal)])] - (property-handler/set-block-property! repo (:block/uuid block) - (:block/name property) - (:block/uuid page))) + (when (fn? on-change) + (on-change (db/entity [:block/name (util/page-name-sanity-lc journal)]))) (exit-edit-property) (toggle-fn) (when-let [toggle (:toggle-fn opts)] @@ -109,6 +107,20 @@ {:modal-class (util/hiccup->class "origin-top-right.absolute.left-0.rounded-md.shadow-lg.mt-2")}))) + +(rum/defc property-value-date-picker + [block property value opts] + (let [multiple-values? (= :many (:cardinality (:block/schema property)))] + (date-picker value + (merge opts + {:multiple-values? multiple-values? + :on-change (fn [page] + (let [repo (state/get-current-repo)] + (property-handler/set-block-property! repo (:block/uuid block) + (:block/name property) + (:block/uuid page)) + (exit-edit-property)))})))) + (defn- create-page-if-not-exists! [property classes page] (let [page* (string/trim page) @@ -153,29 +165,31 @@ :items items' k f')))) -(defn- select-page - [block property - {:keys [classes multiple-choices? dropdown?] :as opts} - {:keys [*show-new-property-config?]}] +(defn select-page + [property + {:keys [block classes multiple-choices? dropdown? input-opts on-chosen] :as opts}] (let [repo (state/get-current-repo) tags? (= "tags" (:block/name property)) alias? (= "alias" (:block/name property)) tags-or-alias? (or tags? alias?) - selected-choices (->> - (if tags-or-alias? - (->> (if (= "tags" (:block/name property)) - (:block/tags block) - (:block/alias block)) - (map (fn [e] (:block/original-name e)))) - (let [v (get-in block [:block/properties (:block/uuid property)])] - (if (coll? v) - (map (fn [id] - (:block/original-name (db/entity [:block/uuid id]))) - v) - [(:block/original-name (db/entity [:block/uuid v]))]))) - (remove nil?)) + selected-choices (when block + (->> + (if tags-or-alias? + (->> (if (= "tags" (:block/name property)) + (:block/tags block) + (:block/alias block)) + (map (fn [e] (:block/original-name e)))) + (when-let [v (get-in block [:block/properties (:block/uuid property)])] + (if (coll? v) + (map (fn [id] + (:block/original-name (db/entity [:block/uuid id]))) + v) + [(:block/original-name (db/entity [:block/uuid v]))]))) + (remove nil?))) + closed-values (seq (get-in property [:block/schema :values])) pages (->> - (if (seq classes) + (cond + (seq classes) (mapcat (fn [class] (if (= :logseq.class class) @@ -184,68 +198,86 @@ (model/get-class-objects repo) (map #(:block/original-name (db/entity %)))))) classes) + + (and block closed-values) + (map (fn [id] (:block/original-name (db/entity [:block/uuid id]))) closed-values) + + :else (model/get-all-page-original-names repo)) distinct) options (map (fn [p] {:value p}) pages) - opts (cond-> - {:multiple-choices? multiple-choices? - :items options - :selected-choices selected-choices - :dropdown? dropdown? - :input-default-placeholder (cond - tags? - "Set tags" - alias? - "Set alias" - multiple-choices? - "Choose pages" - :else - "Choose page") - :show-new-when-not-exact-match? true - :extract-chosen-fn :value - ;; Provides additional completion for inline classes on new pages - :transform-fn (fn [results input] - (if-let [[_ new-page class-input] (and (empty? results) (re-find #"(.*)#(.*)$" input))] - (let [repo (state/get-current-repo) - class-names (map #(:block/original-name (db/entity repo [:block/uuid %])) classes) - descendent-classes (->> class-names - (mapcat #(db/get-namespace-pages repo %)) - (map :block/original-name))] - (->> (concat class-names descendent-classes) - (filter #(string/includes? % class-input)) - (mapv #(hash-map :value (str new-page "#" %))))) - results)) - :input-opts (fn [_] - {:on-blur (fn [] - (exit-edit-property)) - :on-click (fn [] - (when *show-new-property-config? - (reset! *show-new-property-config? false))) - :on-key-down - (fn [e] - (case (util/ekey e) - "Escape" - (do - (exit-edit-property) - (when-let [f (:on-chosen opts)] (f))) - nil))})} - multiple-choices? - (assoc :on-apply (fn [choices] - (let [pages (->> choices - (map #(create-page-if-not-exists! property classes %)) - (map first)) - values (set (map #(pu/get-page-uuid repo %) pages))] - (add-property! block (:block/original-name property) values) - (when-let [f (:on-chosen opts)] (f))))) - (not multiple-choices?) - (assoc :on-chosen (fn [chosen] - (let [page* (string/trim (if (string? chosen) chosen (:value chosen)))] - (when-not (string/blank? page*) - (let [[page id] (create-page-if-not-exists! property classes page*) - id' (or id (pu/get-page-uuid repo page))] - (add-property! block (:block/original-name property) id') - (when-let [f (:on-chosen opts)] (f))))))))] - (select-aux block property opts))) + opts' (cond-> + (merge + opts + {:multiple-choices? multiple-choices? + :items options + :selected-choices selected-choices + :dropdown? dropdown? + :input-default-placeholder (cond + tags? + "Set tags" + alias? + "Set alias" + multiple-choices? + "Choose pages" + :else + "Choose page") + :show-new-when-not-exact-match? (not (and block closed-values)) + :extract-chosen-fn :value + ;; Provides additional completion for inline classes on new pages + :transform-fn (fn [results input] + (if-let [[_ new-page class-input] (and (empty? results) (re-find #"(.*)#(.*)$" input))] + (let [repo (state/get-current-repo) + class-names (map #(:block/original-name (db/entity repo [:block/uuid %])) classes) + descendent-classes (->> class-names + (mapcat #(db/get-namespace-pages repo %)) + (map :block/original-name))] + (->> (concat class-names descendent-classes) + (filter #(string/includes? % class-input)) + (mapv #(hash-map :value (str new-page "#" %))))) + results)) + :input-opts input-opts}) + multiple-choices? + (assoc :on-apply (fn [choices] + (let [pages (->> choices + (map #(create-page-if-not-exists! property classes %)) + (map first)) + values (set (map #(pu/get-page-uuid repo %) pages))] + (when on-chosen (on-chosen values))))) + (not multiple-choices?) + (assoc :on-chosen (fn [chosen] + (let [page* (string/trim (if (string? chosen) chosen (:value chosen)))] + (when-not (string/blank? page*) + (let [[page id] (create-page-if-not-exists! property classes page*) + id' (or id (pu/get-page-uuid repo page))] + (when on-chosen (on-chosen id'))))))))] + (select-aux block property opts'))) + +(defn property-value-select-page + [block property + {:keys [on-chosen] :as opts} + {:keys [*show-new-property-config?]}] + (let [input-opts (fn [_] + {:on-blur (fn [] + (exit-edit-property)) + :on-click (fn [] + (when *show-new-property-config? + (reset! *show-new-property-config? false))) + :on-key-down + (fn [e] + (case (util/ekey e) + "Escape" + (do + (exit-edit-property) + (when-let [f (:on-chosen opts)] (f))) + nil))}) + opts' (assoc opts + :block block + :input-opts input-opts + :on-chosen (fn [values] + (add-property! block (:block/original-name property) values) + (when on-chosen (on-chosen))))] + (select-page property opts'))) ;; (defn- move-cursor ;; [up? opts] @@ -328,18 +360,19 @@ (let [schema (:block/schema property) property (db/sub-block (:db/id property)) type (:type schema) - enum? (= :enum type) - items (if enum? + closed-values? (seq (:values schema)) + items (if closed-values? (keep (fn [id] (when-let [block (when id (db/entity [:block/uuid id]))] (let [icon (pu/get-property block :icon) - name (:block/content block)] + value (or (:block/original-name block) + (get-in block [:block/schema :value]))] {:label (if icon [:div.flex.flex-row.gap-2 (icon-component/icon icon) - name] - name) - :value id}))) (get-in schema [:enum-config :values])) + value] + value) + :value id}))) (:values schema)) (->> (model/get-block-property-values (:block/uuid property)) (mapcat (fn [[_id value]] (if (coll? value) @@ -364,7 +397,7 @@ :items items :selected-choices selected-choices :dropdown? dropdown? - :show-new-when-not-exact-match? (not (contains? #{:enum} type)) + :show-new-when-not-exact-match? (not closed-values?) :input-default-placeholder "Select" :extract-chosen-fn :value :input-opts (fn [_] @@ -382,7 +415,7 @@ (exit-edit-property) (when-let [f (:on-chosen opts)] (f))) nil))})} - enum? + closed-values? (assoc :extract-fn :label) multiple-choices? (assoc :on-apply on-chosen) @@ -436,24 +469,40 @@ invalid-warning))) (rum/defc select-item - [type value {:keys [page-cp inline-text]}] - (case type - (:page :date) - (when-let [page (db/entity [:block/uuid value])] - (page-cp {:disable-preview? true - :hide-close-button? true} page)) + [property type value {:keys [page-cp inline-text]}] + (let [closed-values? (seq (get-in property [:block/schema :values]))] + (cond + (contains? #{:page :date} type) + (when-let [page (db/entity [:block/uuid value])] + (page-cp {:disable-preview? true + :hide-close-button? true} page)) - :number - [:span.number (str value)] + closed-values? + (when-let [block (when value (db/entity [:block/uuid value]))] + (let [value' (get-in block [:block/schema :value]) + icon (pu/get-property block :icon)] + (cond + (:block/name block) + (page-cp {:disable-preview? true + :hide-close-button? true} block) - :enum - (when-let [block (when value (db/entity [:block/uuid value]))] - (let [name (:block/content block)] - (if-let [icon (pu/get-property block :icon)] - (icon-component/icon icon) - name))) + icon + (icon-component/icon icon) - (inline-text {} :markdown (str value)))) + (= type :number) + [:span.number (str value')] + + (= type :url) + (inline-text {} :markdown (str value')) + + :else + value'))) + + (= type :number) + [:span.number (str value)] + + :else + (inline-text {} :markdown (str value))))) (rum/defc single-value-select [block property value value-f select-opts {:keys [editing?] :as opts}] @@ -467,11 +516,11 @@ select-f (fn [] [:div.property-select (cond-> {} editing? (assoc :class "h-6")) (case type - (:number :url :date :enum) + (:number :url :date :default) (select block property select-opts' opts) :page - (select-page block property select-opts' opts))]) + (property-value-select-page block property select-opts' opts))]) dropdown-opts {:modal-class (util/hiccup->class "origin-top-right.absolute.left-0.rounded-md.shadow-lg.mt-2") :initial-open? editing?}] @@ -505,17 +554,19 @@ multiple-values? (= :many (:cardinality schema)) editor-id (or editor-id (str "ls-property-" (:db/id block) "-" (:db/id property))) editing? (or editing? (and @*ref (state/sub-editing? @*ref))) - select-type? (select-type? type) + select-type? (select-type? property type) + closed-values? (seq (:values schema)) select-opts {:on-chosen on-chosen}] - (if (and select-type? (not= type :date)) + (if (and select-type? + (not (and (not closed-values?) (= type :date)))) (single-value-select block property value (fn [] - (select-item type value opts)) + (select-item property type value opts)) select-opts opts) (case type :date - (date-picker block property value nil) + (property-value-date-picker block property value nil) :checkbox (let [add-property! (fn [] @@ -593,9 +644,9 @@ (if (seq items) (concat (for [item items] - (select-item type item opts)) + (select-item property type item opts)) (when date? - [(date-picker block property nil {:toggle-fn toggle-fn})])) + [(property-value-date-picker block property nil {:toggle-fn toggle-fn})])) (when-not editing? [:div.opacity-50.pointer.text-sm "Empty"]))) select-cp (fn [] (let [select-opts {:multiple-choices? true @@ -604,10 +655,10 @@ (when on-chosen (on-chosen)))}] [:div.property-select (cond-> {} editing? (assoc :class "h-6")) (if (= :page type) - (select-page block property - (assoc select-opts - :classes (:classes schema)) - opts) + (property-value-select-page block property + (assoc select-opts + :classes (:classes schema)) + opts) (select block property select-opts opts))]))] (if (and dropdown? (not editing?)) (ui/dropdown @@ -627,21 +678,23 @@ (rum/defc property-value < rum/reactive [block property v opts] - (let [dom-id (str "ls-property-" (:db/id block) "-" (:db/id property)) - editor-id (str dom-id "-editor") - schema (:block/schema property) - multiple-values? (= :many (:cardinality schema)) - editor-args {:block property - :parent-block block - :format :markdown}] - (cond - multiple-values? - (multiple-values block property v opts schema) + (ui/catch-error + (ui/block-error "Something wrong" {}) + (let [dom-id (str "ls-property-" (:db/id block) "-" (:db/id property)) + editor-id (str dom-id "-editor") + schema (:block/schema property) + multiple-values? (= :many (:cardinality schema)) + editor-args {:block property + :parent-block block + :format :markdown}] + (cond + multiple-values? + (multiple-values block property v opts schema) - :else - (property-scalar-value block property v - (merge - opts - {:editor-args editor-args - :editor-id editor-id - :dom-id dom-id}))))) + :else + (property-scalar-value block property v + (merge + opts + {:editor-args editor-args + :editor-id editor-id + :dom-id dom-id})))))) diff --git a/src/main/frontend/components/select.cljs b/src/main/frontend/components/select.cljs index c8d765e218..cfe3bad5f4 100644 --- a/src/main/frontend/components/select.cljs +++ b/src/main/frontend/components/select.cljs @@ -54,9 +54,6 @@ (atom (set (:selected-choices (first (:rum/args state))))))) :will-unmount (fn [state] (state/set-state! [:ui/open-select] nil) - (let [{:keys [multiple-choices? on-chosen]} (first (:rum/args state))] - (when (and multiple-choices? on-chosen) - (on-chosen @(::selected-choices state)))) state)} [state {:keys [items limit on-chosen empty-placeholder prompt-key input-default-placeholder close-modal? diff --git a/src/main/frontend/db/rtc/const.cljs b/src/main/frontend/db/rtc/const.cljs index e98985f271..a2a94f7887 100644 --- a/src/main/frontend/db/rtc/const.cljs +++ b/src/main/frontend/db/rtc/const.cljs @@ -19,7 +19,8 @@ (def general-attr-set (into #{} (map first) general-attrs-schema-coll)) -(def block-type-schema [:enum "property" "class" "whiteboard" "object" "hidden" "enum value"]) +(def block-type-schema [:enum "property" "class" "whiteboard" "object" "hidden" "closed value"]) + (def to-ws-op-schema [:multi {:dispatch first :decode/string #(update % 0 keyword)} [:move @@ -119,10 +120,6 @@ (def data-from-ws-coercer (m/coercer data-from-ws-schema mt/string-transformer)) (def data-from-ws-validator (m/validator data-from-ws-schema)) - - - - (def data-to-ws-schema (mu/closed-schema [:multi {:dispatch :action} diff --git a/src/main/frontend/handler/common/page.cljs b/src/main/frontend/handler/common/page.cljs index df0964193b..43f3d342ae 100644 --- a/src/main/frontend/handler/common/page.cljs +++ b/src/main/frontend/handler/common/page.cljs @@ -305,14 +305,14 @@ truncate-blocks-tx-data (mapv (fn [block] [:db.fn/retractEntity [:block/uuid (:block/uuid block)]]) - blocks)] - (if-let [msg (and (config/db-based-graph? repo) - (page-unable-to-delete repo page))] + blocks) + db-based? (config/db-based-graph? repo)] + (if-let [msg (and db-based? (page-unable-to-delete repo page))] (do (db/transact! repo truncate-blocks-tx-data - {:outliner-op :delete-page :persist-op? persist-op?}) + {:outliner-op :delete-page :persist-op? persist-op?}) (error-handler msg)) - (let [_ (delete-file! repo page-name delete-file?) + (let [_ (when-not db-based? (delete-file! repo page-name delete-file?)) ;; if other page alias this pagename, ;; then just remove some attrs of this entity instead of retractEntity delete-page-tx (cond diff --git a/src/main/frontend/handler/db_based/property.cljs b/src/main/frontend/handler/db_based/property.cljs index 5f4d32b840..3f3d2e3ff8 100644 --- a/src/main/frontend/handler/db_based/property.cljs +++ b/src/main/frontend/handler/db_based/property.cljs @@ -1,7 +1,6 @@ (ns frontend.handler.db-based.property "Properties handler for db graphs" - (:require [clojure.edn :as edn] - [clojure.string :as string] + (:require [clojure.string :as string] [frontend.db :as db] [frontend.db.model :as model] [frontend.handler.notification :as notification] @@ -30,12 +29,24 @@ [property-type property-val-schema])) db-property-type/builtin-schema-types))) +(defn- fail-parse-long + [v-str] + (let [result (parse-long v-str)] + (or result + (throw (js/Error. (str "Can't convert \"" v-str "\" to a number")))))) + +(defn- fail-parse-double + [v-str] + (let [result (parse-double v-str)] + (or result + (throw (js/Error. (str "Can't convert \"" v-str "\" to a number")))))) + (defn- infer-schema-from-input-string [v-str] (try (cond - (parse-long v-str) :number - (parse-double v-str) :number + (fail-parse-long v-str) :number + (fail-parse-double v-str) :number (util/uuid-string? v-str) :page (gp-util/url? v-str) :url (contains? #{"true" "false"} (string/lower-case v-str)) :checkbox @@ -48,19 +59,19 @@ (if (and (not (string? v-str)) (not (object? v-str))) v-str (case schema-type - :default - (if (util/uuid-string? v-str) (uuid v-str) v-str) - :number - (edn/read-string v-str) + (fail-parse-double v-str) :page (uuid v-str) ;; these types don't need to be translated. :date expects uuid and other ;; types usually expect text - (:enum :url :date :any) - v-str))) + (:url :date :any) + v-str + + ;; :default + (if (util/uuid-string? v-str) (uuid v-str) v-str)))) (defn upsert-property! [repo k-name schema {:keys [property-uuid]}] @@ -82,6 +93,14 @@ (assoc :block/schema schema)))] {:outliner-op :insert-blocks})))) +(defn- validate-property-value + [property schema value] + (let [values (get-in property [:block/schema :values])] + (if (seq values) + (when-not (contains? (set values) value) + "Value is not included in the closed values.") + (me/humanize (mu/explain-data schema value))))) + (defn- reset-block-property-multiple-values! [repo block-id k-name values _opts] (let [block (db/entity repo [:block/uuid block-id]) @@ -120,7 +139,7 @@ {:block/uuid block-id attribute property-value-ids}] {:outliner-op :save-block})) - (if-let [msg (some #(me/humanize (mu/explain-data schema %)) values')] + (if-let [msg (some #(validate-property-value property schema %) values')] (let [msg' (str "\"" k-name "\"" " " (if (coll? msg) (first msg) msg))] (notification/show! msg' :warning)) (do @@ -193,7 +212,7 @@ [[:db/add (:db/id block) attribute property-value-id]] {:outliner-op :save-block})) (when-not (contains? (if (set? value) value #{value}) v*) - (if-let [msg (me/humanize (mu/explain-data schema v*))] + (if-let [msg (validate-property-value property schema v*)] (let [msg' (str "\"" k-name "\"" " " (if (coll? msg) (first msg) msg))] (notification/show! msg' :warning)) (do diff --git a/src/main/frontend/handler/property.cljs b/src/main/frontend/handler/property.cljs index 553b410698..2252543591 100644 --- a/src/main/frontend/handler/property.cljs +++ b/src/main/frontend/handler/property.cljs @@ -4,6 +4,7 @@ [frontend.handler.file-based.property :as file-property-handler] [frontend.handler.file-based.page-property :as file-page-property] [frontend.handler.notification :as notification] + [logseq.db.frontend.property.type :as db-property-type] [frontend.config :as config] [frontend.util :as util] [frontend.state :as state] @@ -150,21 +151,20 @@ :all-classes all-classes ; block own classes + parent classes :classes-properties all-properties})) -(defn enum-other-position? +(defn closed-value-other-position? [property-id block-properties] (and (some? (get block-properties property-id)) (let [schema (:block/schema (db/entity [:block/uuid property-id]))] - (and (= :enum (:type schema)) - (= (:position schema) "block-beginning"))))) + (= (:position schema) "block-beginning")))) -(defn get-block-enum-other-position-properties +(defn get-block-other-position-properties [eid] (let [block (db/entity eid) own-properties (keys (:block/properties block))] (->> (:classes-properties (get-block-classes-properties eid)) (concat own-properties) - (filter (fn [id] (enum-other-position? id (:block/properties block)))) + (filter (fn [id] (closed-value-other-position? id (:block/properties block)))) (distinct)))) (defn block-has-viewable-properties? @@ -240,27 +240,50 @@ {:page page-tx :blocks [new-block]})) -(defn upsert-enum-item - [property {:keys [id name icon description]}] - (when (= :enum (get-in property [:block/schema :type])) - (let [icon-id (pu/get-pid "icon") - name (string/trim name) +(defn upsert-closed-value + "id should be a block UUID or nil" + [property {:keys [id value icon description]}] + (assert (or (nil? id) (uuid? id))) + (when (contains? db-property-type/closed-values-schema-types (get-in property [:block/schema :type] :default)) + (let [value (if (string? value) (string/trim value) value) property-schema (:block/schema property) - enum-values (get-in property-schema [:enum-config :values]) - block-values (map (fn [id] (db/entity [:block/uuid id])) enum-values) - icon (when-not (and (string? icon) (string/blank? icon)) icon) - description (string/trim description) - description (when-not (string/blank? description) description)] - (if (some (fn [b] (and (= name (:block/content b)) - (not= id (:block/uuid b)))) block-values) - (notification/show! "Choice exists already." :warning) - (let [block (when id (db/entity [:block/uuid id])) - block-id (or id (db/new-block-id)) + closed-values (:values property-schema) + block-values (map (fn [id] (db/entity [:block/uuid id])) closed-values) + resolved-value (try + (db-property-handler/convert-property-input-string (:type property-schema) value) + (catch :default e + (js/console.error e) + (notification/show! (str e) :error false) + nil)) + block (when id (db/entity [:block/uuid id])) + value-block (when (uuid? value) (db/entity [:block/uuid value]))] + (cond + (nil? resolved-value) + nil + + (some (fn [b] (and (= resolved-value (get-in b [:block/metadata :value])) + (not= id (:block/uuid b)))) block-values) + (do + (notification/show! "Choice already exists" :warning) + :value-exists) + + (:block/name value-block) ; page + (let [new-values (vec (conj closed-values value))] + {:block-id value + :tx-data [{:db/id (:db/id property) + :block/schema (assoc property-schema :values new-values)}]}) + + :else + (let [block-id (or id (db/new-block-id)) + icon-id (pu/get-pid "icon") + icon (when-not (and (string? icon) (string/blank? icon)) icon) + description (string/trim description) + description (when-not (string/blank? description) description) tx-data (if block [(let [properties (:block/properties block) - schema (:block/schema block)] + schema (assoc (:block/schema block) + :value resolved-value)] {:block/uuid id - :block/content name :block/properties (if icon (assoc properties icon-id icon) (dissoc properties icon-id)) @@ -277,12 +300,11 @@ page-id [:block/uuid (:block/uuid page)] metadata {:created-from-property (:block/uuid property)} new-block (cond-> - {:block/type #{"enum value"} + {:block/type #{"closed value"} :block/uuid block-id - :block/format :markdown - :block/content name :block/page page-id :block/metadata metadata + :block/schema {:value resolved-value} :block/parent page-id :block/left (or (when page-entity (model/get-block-last-direct-child (db/get-db) (:db/id page-entity))) page-id)} @@ -290,28 +312,28 @@ (assoc :block/properties {icon-id icon}) description - (assoc :block/schema {:description description}) + (update :block/schema assoc :description description) true outliner-core/block-with-timestamps) - new-values (vec (conj enum-values block-id))] + new-values (vec (conj closed-values block-id))] (->> (cons page-tx [new-block {:db/id (:db/id property) - :block/schema (assoc property-schema :enum-config {:values new-values})}]) + :block/schema (assoc property-schema :values new-values)}]) (remove nil?))))] {:block-id block-id :tx-data tx-data}))))) -(defn delete-enum-item +(defn delete-closed-value [property item] (if (seq (:block/_refs item)) (notification/show! "The choice can't be deleted because it's still used." :warning) (let [schema (:block/schema property) tx-data [[:db/retractEntity (:db/id item)] {:db/id (:db/id property) - :block/schema (update-in schema [:enum-config :values] - (fn [values] - (vec (remove #{(:block/uuid item)} values))))}]] + :block/schema (update schema :values + (fn [values] + (vec (remove #{(:block/uuid item)} values))))}]] (db/transact! tx-data)))) (defn get-property-block-created-block diff --git a/src/main/frontend/handler/property/util.cljs b/src/main/frontend/handler/property/util.cljs index e9fdd9cd5e..3e171a9059 100644 --- a/src/main/frontend/handler/property/util.cljs +++ b/src/main/frontend/handler/property/util.cljs @@ -87,7 +87,9 @@ [(-> prop-ent :block/name keyword) - (if (= :enum (get-in prop-ent [:block/schema :type])) - (:block/content (db/entity [:block/uuid v])) + (if (seq (get-in prop-ent [:block/schema :values])) ; closed values + (when-let [block (db/entity [:block/uuid v])] + (or (:block/original-name block) + (get-in block [:block/schema :value]))) v)]))) (into {}))) diff --git a/src/main/frontend/ui.cljs b/src/main/frontend/ui.cljs index 8b988f5563..00ce65d091 100644 --- a/src/main/frontend/ui.cljs +++ b/src/main/frontend/ui.cljs @@ -140,7 +140,7 @@ [dropdown-state _close-fn content class style-opts] (let [class (or class (util/hiccup->class "origin-top-right.absolute.right-0.mt-2"))] - [:div.dropdown-wrapper + [:div.dropdown-wrapper.max-h-screen.overflow-y-auto {:style style-opts :class (str class " " (case dropdown-state