Use new tables for all pages

feat/tables
Tienson Qin 2024-07-05 15:13:34 +08:00
parent 5fa81817de
commit d523948803
4 changed files with 219 additions and 290 deletions

View File

@ -106,11 +106,13 @@
prop)
children]))
;; FIXME: sticky header
(rum/defc table-header < rum/static
[& prop-and-children]
(let [[prop children] (get-prop-and-children prop-and-children)]
[:div.flex.flex-row.items-center.w-fit
(merge {:class "border-y transition-colors bg-gray-01"}
(merge {:class "border-y transition-colors bg-gray-01"
:style {:z-index 100}}
prop)
children]))

View File

@ -131,7 +131,6 @@
(def table-option table-core/table-option)
(def table table-core/table)
(def table-header table-core/table-header)
(def table-head table-core/table-head)
(def table-row table-core/table-row)
(def table-cell table-core/table-cell)
(def table-get-selection-rows table-core/get-selection-rows)

View File

@ -1,161 +1,57 @@
(ns frontend.components.all-pages
"All pages"
(:require [logseq.shui.ui :as shui]
[rum.core :as rum]
[frontend.util :as util]
[frontend.ui :as ui]
[clojure.string :as string]
(:require [clojure.string :as string]
[frontend.components.block :as component-block]
[frontend.components.page :as component-page]
[frontend.components.views :as views]
[frontend.handler.page :as page-handler]
[frontend.state :as state]
[frontend.date :as date]
[goog.object :as gobj]
[goog.dom :as gdom]
[cljs-bean.core :as bean]
[promesa.core :as p]
[logseq.db :as ldb]
[frontend.search.fuzzy :as fuzzy-search]))
;; columns:
;; page name, tags, backlinks, created at updated at
;; default sort: updated at
(defn header-checkbox [{:keys [selected-all? selected-some? toggle-selected-all!]}]
(shui/checkbox
{:checked (or selected-all? (and selected-some? "indeterminate"))
:on-checked-change toggle-selected-all!
:aria-label "Select all"}))
(defn row-checkbox [{:keys [row-selected? row-toggle-selected!]} row _column]
(shui/checkbox
{:checked (row-selected? row)
:on-checked-change (fn [v] (row-toggle-selected! row v))
:aria-label "Select row"}))
(defn- header-cp
[{:keys [column-toggle-sorting! state]} column]
(let [sorting (:sorting state)
[asc?] (some (fn [item] (when (= (:id item) (:id column))
(when-some [asc? (:asc? item)]
[asc?]))) sorting)]
(shui/button
{:variant "text"
:class "!pl-0 hover:text-foreground"
:onClick #(column-toggle-sorting! column)}
(:name column)
(case asc?
true
(ui/icon "arrow-up")
false
(ui/icon "arrow-down")
nil))))
(comment
(defn- default-cell-cp
[_table row column]
(str (get row (:id column)))))
(defn- timestamp-cell-cp
[_table row column]
(some-> (get row (:id column))
date/int->local-time-2))
[promesa.core :as p]
[rum.core :as rum]))
(def columns
[{:id :select
:name "Select"
:header (fn [table _column] (header-checkbox table))
:cell (fn [table row column] (row-checkbox table row column))
:column-list? false}
{:id :block/original-name
[{:id :block/original-name
:name "Page name"
:header header-cp
:cell (fn [_table row _column]
(component-block/page-cp {} row))}
(component-block/page-cp {} row))
:type :string}
{:id :block/type
:name "Type"
:header header-cp
:cell (fn [_table row _column] [:div.capitalize (string/join ", " (get row :block/type))])
:get-value (fn [row] (string/join ", " (get row :block/type)))}
:get-value (fn [row] (string/join ", " (get row :block/type)))
:type :string}
{:id :block/tags
:name "Tags"
:header header-cp
:cell (fn [_table row _column]
(component-block/tags {} row))
:get-value (fn [row] (string/join ", " (map :block/original-name (get row :block/tags))))}
:name "Tags"}
{:id :block.temp/refs-count
:name "Backlinks"
:header header-cp
:cell (fn [_table row _column] (:block.temp/refs-count row))}
{:id :block/created-at
:name "Created At"
:header header-cp
:cell timestamp-cell-cp}
{:id :block/updated-at
:name "Updated At"
:header header-cp
:cell timestamp-cell-cp}])
:cell (fn [_table row _column] (:block.temp/refs-count row))
:type :number}])
(defn- get-all-pages
[]
(->> (page-handler/get-all-pages (state/get-current-repo))
(map (fn [p] (assoc p :id (:db/id p))))))
(rum/defc columns-select
[columns {:keys [column-visible? column-toggle-visibility]}]
(shui/dropdown-menu
(shui/dropdown-menu-trigger
{:asChild true}
(shui/button
{:variant "outline" :size :sm
:class "text-muted-foreground"}
"Columns"
(ui/icon "chevron-down")))
(shui/dropdown-menu-content
{:align "end"}
(for [column (remove #(false? (:column-list? %)) columns)]
(shui/dropdown-menu-checkbox-item
{:key (str (:id column))
:className "capitalize"
:checked (column-visible? column)
:onCheckedChange #(column-toggle-visibility column %)}
(:name column))))))
(defn table-header
[table columns]
(shui/table-row
{:class "bg-gray-01 shadow"}
(for [column columns]
(let [style (case (:id column)
:block/original-name
{}
:select
{:width 32}
{:width 180})]
(shui/table-head
{:key (str (:id column))
:style style}
(let [header-fn (:header column)]
(if (fn? header-fn)
(header-fn table column)
header-fn)))))))
(defn table-row
[{:keys [row-selected?] :as table} rows columns props]
(let [idx (gobj/get props "data-index")
row (nth rows idx)]
(shui/table-row
(merge
(bean/->clj props)
{:key (str (:id row))
:data-state (when (row-selected? row) "selected")})
(for [column columns]
(let [id (str (:id row) "-" (:id column))
render (get column :cell)]
(shui/table-cell
{:key id}
(render table row column)))))))
(rum/defc all-pages < rum/static
[]
(let [[data set-data!] (rum/use-state (get-all-pages))
columns (views/build-columns {} columns
{:with-object-name? false})]
(rum/use-effect!
(fn []
(when-let [^js worker @state/*db-worker]
(p/let [result-str (.get-page-refs-count worker (state/get-current-repo))
result (ldb/read-transit-str result-str)
data (map (fn [row] (assoc row :block.temp/refs-count (get result (:db/id row) 0))) data)]
(set-data! data))))
[])
[:div.ls-all-pages.max-w-fit.m-auto
(views/view nil {:data data
:set-data! set-data!
:columns columns})]))
(comment
(rum/defc all-pages < rum/static
[]
(let [[input set-input!] (rum/use-state "")
[sorting set-sorting!] (rum/use-state [{:id :block/updated-at, :asc? false}])
@ -227,4 +123,4 @@
[:div.flex-1.text-sm.text-muted-foreground
(if (pos? selected-rows-count)
(str selected-rows-count " of " rows-count " row(s) selected.")
(str "Total: " rows-count))]])]))
(str "Total: " rows-count))]])])))

View File

@ -75,10 +75,10 @@
(cond
(uuid? entity)
(db-property/property-value-content (db/entity [:block/uuid entity]))
(map? entity)
(de/entity? entity)
(db-property/property-value-content entity)
:else
(str entity))))
entity)))
(defn- get-property-value-for-search
[block property]
@ -95,30 +95,43 @@
(string/join ", " col))))
(defn build-columns
[config properties]
[config properties & {:keys [with-object-name?]
:or {with-object-name? true}}]
(let [container-id (state/get-next-container-id)]
(concat
(->> (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}
(when with-object-name?
{:id :object/name
:name "Name"
:type :string
:header header-cp
:cell (fn [_table row _column]
(component-block/block-container (assoc config :table? true) row))
:disable-hide? true}]
:disable-hide? true})]
(map
(fn [property]
{:id (:db/ident property)
:name (:block/original-name property)
:header header-cp
:cell (fn [_table row _column]
(pv/property-value row property (get row (:db/ident property)) {:container-id container-id}))
:get-value (fn [row] (get-property-value-for-search row property))})
(let [ident (or (:id property) (:db/ident property))
property (if (de/entity? property)
property
(or (db/entity ident) property))]
{:id ident
:name (or (:name property)
(:block/original-name property))
:header (or (:header property)
header-cp)
:cell (or (:cell property)
(when (de/entity? property)
(fn [_table row _column]
(pv/property-value row property (get row (:db/ident property)) {:container-id container-id}))))
:get-value (or (:get-value property)
(when (de/entity? property)
(fn [row] (get-property-value-for-search row property))))
:type (:type property)}))
properties)
[{:id :block/created-at
@ -130,7 +143,8 @@
:name "Updated At"
:type :date-time
:header header-cp
:cell timestamp-cell-cp}])))
:cell timestamp-cell-cp}])
(remove nil?))))
(defn- sort-columns
[columns ordered-column-ids]
@ -177,7 +191,7 @@
[column]
(case (:id column)
:select 32
:object/name 360
(:object/name :block/original-name :block/name :block/content) 360
(:block/created-at :block/updated-at) 160
180))
@ -204,8 +218,9 @@
[{:keys [row-selected?] :as table} rows columns props]
(let [idx (gobj/get props "data-index")
row (nth rows idx)
row (db/sub-block (:id row))
row (assoc row :id (:db/id row))]
row' (db/sub-block (:id row))
;; merge entity temporal attributes
row (reduce (fn [e [k v]] (assoc e k v)) row' (.-kv ^js row))]
(shui/table-row
(merge
(bean/->clj props)
@ -312,6 +327,7 @@
(rum/defc filter-property < rum/static
[columns {:keys [data-fns] :as table}]
(let [[property set-property!] (rum/use-state nil)
schema (:schema (db/get-db))
timestamp? (timestamp-property? (:db/ident property))
set-filters! (:set-filters! data-fns)
filters (get-in table [:state :filters])
@ -328,8 +344,11 @@
(let [id (:id column)
property (db/entity id)
internal-property {:db/ident (:id column)
:block/original-name (:name column)}]
(if (or property (timestamp-property? id))
:block/original-name (:name column)
:block/schema {:type (:type column)}}]
(if (or property
(= :db.cardinality/many (:db/cardinality (get schema id)))
(not= (:type column) :string))
(set-property! (or property internal-property))
(do
(shui/popup-hide!)
@ -364,8 +383,11 @@
:input-default-placeholder (if property (:block/original-name property) "Select")
:multiple-choices? true
:on-chosen (fn [_value _selected? selected]
(let [filters' (if (seq selected)
(conj filters [(:db/ident property) :is selected])
(let [selected-value (if (de/entity? (first selected))
(set (map :block/uuid selected))
selected)
filters' (if (seq selected)
(conj filters [(:db/ident property) :is selected-value])
filters)]
(set-filters! filters')))})))
:else
@ -598,7 +620,7 @@
(filter-value-select table property value operator idx))))
(rum/defc filters-row < rum/static
[{:keys [data-fns] :as table}]
[{:keys [data-fns columns] :as table}]
(let [filters (get-in table [:state :filters])
{:keys [set-filters!]} data-fns]
(when (seq filters)
@ -609,7 +631,10 @@
property (if (= property-ident :object/name)
{:db/ident property-ident
:block/original-name "Name"}
(db/entity property-ident))]
(or (db/entity property-ident)
(some (fn [column] (when (= (:id column) property-ident)
{:db/ident (:id column)
:block/original-name (:name column)})) columns)))]
[:div.flex.flex-row.items-center.border.rounded
(shui/button
{:class "!px-2 rounded-none border-r"
@ -650,26 +675,30 @@
(set? value) value
(nil? value) #{}
:else #{value})
entity? (de/entity? (first value'))
result
(case operator
:is
(if (boolean? match)
(= (boolean (get-property-value-content (get row property-ident))) match)
(if (and (empty? match) (empty? value))
(if (and (empty? match) (empty? value'))
true
(when (coll? value)
(boolean (seq (set/intersection (set (map :block/uuid value')) match))))))
(if entity?
(boolean (seq (set/intersection (set (map :block/uuid value')) match)))
(boolean (seq (set/intersection (set value') match))))))
:is-not
(if (boolean? match)
(not= (boolean (get-property-value-content (get row property-ident))) match)
(cond
(and (empty? match) (seq value))
(and (empty? match) (seq value'))
true
(and (seq match) (empty? value))
(and (seq match) (empty? value'))
true
(coll? value)
(boolean (empty? (set/intersection (set (map :block/uuid value')) match)))))
:else
(if entity?
(boolean (empty? (set/intersection (set (map :block/uuid value')) match)))
(boolean (empty? (set/intersection (set value') match))))))
:text-contains
(some #(fuzzy-matched? match (get-property-value-content %)) value')
@ -751,7 +780,8 @@
filters))
(defn- db-set-table-state!
[entity {:keys [set-sorting! set-filters! set-visible-columns! set-ordered-columns!]}]
[entity {:keys [set-sorting! set-filters! set-visible-columns! set-ordered-columns!] :as option}]
(if entity
(let [repo (state/get-current-repo)]
{:set-sorting!
(fn [sorting]
@ -773,7 +803,8 @@
(fn [ordered-columns]
(let [ids (vec (remove #{:select} ordered-columns))]
(set-ordered-columns! ordered-columns)
(property-handler/set-block-property! repo (:db/id entity) :logseq.property/table-ordered-columns ids)))}))
(property-handler/set-block-property! repo (:db/id entity) :logseq.property/table-ordered-columns ids)))})
option))
(rum/defc view < rum/static
[view-entity {:keys [data set-data! columns add-new-object!]}]
@ -841,6 +872,7 @@
(let [columns' (:columns table)
rows (:rows table)]
[:div.ls-table-rows.rounded-md.content.overflow-x-auto.force-visible-scrollbar
[:div.relative
(table-header table columns')
(ui/virtualized-table
@ -850,4 +882,4 @@
(shui/table {}
(.-children props)))
:TableRow (fn [props] (table-row table rows columns' props))}})
(when add-new-object! (add-new-row table))])]))
(when add-new-object! (add-new-row table))]])]))