|
|
@@ -21,6 +21,7 @@
|
|
|
[frontend.db :as db]
|
|
|
[frontend.db-mixins :as db-mixins]
|
|
|
[frontend.db.async :as db-async]
|
|
|
+ [frontend.db.react :as react]
|
|
|
[frontend.handler.db-based.export :as db-export-handler]
|
|
|
[frontend.handler.db-based.property :as db-property-handler]
|
|
|
[frontend.handler.editor :as editor-handler]
|
|
|
@@ -46,6 +47,12 @@
|
|
|
[promesa.core :as p]
|
|
|
[rum.core :as rum]))
|
|
|
|
|
|
+(defn- get-scroll-parent
|
|
|
+ [config]
|
|
|
+ (if (:sidebar? config)
|
|
|
+ (dom/sel1 ".sidebar-item-list")
|
|
|
+ (gdom/getElement "main-content-container")))
|
|
|
+
|
|
|
(rum/defc header-checkbox < rum/static
|
|
|
[{:keys [selected-all? selected-some? toggle-selected-all!] :as table}]
|
|
|
(let [[show? set-show!] (rum/use-state false)]
|
|
|
@@ -190,16 +197,27 @@
|
|
|
|
|
|
(rum/defc block-title
|
|
|
"Used on table view"
|
|
|
- [block {:keys [create-new-block width]}]
|
|
|
- (let [inline-title (state/get-component :block/inline-title)
|
|
|
+ [block* {:keys [create-new-block width row property]}]
|
|
|
+ (let [*ref (hooks/use-ref nil)
|
|
|
[opacity set-opacity!] (hooks/use-state 0)
|
|
|
- add-to-sidebar! #(state/sidebar-add-block! (state/get-current-repo) (:db/id block) :block)]
|
|
|
+ [focus-timeout set-focus-timeout!] (hooks/use-state nil)
|
|
|
+ inline-title (state/get-component :block/inline-title)
|
|
|
+ many? (db-property/many? property)
|
|
|
+ block (if many? (first block*) block*)
|
|
|
+ add-to-sidebar! #(state/sidebar-add-block! (state/get-current-repo)
|
|
|
+ (or (and many? (:db/id row)) (:db/id block))
|
|
|
+ :block)
|
|
|
+ redirect! #(some-> (:block/uuid block) route-handler/redirect-to-page!)]
|
|
|
+ (hooks/use-effect!
|
|
|
+ (fn []
|
|
|
+ #(some-> focus-timeout js/clearTimeout))
|
|
|
+ [])
|
|
|
[:div.table-block-title.relative.flex.items-center.w-full.h-full.cursor-pointer.items-center
|
|
|
- {:on-mouse-over #(set-opacity! 100)
|
|
|
+ {:ref *ref
|
|
|
+ :on-mouse-over #(set-opacity! 100)
|
|
|
:on-mouse-out #(set-opacity! 0)
|
|
|
:on-click (fn [e]
|
|
|
- (p/let [block (or block (and (fn? create-new-block) (create-new-block)))
|
|
|
- redirect! #(some-> (:block/uuid block) route-handler/redirect-to-page!)]
|
|
|
+ (p/let [block (or block (and (fn? create-new-block) (create-new-block)))]
|
|
|
(when block
|
|
|
(cond
|
|
|
(util/meta-key? e)
|
|
|
@@ -209,51 +227,68 @@
|
|
|
(add-to-sidebar!)
|
|
|
|
|
|
:else
|
|
|
- (p/do!
|
|
|
- (shui/popup-show!
|
|
|
- (.closest (.-target e) ".ls-table-cell")
|
|
|
- (fn []
|
|
|
- (let [width (-> (max 160 width)
|
|
|
- (- 18))]
|
|
|
- [:div.ls-table-block.flex.flex-row.items-start
|
|
|
- {:style {:width width :max-width width :margin-right "6px"}
|
|
|
- :on-click util/stop-propagation}
|
|
|
- (block-container {:popup? true
|
|
|
- :view? true
|
|
|
- :table-block-title? true} block)
|
|
|
- (shui/button
|
|
|
- {:variant :ghost
|
|
|
- :title "Open node"
|
|
|
- :on-click (fn [e]
|
|
|
- (util/stop-propagation e)
|
|
|
- (shui/popup-hide!)
|
|
|
- (redirect!))
|
|
|
- :class (str "h-6 w-6 !p-0 text-muted-foreground transition-opacity duration-100 ease-in bg-gray-01 "
|
|
|
- "opacity-" opacity)}
|
|
|
- (ui/icon "arrow-right"))]))
|
|
|
- {:id :ls-table-block-editor
|
|
|
- :as-mask? true})
|
|
|
- (editor-handler/edit-block! block :max {:container-id :unknown-container}))))))}
|
|
|
+ (let [popup (fn []
|
|
|
+ (let [width (-> (max 160 width) (- 18))]
|
|
|
+ (if many?
|
|
|
+ [:div.ls-table-block.flex.flex-row.items-start
|
|
|
+ {:style {:width width :max-width width :margin-right "6px"}
|
|
|
+ :on-click util/stop-propagation}
|
|
|
+ (pv/property-value row property {})]
|
|
|
+ [:div.ls-table-block.flex.flex-row.items-start
|
|
|
+ {:style {:width width :max-width width :margin-right "6px"}
|
|
|
+ :on-click util/stop-propagation}
|
|
|
+ (block-container {:popup? true
|
|
|
+ :view? true
|
|
|
+ :table-block-title? true} block)])))]
|
|
|
+ (p/do!
|
|
|
+ (shui/popup-show!
|
|
|
+ (.closest (.-target e) ".ls-table-cell")
|
|
|
+ popup
|
|
|
+ {:id :ls-table-block-editor
|
|
|
+ :as-mask? true
|
|
|
+ :on-after-hide (fn []
|
|
|
+ (let [node (rum/deref *ref)
|
|
|
+ cell (util/rec-get-node node "ls-table-cell")]
|
|
|
+ (p/do!
|
|
|
+ (editor-handler/save-current-block!)
|
|
|
+ (state/exit-editing-and-set-selected-blocks! [cell])
|
|
|
+ (set-focus-timeout! (js/setTimeout #(.focus cell) 100)))))})
|
|
|
+ (editor-handler/edit-block! block :max {:container-id :unknown-container})))))))}
|
|
|
(if block
|
|
|
- [:div
|
|
|
- (inline-title
|
|
|
- (some->> (:block/title block)
|
|
|
- string/trim
|
|
|
- string/split-lines
|
|
|
- first))]
|
|
|
+ [:div.flex.flex-row
|
|
|
+ (let [render (fn [block]
|
|
|
+ [:div
|
|
|
+ (inline-title
|
|
|
+ (some->> (:block/title block)
|
|
|
+ string/trim
|
|
|
+ string/split-lines
|
|
|
+ first))])]
|
|
|
+ (if many?
|
|
|
+ (->> (map render block*)
|
|
|
+ (interpose [:div.mr-1 ","]))
|
|
|
+ (render block*)))]
|
|
|
[:div])
|
|
|
|
|
|
- [:div.absolute.right-0.p-1
|
|
|
- {:on-click (fn [e]
|
|
|
- (util/stop-propagation e)
|
|
|
- (add-to-sidebar!))}
|
|
|
- [:div.flex.items-center
|
|
|
- (shui/button
|
|
|
- {:variant :ghost
|
|
|
- :title "Open in sidebar"
|
|
|
- :class (str "h-5 w-5 !p-0 text-muted-foreground transition-opacity duration-100 ease-in bg-gray-01 "
|
|
|
- "opacity-" opacity)}
|
|
|
- (ui/icon "layout-sidebar-right"))]]]))
|
|
|
+ (let [class (str "h-6 w-6 !p-1 text-muted-foreground transition-opacity duration-100 ease-in bg-gray-01 "
|
|
|
+ "opacity-" opacity)]
|
|
|
+ [:div.absolute.-right-1
|
|
|
+ [:div.flex.flex-row.items-center
|
|
|
+ (shui/button
|
|
|
+ {:variant :ghost
|
|
|
+ :title "Open"
|
|
|
+ :on-click (fn [e]
|
|
|
+ (util/stop-propagation e)
|
|
|
+ (redirect!))
|
|
|
+ :class class}
|
|
|
+ (ui/icon "arrow-right"))
|
|
|
+ (shui/button
|
|
|
+ {:variant :ghost
|
|
|
+ :title "Open in sidebar"
|
|
|
+ :class class
|
|
|
+ :on-click (fn [e]
|
|
|
+ (util/stop-propagation e)
|
|
|
+ (add-to-sidebar!))}
|
|
|
+ (ui/icon "layout-sidebar-right"))]])]))
|
|
|
|
|
|
(defn build-columns
|
|
|
[config properties & {:keys [with-object-name? with-id? add-tags-column?]
|
|
|
@@ -263,30 +298,30 @@
|
|
|
(let [;; FIXME: Shouldn't file graphs have :block/tags?
|
|
|
add-tags-column?' (and (config/db-based-graph? (state/get-current-repo)) add-tags-column?)
|
|
|
properties' (->>
|
|
|
- (if (or (some #(= (:db/ident %) :block/tags) properties) (not add-tags-column?'))
|
|
|
- properties
|
|
|
- (conj properties (db/entity :block/tags)))
|
|
|
- (remove nil?))]
|
|
|
+ (if (or (some #(= (:db/ident %) :block/tags) properties) (not add-tags-column?'))
|
|
|
+ properties
|
|
|
+ (conj properties (db/entity :block/tags)))
|
|
|
+ (remove nil?))]
|
|
|
(->> (concat
|
|
|
- [{:id :select
|
|
|
- :name "Select"
|
|
|
- :header (fn [table _column] (header-checkbox table))
|
|
|
- :cell (fn [table row column]
|
|
|
- (row-checkbox table row column))
|
|
|
- :column-list? false
|
|
|
- :resizable? false}
|
|
|
- (when with-id?
|
|
|
- {:id :id
|
|
|
- :name "ID"
|
|
|
- :header (fn [_table _column] (header-index))
|
|
|
- :cell (fn [table row _column]
|
|
|
- (inc (.indexOf (:rows table) (:db/id row))))
|
|
|
- :resizable? false})
|
|
|
- (when with-object-name?
|
|
|
- {:id :block/title
|
|
|
- :name "Name"
|
|
|
- :type :string
|
|
|
- :header header-cp
|
|
|
+ [{:id :select
|
|
|
+ :name "Select"
|
|
|
+ :header (fn [table _column] (header-checkbox table))
|
|
|
+ :cell (fn [table row column]
|
|
|
+ (row-checkbox table row column))
|
|
|
+ :column-list? false
|
|
|
+ :resizable? false}
|
|
|
+ (when with-id?
|
|
|
+ {:id :id
|
|
|
+ :name "ID"
|
|
|
+ :header (fn [_table _column] (header-index))
|
|
|
+ :cell (fn [table row _column]
|
|
|
+ (inc (.indexOf (:rows table) (:db/id row))))
|
|
|
+ :resizable? false})
|
|
|
+ (when with-object-name?
|
|
|
+ {:id :block/title
|
|
|
+ :name "Name"
|
|
|
+ :type :string
|
|
|
+ :header header-cp
|
|
|
:cell (fn [_table row _column style]
|
|
|
(block-title row {:property-ident :block/title
|
|
|
:sidebar? (:sidebar? config)
|
|
|
@@ -320,6 +355,8 @@
|
|
|
:table-text-property-render
|
|
|
(fn [block opts]
|
|
|
(block-title block (assoc opts
|
|
|
+ :row row
|
|
|
+ :property property
|
|
|
:width (:width style)
|
|
|
:sidebar? (:sidebar? config))))}))))
|
|
|
:get-value get-value
|
|
|
@@ -356,16 +393,18 @@
|
|
|
[view-entity columns {:keys [column-visible? rows column-toggle-visibility]}]
|
|
|
(let [display-type (:db/ident (:logseq.property.view/type view-entity))
|
|
|
table? (= display-type :logseq.property.view/type.table)
|
|
|
- group-by-columns (concat (filter (fn [column]
|
|
|
+ group-by-columns (concat (when (or
|
|
|
+ (contains? #{:linked-references :unlinked-references}
|
|
|
+ (:logseq.property.view/feature-type view-entity))
|
|
|
+ (:logseq.property/query view-entity))
|
|
|
+ [{:id :block/page
|
|
|
+ :name "Block Page"}])
|
|
|
+ (filter (fn [column]
|
|
|
(when (:id column)
|
|
|
(when-let [p (db/entity (:id column))]
|
|
|
(and (not (db-property/many? p))
|
|
|
(contains? #{:default :number :checkbox :url :node :date}
|
|
|
- (:logseq.property/type p)))))) columns)
|
|
|
- (when (contains? #{:linked-references :unlinked-references}
|
|
|
- (:logseq.property.view/feature-type view-entity))
|
|
|
- [{:id :block/page
|
|
|
- :name "Block Page"}]))]
|
|
|
+ (:logseq.property/type p)))))) columns))]
|
|
|
(shui/dropdown-menu
|
|
|
(shui/dropdown-menu-trigger
|
|
|
{:asChild true}
|
|
|
@@ -653,14 +692,109 @@
|
|
|
[cell-render-f cell-placeholder]
|
|
|
(let [^js state (ui/useInView #js {:rootMargin "0px"})
|
|
|
in-view? (.-inView state)]
|
|
|
- [:div.h-full {:ref (.-ref state)}
|
|
|
+ [:div.h-full
|
|
|
+ {:ref (.-ref state)}
|
|
|
(if in-view?
|
|
|
(cell-render-f)
|
|
|
cell-placeholder)]))
|
|
|
|
|
|
+(defn- click-cell
|
|
|
+ [node]
|
|
|
+ (when-let [trigger (or (dom/sel1 node ".jtrigger")
|
|
|
+ (dom/sel1 node ".table-block-title"))]
|
|
|
+ (.click trigger)))
|
|
|
+
|
|
|
+(defn navigate-to-cell
|
|
|
+ [e cell direction]
|
|
|
+ (util/stop e)
|
|
|
+ (let [row (util/rec-get-node cell "ls-table-row")
|
|
|
+ cells (dom/sel row ".ls-table-cell")
|
|
|
+ idx (.indexOf cells cell)
|
|
|
+ rows-container (util/rec-get-node row "ls-table-rows")
|
|
|
+ rows (dom/sel rows-container ".ls-table-row")
|
|
|
+ row-idx (.indexOf rows row)
|
|
|
+ container-left (.-left (.getBoundingClientRect rows-container))
|
|
|
+ next-cell (case direction
|
|
|
+ :left (if (> idx 1) ; don't focus on checkbox
|
|
|
+ (nth cells (dec idx))
|
|
|
+ ;; last cell in the prev row
|
|
|
+ (let [prev-row (when (> row-idx 0)
|
|
|
+ (nth rows (dec row-idx)))]
|
|
|
+ (when prev-row
|
|
|
+ (let [cells (dom/sel prev-row ".ls-table-cell")]
|
|
|
+ (last cells)))))
|
|
|
+ :right (if (< idx (dec (count cells)))
|
|
|
+ (nth cells (inc idx))
|
|
|
+ ;; first cell in the next row
|
|
|
+ (let [next-row (when (< row-idx (dec (count rows)))
|
|
|
+ (nth rows (inc row-idx)))]
|
|
|
+ (when next-row
|
|
|
+ (let [cells (dom/sel next-row ".ls-table-cell")]
|
|
|
+ (second cells)))))
|
|
|
+ :up (let [prev-row (when (> row-idx 0)
|
|
|
+ (nth rows (dec row-idx)))]
|
|
|
+ (when prev-row
|
|
|
+ (let [cells (dom/sel prev-row ".ls-table-cell")]
|
|
|
+ (nth cells idx))))
|
|
|
+ :down (let [next-row (when (< row-idx (dec (count rows)))
|
|
|
+ (nth rows (inc row-idx)))]
|
|
|
+ (when next-row
|
|
|
+ (let [cells (dom/sel next-row ".ls-table-cell")]
|
|
|
+ (nth cells idx)))))]
|
|
|
+ (when next-cell
|
|
|
+ (let [next-cell-left (.-left (.getBoundingClientRect next-cell))]
|
|
|
+ (state/clear-selection!)
|
|
|
+ (dom/add-class! next-cell "selected")
|
|
|
+ (.focus next-cell)
|
|
|
+ (when (< next-cell-left container-left)
|
|
|
+ (.scrollIntoView next-cell #js {:inline "center"
|
|
|
+ :block "nearest"}))))))
|
|
|
+
|
|
|
+(rum/defc table-cell-container
|
|
|
+ [cell-opts body]
|
|
|
+ (let [*ref (hooks/use-ref nil)]
|
|
|
+ (shui/table-cell
|
|
|
+ (assoc cell-opts
|
|
|
+ :tabIndex 0
|
|
|
+ :ref *ref
|
|
|
+ :on-click (fn [] (click-cell (rum/deref *ref)))
|
|
|
+ :on-key-down (fn [e]
|
|
|
+ (let [container (rum/deref *ref)]
|
|
|
+ (case (util/ekey e)
|
|
|
+ "Escape"
|
|
|
+ (do
|
|
|
+ (if (util/input? (.-target e))
|
|
|
+ (do
|
|
|
+ (state/exit-editing-and-set-selected-blocks! [container])
|
|
|
+ (.focus container))
|
|
|
+ (do
|
|
|
+ (dom/remove-class! container "selected")
|
|
|
+ (let [row (util/rec-get-node container "ls-table-row")]
|
|
|
+ (state/exit-editing-and-set-selected-blocks! [row]))))
|
|
|
+ (util/stop e))
|
|
|
+ "Enter"
|
|
|
+ (do
|
|
|
+ (if (util/input? (.-target e)) ; number
|
|
|
+ (do
|
|
|
+ (state/exit-editing-and-set-selected-blocks! [container])
|
|
|
+ (.focus container))
|
|
|
+ (click-cell container))
|
|
|
+ (util/stop e))
|
|
|
+ "ArrowUp"
|
|
|
+ (navigate-to-cell e container :up)
|
|
|
+ "ArrowDown"
|
|
|
+ (navigate-to-cell e container :down)
|
|
|
+ "ArrowLeft"
|
|
|
+ (navigate-to-cell e container :left)
|
|
|
+ "ArrowRight"
|
|
|
+ (navigate-to-cell e container :right)
|
|
|
+ nil))))
|
|
|
+ body)))
|
|
|
+
|
|
|
(rum/defc table-row-inner < rum/static
|
|
|
[{:keys [row-selected?] :as table} row props {:keys [show-add-property? scrolling?]}]
|
|
|
- (let [pinned-columns (get-in table [:state :pinned-columns])
|
|
|
+ (let [*ref (hooks/use-ref nil)
|
|
|
+ pinned-columns (get-in table [:state :pinned-columns])
|
|
|
unpinned (get-in table [:state :unpinned-columns])
|
|
|
unpinned-columns (if show-add-property?
|
|
|
(conj (vec unpinned)
|
|
|
@@ -678,19 +812,60 @@
|
|
|
:select? select?
|
|
|
:add-property? add-property?
|
|
|
:style style}
|
|
|
- cell-placeholder (shui/table-cell cell-opts nil)]
|
|
|
+ cell-placeholder (table-cell-container cell-opts nil)]
|
|
|
(if (and scrolling? (not (:block/title row)))
|
|
|
cell-placeholder
|
|
|
(when-let [render (get column :cell)]
|
|
|
(lazy-table-cell
|
|
|
- (fn [] (shui/table-cell cell-opts (render table row column style)))
|
|
|
+ (fn []
|
|
|
+ (table-cell-container
|
|
|
+ cell-opts (render table row column style)))
|
|
|
cell-placeholder)))))]
|
|
|
(shui/table-row
|
|
|
(merge
|
|
|
props
|
|
|
{:key (str (:db/id row))
|
|
|
+ :tabIndex 0
|
|
|
+ :ref *ref
|
|
|
:data-state (when (row-selected? row) "selected")
|
|
|
- :on-pointer-down (fn [_e] (db-async/<get-block (state/get-current-repo) (:db/id row) {:children? false}))})
|
|
|
+ :data-id (:db/id row)
|
|
|
+ :blockid (str (:block/uuid row))
|
|
|
+ :on-pointer-down (fn [_e] (db-async/<get-block (state/get-current-repo) (:db/id row) {:children? false}))
|
|
|
+ :on-key-down (fn [e]
|
|
|
+ (let [container (rum/deref *ref)]
|
|
|
+ (when (dom/has-class? container "selected")
|
|
|
+ (case (util/ekey e)
|
|
|
+ "Enter"
|
|
|
+ (do
|
|
|
+ (state/sidebar-add-block! (state/get-current-repo) (:db/id row) :block)
|
|
|
+ (state/clear-selection!)
|
|
|
+ (util/stop e))
|
|
|
+ "ArrowLeft"
|
|
|
+ (do
|
|
|
+ (when-let [cell (->> (dom/sel container ".ls-table-cell")
|
|
|
+ (remove (fn [node]
|
|
|
+ (some? (dom/sel1 node ".ui__checkbox"))))
|
|
|
+ first)]
|
|
|
+ (state/clear-selection!)
|
|
|
+ (dom/add-class! cell "selected")
|
|
|
+ (.focus cell))
|
|
|
+ (util/stop e))
|
|
|
+ "ArrowRight"
|
|
|
+ (do
|
|
|
+ (when-let [cell (->> (dom/sel container ".ls-table-cell")
|
|
|
+ (remove (fn [node]
|
|
|
+ (some? (dom/sel1 node ".ui__checkbox"))))
|
|
|
+ last)]
|
|
|
+ (state/clear-selection!)
|
|
|
+ (dom/remove-class! container "selected")
|
|
|
+ (dom/add-class! cell "selected")
|
|
|
+ (.focus cell))
|
|
|
+ (util/stop e))
|
|
|
+ "Escape"
|
|
|
+ (do
|
|
|
+ (state/clear-selection!)
|
|
|
+ (util/stop e))
|
|
|
+ nil))))})
|
|
|
(when (seq pinned-columns)
|
|
|
[:div.sticky-columns.flex.flex-row
|
|
|
(map #(row-cell-f % {}) pinned-columns)])
|
|
|
@@ -996,106 +1171,102 @@
|
|
|
(rum/defc ^:large-vars/cleanup-todo filter-value-select < rum/static
|
|
|
[view-entity {:keys [data-fns] :as table} property value operator idx opts]
|
|
|
(let [type (:logseq.property/type property)
|
|
|
- property-ident (:db/ident property)
|
|
|
- [values set-values!] (hooks/use-state nil)
|
|
|
- [dropdown-open? set-dropdown-open!] (hooks/use-state false)]
|
|
|
+ property-ident (:db/ident property)]
|
|
|
(hooks/use-effect!
|
|
|
(fn []
|
|
|
- (p/do!
|
|
|
- (let [values (if (coll? value) value [value])
|
|
|
- ids (filter #(and (uuid? %) (nil? (db/entity [:block/uuid %]))) values)]
|
|
|
- (when (seq ids) (db-async/<get-blocks (state/get-current-repo) ids)))
|
|
|
- (when (and property-ident dropdown-open?
|
|
|
- (not (contains? #{:data :datetime :checkbox} type)))
|
|
|
- (p/let [data (db-async/<get-property-values property-ident {:view-id (:db/id view-entity)
|
|
|
- :query-entity-ids (:query-entity-ids opts)})]
|
|
|
- (set-values! (map (fn [v] (if (map? (:value v))
|
|
|
- (assoc v :value (:block/uuid (:value v)))
|
|
|
- v)) data))))))
|
|
|
- [property-ident dropdown-open?])
|
|
|
- (let [items (cond
|
|
|
- (contains? #{:before :after} operator)
|
|
|
- timestamp-options
|
|
|
- (= type :checkbox)
|
|
|
- [{:value true :label "true"} {:value false :label "false"}]
|
|
|
- :else
|
|
|
- values)
|
|
|
- filters (get-in table [:state :filters])
|
|
|
+ (let [values (if (coll? value) value [value])
|
|
|
+ ids (filter #(and (uuid? %) (nil? (db/entity [:block/uuid %]))) values)]
|
|
|
+ (when (seq ids) (db-async/<get-blocks (state/get-current-repo) ids))))
|
|
|
+ [])
|
|
|
+ (let [filters (get-in table [:state :filters])
|
|
|
set-filters! (:set-filters! data-fns)
|
|
|
many? (if (or (contains? #{:date-before :date-after :before :after} operator)
|
|
|
(contains? #{:checkbox} type))
|
|
|
false
|
|
|
- true)
|
|
|
- option (cond->
|
|
|
- {:input-default-placeholder (:block/title property)
|
|
|
- :input-opts {:class "!px-3 !py-1"}
|
|
|
- :items items
|
|
|
- :extract-fn :label
|
|
|
- :extract-chosen-fn :value
|
|
|
- :on-chosen (fn [value _selected? selected e]
|
|
|
- (when-not many?
|
|
|
- (shui/popup-hide!))
|
|
|
- (let [value' (if many? selected value)
|
|
|
- set-filters-fn (fn [value']
|
|
|
- (let [new-filters (update filters :filters
|
|
|
- (fn [col]
|
|
|
- (update col idx
|
|
|
- (fn [[property operator _value]]
|
|
|
- [property operator value']))))]
|
|
|
- (set-filters! new-filters)))]
|
|
|
- (if (= value "Custom date")
|
|
|
- (shui/popup-show!
|
|
|
- (.-target e)
|
|
|
- (ui/nlp-calendar
|
|
|
- {:initial-focus true
|
|
|
- :datetime? false
|
|
|
- :on-day-click (fn [value]
|
|
|
- (set-filters-fn value)
|
|
|
- (shui/popup-hide!))})
|
|
|
- {})
|
|
|
- (set-filters-fn value'))))}
|
|
|
- many?
|
|
|
- (assoc
|
|
|
- :multiple-choices? true
|
|
|
- :selected-choices value))]
|
|
|
- (shui/dropdown-menu
|
|
|
- (shui/dropdown-menu-trigger
|
|
|
- {:asChild true}
|
|
|
- (shui/button
|
|
|
- {:class "!px-2 rounded-none border-r"
|
|
|
- :variant "ghost"
|
|
|
- :size :sm
|
|
|
- :on-click #(set-dropdown-open! (not dropdown-open?))}
|
|
|
- (let [value (cond
|
|
|
- (uuid? value)
|
|
|
- (db/entity [:block/uuid value])
|
|
|
- (instance? js/Date value)
|
|
|
- (some->> (tc/to-date value)
|
|
|
- (t/to-default-time-zone)
|
|
|
- (tf/unparse (tf/formatter "yyyy-MM-dd")))
|
|
|
- (and (coll? value) (every? uuid? value))
|
|
|
- (keep #(db/entity [:block/uuid %]) value)
|
|
|
- :else
|
|
|
- value)]
|
|
|
- [:div.flex.flex-row.items-center.gap-1.text-xs
|
|
|
- (cond
|
|
|
- (de/entity? value)
|
|
|
- [:div (get-property-value-content value)]
|
|
|
-
|
|
|
- (string? value)
|
|
|
- [:div value]
|
|
|
-
|
|
|
- (boolean? value)
|
|
|
- [:div (str value)]
|
|
|
-
|
|
|
- (seq value)
|
|
|
- (->> (map (fn [v] [:div (get-property-value-content v)]) value)
|
|
|
- (interpose [:div "or"]))
|
|
|
- :else
|
|
|
- "All")])))
|
|
|
- (shui/dropdown-menu-content
|
|
|
- {:align "start"}
|
|
|
- (select/select option))))))
|
|
|
+ true)]
|
|
|
+ (shui/button
|
|
|
+ {:class "!px-2 rounded-none border-r"
|
|
|
+ :variant "ghost"
|
|
|
+ :size :sm
|
|
|
+ :on-click (fn [e]
|
|
|
+ (p/let [values (when (and property-ident
|
|
|
+ (not (contains? #{:data :datetime :checkbox} type)))
|
|
|
+ (p/let [data (db-async/<get-property-values property-ident {:view-id (:db/id view-entity)
|
|
|
+ :query-entity-ids (:query-entity-ids opts)})]
|
|
|
+ (map (fn [v] (if (map? (:value v))
|
|
|
+ (assoc v :value (:block/uuid (:value v)))
|
|
|
+ v)) data)))
|
|
|
+ items (cond
|
|
|
+ (contains? #{:before :after} operator)
|
|
|
+ timestamp-options
|
|
|
+ (= type :checkbox)
|
|
|
+ [{:value true :label "true"} {:value false :label "false"}]
|
|
|
+ :else
|
|
|
+ values)]
|
|
|
+ (shui/popup-show!
|
|
|
+ (.-target e)
|
|
|
+ (fn []
|
|
|
+ (let [option (cond->
|
|
|
+ {:input-default-placeholder (:block/title property)
|
|
|
+ :input-opts {:class "!px-3 !py-1"}
|
|
|
+ :items items
|
|
|
+ :extract-fn :label
|
|
|
+ :extract-chosen-fn :value
|
|
|
+ :on-chosen (fn [value _selected? selected e]
|
|
|
+ (when-not many?
|
|
|
+ (shui/popup-hide!))
|
|
|
+ (let [value' (if many? selected value)
|
|
|
+ set-filters-fn (fn [value']
|
|
|
+ (let [new-filters (update filters :filters
|
|
|
+ (fn [col]
|
|
|
+ (update col idx
|
|
|
+ (fn [[property operator _value]]
|
|
|
+ [property operator value']))))]
|
|
|
+ (set-filters! new-filters)))]
|
|
|
+ (if (= value "Custom date")
|
|
|
+ (shui/popup-show!
|
|
|
+ (.-target e)
|
|
|
+ (ui/nlp-calendar
|
|
|
+ {:initial-focus true
|
|
|
+ :datetime? false
|
|
|
+ :on-day-click (fn [value]
|
|
|
+ (set-filters-fn value)
|
|
|
+ (shui/popup-hide!))})
|
|
|
+ {})
|
|
|
+ (set-filters-fn value'))))}
|
|
|
+ many?
|
|
|
+ (assoc
|
|
|
+ :multiple-choices? true
|
|
|
+ :selected-choices value))]
|
|
|
+ (select/select option)))
|
|
|
+ {:align :start})))}
|
|
|
+ (let [value (cond
|
|
|
+ (uuid? value)
|
|
|
+ (db/entity [:block/uuid value])
|
|
|
+ (instance? js/Date value)
|
|
|
+ (some->> (tc/to-date value)
|
|
|
+ (t/to-default-time-zone)
|
|
|
+ (tf/unparse (tf/formatter "yyyy-MM-dd")))
|
|
|
+ (and (coll? value) (every? uuid? value))
|
|
|
+ (keep #(db/entity [:block/uuid %]) value)
|
|
|
+ :else
|
|
|
+ value)]
|
|
|
+ [:div.flex.flex-row.items-center.gap-1.text-xs
|
|
|
+ (cond
|
|
|
+ (de/entity? value)
|
|
|
+ [:div (get-property-value-content value)]
|
|
|
+
|
|
|
+ (string? value)
|
|
|
+ [:div value]
|
|
|
+
|
|
|
+ (boolean? value)
|
|
|
+ [:div (str value)]
|
|
|
+
|
|
|
+ (seq value)
|
|
|
+ (->> (map (fn [v] [:div (get-property-value-content v)]) value)
|
|
|
+ (interpose [:div "or"]))
|
|
|
+ :else
|
|
|
+ "All")])))))
|
|
|
|
|
|
(rum/defc filter-value < rum/static
|
|
|
[view-entity table property operator value filters set-filters! idx opts]
|
|
|
@@ -1280,14 +1451,12 @@
|
|
|
|
|
|
(rum/defc table-body < rum/static
|
|
|
[table option rows *scroller-ref set-items-rendered!]
|
|
|
- (let [[scrolling? set-scrolling!] (hooks/use-state false)
|
|
|
- sidebar? (get-in option [:config :sidebar?])]
|
|
|
+ (let [[scrolling? set-scrolling!] (hooks/use-state false)]
|
|
|
(when (seq rows)
|
|
|
(ui/virtualized-list
|
|
|
{:ref #(reset! *scroller-ref %)
|
|
|
- :custom-scroll-parent (if sidebar?
|
|
|
- (first (dom/by-class "sidebar-item-list"))
|
|
|
- (gdom/getElement "main-content-container"))
|
|
|
+ :increase-viewport-by {:top 300 :bottom 300}
|
|
|
+ :custom-scroll-parent (get-scroll-parent (:config option))
|
|
|
:compute-item-key (fn [idx]
|
|
|
(let [block-id (util/nth-safe rows idx)]
|
|
|
(str "table-row-" block-id)))
|
|
|
@@ -1332,7 +1501,7 @@
|
|
|
(ui/virtualized-list
|
|
|
{:ref #(reset! *scroller-ref %)
|
|
|
:class "content"
|
|
|
- :custom-scroll-parent (gdom/getElement "main-content-container")
|
|
|
+ :custom-scroll-parent (get-scroll-parent config)
|
|
|
:increase-viewport-by {:top 64 :bottom 64}
|
|
|
:compute-item-key (fn [idx]
|
|
|
(let [block-id (util/nth-safe rows idx)]
|
|
|
@@ -1377,7 +1546,7 @@
|
|
|
(ui/virtualized-grid
|
|
|
{:ref #(reset! *scroller-ref %)
|
|
|
:total-count (count blocks)
|
|
|
- :custom-scroll-parent (gdom/getElement "main-content-container")
|
|
|
+ :custom-scroll-parent (get-scroll-parent config)
|
|
|
:skipAnimationFrameInResizeObserver true
|
|
|
:compute-item-key (fn [idx]
|
|
|
(str (:db/id view-entity) "-card-" idx))
|
|
|
@@ -1753,9 +1922,12 @@
|
|
|
(assoc-in [:data-fns :add-new-object!] add-new-object!)
|
|
|
(assoc :data group ; data for this group
|
|
|
)))
|
|
|
- readable-property-value #(if (and (map? %) (or (:block/title %) (:logseq.property/value %)))
|
|
|
- (db-property/property-value-content %)
|
|
|
- (str %))
|
|
|
+ readable-property-value #(cond (and (map? %) (or (:block/title %) (:logseq.property/value %)))
|
|
|
+ (db-property/property-value-content %)
|
|
|
+ (= (:db/ident %) :logseq.property/empty-placeholder)
|
|
|
+ "Empty"
|
|
|
+ :else
|
|
|
+ (str %))
|
|
|
group-by-page? (or (= :block/page group-by-property-ident)
|
|
|
(and (not db-based?) (contains? #{:linked-references :unlinked-references} display-type)))]
|
|
|
(rum/with-key
|
|
|
@@ -1865,7 +2037,8 @@
|
|
|
(:logseq.property.linked-references/includes view-parent)
|
|
|
(:logseq.property.linked-references/excludes view-parent)
|
|
|
(:filters view-parent)
|
|
|
- query-entity-ids]))
|
|
|
+ query-entity-ids
|
|
|
+ (:data-changes-version option)]))
|
|
|
(if loading?
|
|
|
[:div.flex.flex-col.space-2.gap-2.my-2
|
|
|
(repeat 3 (shui/skeleton {:class "h-6 w-full"}))]
|
|
|
@@ -1889,10 +2062,24 @@
|
|
|
:load-view-data load-view-data
|
|
|
:set-view-entity! set-view-entity!))])))
|
|
|
|
|
|
+(defn sub-view-data-changes
|
|
|
+ [view-parent view-feature-type]
|
|
|
+ (when view-parent
|
|
|
+ (when-let [repo (state/get-current-repo)]
|
|
|
+ (when-let [k (case view-feature-type
|
|
|
+ :class-objects :frontend.worker.react/objects
|
|
|
+ :linked-references :frontend.worker.react/refs
|
|
|
+ nil)]
|
|
|
+ (let [*version (atom 0)]
|
|
|
+ (react/q repo [k (:db/id view-parent)]
|
|
|
+ {:query-fn (fn [_] (swap! *version inc))}
|
|
|
+ nil))))))
|
|
|
+
|
|
|
(rum/defc sub-view < rum/reactive db-mixins/query
|
|
|
[view-entity option]
|
|
|
- (let [view (or (some-> (:db/id view-entity) db/sub-block) view-entity)]
|
|
|
- (view-aux view option)))
|
|
|
+ (let [view (or (some-> (:db/id view-entity) db/sub-block) view-entity)
|
|
|
+ data-changes-version (some-> (sub-view-data-changes (:view-parent option) (:view-feature-type option)) rum/react)]
|
|
|
+ (view-aux view (assoc option :data-changes-version data-changes-version))))
|
|
|
|
|
|
(rum/defc view < rum/static
|
|
|
[{:keys [view-parent view-feature-type view-entity] :as option}]
|
|
|
@@ -1903,7 +2090,7 @@
|
|
|
(hooks/use-effect!
|
|
|
#(c.m/run-task*
|
|
|
(m/sp
|
|
|
- (when-not query? ; TODO: move query logic to worker
|
|
|
+ (when-not query?
|
|
|
(let [repo (state/get-current-repo)]
|
|
|
(when (and db-based? (not view-entity))
|
|
|
(c.m/<? (db-async/<get-views repo (:db/id view-parent) view-feature-type))
|
|
|
@@ -1920,6 +2107,8 @@
|
|
|
(when (if db-based? view-entity (or view-entity view-parent
|
|
|
(= view-feature-type :all-pages)))
|
|
|
(let [option' (assoc option
|
|
|
+ :view-feature-type (or view-feature-type
|
|
|
+ (:logseq.property.view/feature-type view-entity))
|
|
|
:views views
|
|
|
:set-views! set-views!
|
|
|
:set-view-entity! set-view-entity!)]
|