|
@@ -12,38 +12,41 @@
|
|
|
[frontend.handler.property.util :as pu]
|
|
|
[frontend.handler.db-based.property.util :as db-pu]
|
|
|
[frontend.ui :as ui]
|
|
|
- [frontend.util :as util]
|
|
|
[frontend.state :as state]
|
|
|
- [rum.core :as rum]))
|
|
|
+ [rum.core :as rum]
|
|
|
+ [logseq.shui.ui :as shui-ui]
|
|
|
+ [frontend.util :as util]
|
|
|
+ [clojure.set :as set]
|
|
|
+ [clojure.string :as string]))
|
|
|
|
|
|
(rum/defc page-properties < rum/reactive
|
|
|
- [page {:keys [configure? show-page-properties?]}]
|
|
|
+ [page {:keys [configure? mode]}]
|
|
|
(let [types (:block/type page)
|
|
|
class? (contains? types "class")
|
|
|
+ property? (contains? types "property")
|
|
|
edit-input-id-prefix (str "edit-block-" (:block/uuid page))
|
|
|
configure-opts {:selected? false
|
|
|
:page-configure? true}
|
|
|
has-viewable-properties? (db-property-handler/block-has-viewable-properties? page)
|
|
|
has-class-properties? (seq (:properties (:block/schema page)))]
|
|
|
- (when (or configure? has-viewable-properties? has-class-properties?)
|
|
|
- [:div.ls-page-properties.mb-4 {:style {:padding 2}}
|
|
|
+ (when (or configure? has-viewable-properties? has-class-properties? property?)
|
|
|
+ [:div.ls-page-properties.mb-4
|
|
|
(if configure?
|
|
|
(cond
|
|
|
- (and class? (not show-page-properties?) (not has-class-properties?))
|
|
|
- [:div
|
|
|
- [:div.mb-1 "Class properties:"]
|
|
|
- (component-block/db-properties-cp {:editor-box editor/box}
|
|
|
- page
|
|
|
- (str edit-input-id-prefix "-schema")
|
|
|
- (assoc configure-opts :class-schema? true))]
|
|
|
+ (and class? has-class-properties? (= :class mode))
|
|
|
+ nil
|
|
|
+
|
|
|
+ (and class? (not has-class-properties?))
|
|
|
+ (component-block/db-properties-cp {:editor-box editor/box}
|
|
|
+ page
|
|
|
+ (str edit-input-id-prefix "-schema")
|
|
|
+ (assoc configure-opts :class-schema? true))
|
|
|
|
|
|
(not (db-property-handler/block-has-viewable-properties? page))
|
|
|
- [:div
|
|
|
- [:div.mb-1 "Page properties:"]
|
|
|
- (component-block/db-properties-cp {:editor-box editor/box}
|
|
|
- page
|
|
|
- (str edit-input-id-prefix "-page")
|
|
|
- (assoc configure-opts :class-schema? false))])
|
|
|
+ (component-block/db-properties-cp {:editor-box editor/box}
|
|
|
+ page
|
|
|
+ (str edit-input-id-prefix "-page")
|
|
|
+ (assoc configure-opts :class-schema? false)))
|
|
|
(if config/publishing?
|
|
|
[:div.flex.flex-col.gap-4
|
|
|
(when has-viewable-properties?
|
|
@@ -88,7 +91,7 @@
|
|
|
|
|
|
(rum/defc icon-row < rum/reactive
|
|
|
[page]
|
|
|
- [:div.grid.grid-cols-5.gap-1.items-center.leading-8
|
|
|
+ [:div.grid.grid-cols-5.gap-1.items-center
|
|
|
[:label.col-span-2 "Icon:"]
|
|
|
(let [icon-value (pu/get-block-property-value page :icon)]
|
|
|
[:div.col-span-3.flex.flex-row.items-center.gap-2
|
|
@@ -109,85 +112,43 @@
|
|
|
:title "Delete this icon"}
|
|
|
(ui/icon "X")])])])
|
|
|
|
|
|
-(rum/defcs page-configure-inner <
|
|
|
- (rum/local false ::show-page-properties?)
|
|
|
- {:will-unmount (fn [state]
|
|
|
- (let [on-unmount (nth (:rum/args state) 1)]
|
|
|
- (on-unmount)))}
|
|
|
- [state page _on-unmount opts]
|
|
|
- (let [*show-page-properties? (::show-page-properties? state)
|
|
|
+(rum/defc tags
|
|
|
+ [page]
|
|
|
+ (let [tags-property (pu/get-property :tags)]
|
|
|
+ (pv/property-value page tags-property
|
|
|
+ (map :block/uuid (:block/tags page))
|
|
|
+ {:page-cp (fn [config page]
|
|
|
+ (component-block/page-cp (assoc config :tag? true) page))})))
|
|
|
+
|
|
|
+(rum/defc tags-row < rum/reactive
|
|
|
+ [page]
|
|
|
+ [:div.grid.grid-cols-5.gap-1.items-center
|
|
|
+ [:label.col-span-2 "Tags:"]
|
|
|
+ [:div.col-span-3.flex.flex-row.items-center.gap-2
|
|
|
+ (tags page)]])
|
|
|
+
|
|
|
+(rum/defcs page-configure < rum/reactive
|
|
|
+ [state page *mode]
|
|
|
+ (let [*mode *mode
|
|
|
+ mode (rum/react *mode)
|
|
|
types (:block/type page)
|
|
|
class? (contains? types "class")
|
|
|
property? (contains? types "property")
|
|
|
- class-or-property? (or class? property?)
|
|
|
- page-opts {:configure? true
|
|
|
- :show-page-properties? @*show-page-properties?}]
|
|
|
- [:div.flex.flex-col.justify-between.p-4 {:style {:min-width 700
|
|
|
- :min-height 400}}
|
|
|
- [:div.flex.flex-col.gap-2
|
|
|
- (cond
|
|
|
- (not class-or-property?)
|
|
|
- (when (and (not class?)
|
|
|
- (not property?))
|
|
|
- [:<>
|
|
|
- (icon-row page)
|
|
|
- (page-properties page page-opts)])
|
|
|
-
|
|
|
- @*show-page-properties?
|
|
|
- (page-properties page page-opts)
|
|
|
-
|
|
|
- :else
|
|
|
- [:<>
|
|
|
- (when class?
|
|
|
- (class-component/configure page))
|
|
|
- (when class?
|
|
|
- (icon-row page))
|
|
|
- (when class?
|
|
|
- (page-properties page page-opts))
|
|
|
- (when (and property? (not class?))
|
|
|
- [:h2.title "Configure property"])
|
|
|
- (when property?
|
|
|
- (property-component/property-config page page (assoc opts
|
|
|
- :inline-text component-block/inline-text)))])]
|
|
|
-
|
|
|
- (when (and class-or-property?
|
|
|
- (not (db-property-handler/block-has-viewable-properties? page))
|
|
|
- (not config/publishing?)
|
|
|
- (empty? (:properties (:block/schema page))))
|
|
|
- [:a.fade-link.flex.flex-row.items-center.gap-1.text-sm
|
|
|
- {:on-click #(swap! *show-page-properties? not)}
|
|
|
- (ui/icon (if @*show-page-properties?
|
|
|
- "arrow-narrow-left"
|
|
|
- "arrow-narrow-right"))
|
|
|
- (if @*show-page-properties?
|
|
|
- "Back"
|
|
|
- "Edit page properties")])]))
|
|
|
-
|
|
|
-(rum/defc page-configure
|
|
|
- [page *hover? *configuring?]
|
|
|
- (when (or @*hover? (and config/publishing? (some #{"class" "property"} (:block/type page))))
|
|
|
- (let [toggle-fn' (fn [toggle-fn]
|
|
|
- (fn []
|
|
|
- (toggle-fn)
|
|
|
- (reset! *configuring? true)))]
|
|
|
- (ui/dropdown
|
|
|
- (fn [{:keys [toggle-fn]}]
|
|
|
- [:a.fade-link.flex.flex-row.items-center
|
|
|
- {:on-click (toggle-fn' toggle-fn)}
|
|
|
- [:div.mr-1.text-sm (if-let [block-type (and config/publishing?
|
|
|
- (some #{"class" "property"} (:block/type page)))]
|
|
|
- (str "More info on this " block-type)
|
|
|
- "Configure")]])
|
|
|
- (fn [{:keys [toggle-fn]}]
|
|
|
- (page-configure-inner
|
|
|
- page
|
|
|
- (fn []
|
|
|
- (reset! *configuring? false)
|
|
|
- (reset! *hover? false))
|
|
|
- {:toggle-fn toggle-fn}))
|
|
|
-
|
|
|
- {:modal-class (util/hiccup->class
|
|
|
- "origin-top-right.absolute.left-0.mt-2.rounded-md.shadow-lg")}))))
|
|
|
+ page-opts {:configure? true}]
|
|
|
+ (when (nil? mode)
|
|
|
+ (reset! *mode (cond
|
|
|
+ property? :property
|
|
|
+ class? :class
|
|
|
+ :else :page)))
|
|
|
+ [:div.flex.flex-col.gap-1
|
|
|
+ (if (= mode :property)
|
|
|
+ (property-component/property-config page page {:inline-text component-block/inline-text})
|
|
|
+ [:<>
|
|
|
+ (when (= mode :class)
|
|
|
+ (class-component/configure page {:show-title? false}))
|
|
|
+ (when-not config/publishing? (tags-row page))
|
|
|
+ (when-not config/publishing? (icon-row page))
|
|
|
+ (page-properties page (assoc page-opts :mode mode))])]))
|
|
|
|
|
|
(rum/defc page-properties-react < rum/reactive
|
|
|
[page* page-opts]
|
|
@@ -197,23 +158,96 @@
|
|
|
(some #{"class" "property"} (:block/type page)))
|
|
|
(page-properties page page-opts))))
|
|
|
|
|
|
-(rum/defc page-tags <
|
|
|
- [page tags-property *hover? *configuring?]
|
|
|
- (let [toggle-fn' (fn [toggle-fn]
|
|
|
- (fn []
|
|
|
- (toggle-fn)
|
|
|
- (swap! *configuring? not)))]
|
|
|
- (ui/dropdown
|
|
|
- (fn [{:keys [toggle-fn]}]
|
|
|
- [:a.fade-link.flex.flex-row.items-center
|
|
|
- {:on-click (toggle-fn' toggle-fn)}
|
|
|
- [:div.ml-1.text-sm "Set tags"]])
|
|
|
- (fn [{:keys [toggle-fn]}]
|
|
|
- (pv/property-value page tags-property nil {:on-chosen (toggle-fn' toggle-fn)
|
|
|
- :dropdown? false}))
|
|
|
- {:modal-class (util/hiccup->class
|
|
|
- "origin-top-right.absolute.left-0.mt-2.rounded-md.shadow-lg")
|
|
|
- :on-toggle (fn [value]
|
|
|
- (when (false? value)
|
|
|
- (reset! *configuring? false)
|
|
|
- (reset! *hover? false)))})))
|
|
|
+(rum/defc mode-switch < rum/reactive
|
|
|
+ [types *mode]
|
|
|
+ (let [current-mode (rum/react *mode)
|
|
|
+ property? (contains? types "property")
|
|
|
+ class? (contains? types "class")
|
|
|
+ modes (->
|
|
|
+ (cond
|
|
|
+ (and property? class?)
|
|
|
+ ["Property" "Class"]
|
|
|
+ property?
|
|
|
+ ["Property"]
|
|
|
+ class?
|
|
|
+ ["Class"]
|
|
|
+ :else
|
|
|
+ [])
|
|
|
+ (conj "Page"))]
|
|
|
+ [:div.flex.flex-row.items-center.gap-1
|
|
|
+ (for [mode modes]
|
|
|
+ (let [mode' (keyword (string/lower-case mode))
|
|
|
+ selected? (= mode' current-mode)]
|
|
|
+ (shui-ui/button {:variant (if selected? :outline :ghost) :size :sm
|
|
|
+ :on-click (if config/publishing?
|
|
|
+ util/stop-propagation
|
|
|
+ (fn [e]
|
|
|
+ (util/stop-propagation e)
|
|
|
+ (reset! *mode mode')))}
|
|
|
+ mode)))]))
|
|
|
+
|
|
|
+(rum/defcs page-info < rum/reactive
|
|
|
+ (rum/local false ::hover?)
|
|
|
+ (rum/local nil ::mode)
|
|
|
+ {:init (if config/publishing?
|
|
|
+ (fn [state]
|
|
|
+ (let [page* (first (:rum/args state))
|
|
|
+ page (db/sub-block (:db/id page*))]
|
|
|
+ (assoc state
|
|
|
+ ::collapsed?
|
|
|
+ (atom (not (seq (set/intersection #{"class" "property"} (:block/type page))))))))
|
|
|
+ (fn [state]
|
|
|
+ (let [page (first (:rum/args state))
|
|
|
+ properties (:block/properties page)]
|
|
|
+ (assoc state ::collapsed? (atom (empty? properties))))))}
|
|
|
+ [state page *hover-title?]
|
|
|
+ (let [page (db/sub-block (:db/id page))
|
|
|
+ *collapsed? (::collapsed? state)
|
|
|
+ *hover? (::hover? state)
|
|
|
+ *mode (::mode state)
|
|
|
+ types (:block/type page)
|
|
|
+ hover-title? (rum/react *hover-title?)
|
|
|
+ collapsed? (rum/react *collapsed?)
|
|
|
+ has-tags? (seq (:block/tags page))
|
|
|
+ hover-or-expanded? (or @*hover? hover-title? (not collapsed?))]
|
|
|
+ (when (if config/publishing?
|
|
|
+ ;; Since publishing is read-only, hide this component if it has no info to show
|
|
|
+ ;; as it creates a fair amount of empty vertical space
|
|
|
+ (or has-tags? (some? types))
|
|
|
+ true)
|
|
|
+ [:div.page-info {:on-mouse-over #(reset! *hover? true)
|
|
|
+ :on-mouse-leave #(reset! *hover? false)}
|
|
|
+ (when (or hover-or-expanded? has-tags?)
|
|
|
+ [:div.fade-in.p-2 (cond-> {}
|
|
|
+ (or @*hover? (not collapsed?))
|
|
|
+ (assoc :class "border rounded"))
|
|
|
+ [:div.info-title.cursor {:on-click
|
|
|
+ (if config/publishing?
|
|
|
+ (fn [_]
|
|
|
+ (when (seq (set/intersection #{"class" "empty"} types))
|
|
|
+ (swap! *collapsed? not)))
|
|
|
+ #(swap! *collapsed? not))}
|
|
|
+ [:div.flex.flex-row.items-center.gap-2.justify-between
|
|
|
+ [:div.flex.flex-row.items-center.gap-2
|
|
|
+ (if collapsed?
|
|
|
+ [:<>
|
|
|
+ (shui-ui/button {:variant :ghost :size :sm :class "fade-link"}
|
|
|
+ (ui/icon "tags"))
|
|
|
+ [:div {:on-click util/stop-propagation}
|
|
|
+ (tags page)]]
|
|
|
+ [:div.flex.flex-row.items-center.gap-1
|
|
|
+ (shui-ui/button {:variant :ghost :size :sm :class "fade-link"}
|
|
|
+ (ui/icon "info-circle"))
|
|
|
+ [:a.text-sm.font-medium.fade-link
|
|
|
+ "Configure:"]
|
|
|
+ (mode-switch types *mode)])]
|
|
|
+ (when (or @*hover? (not collapsed?))
|
|
|
+ (shui-ui/button
|
|
|
+ {:variant :ghost :size :sm :class "fade-link"}
|
|
|
+ (ui/icon (if collapsed?
|
|
|
+ "chevron-down"
|
|
|
+ "chevron-up"))))]]
|
|
|
+
|
|
|
+ (when-not collapsed?
|
|
|
+ [:div.py-2.px-4
|
|
|
+ (page-configure page *mode)])])])))
|