mirror of
https://github.com/logseq/logseq.git
synced 2026-04-24 14:14:55 +00:00
Merge branch 'master' into enhance/mobile-ux-2
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -13,7 +13,8 @@ pom.xml.asc
|
||||
.hg/
|
||||
|
||||
node_modules/
|
||||
static/
|
||||
static/**
|
||||
!static/yarn.lock
|
||||
tmp
|
||||
cljs-test-runner-out
|
||||
|
||||
|
||||
@@ -82,8 +82,9 @@ html[data-theme='dark'] {
|
||||
--ls-scrollbar-thumb-hover-color: rgba(255, 255, 255, 0.2);
|
||||
--ls-cloze-text-color: #8fbc8f;
|
||||
--ls-icon-color: var(--ls-link-text-color);
|
||||
--ls-search-icon-color: var(--ls-link-text-color);
|
||||
--ls-a-chosen-bg: var(--ls-secondary-background-color);
|
||||
--ls-search-icon-color: var(--ls-primary-text-color);
|
||||
--ls-search-icon-hover-color: var(--ls-secondary-text-color);
|
||||
--ls-a-chosen-bg: var(--ls-quaternary-background-color);
|
||||
--ls-pie-bg-color: #01303b;
|
||||
--ls-pie-fg-color: #0b5869;
|
||||
--ls-highlight-color-gray: var(--color-gray-900);
|
||||
@@ -119,11 +120,11 @@ html[data-theme='light'] {
|
||||
--ls-secondary-background-color: #f7f7f7;
|
||||
--ls-tertiary-background-color: #eaeaea;
|
||||
--ls-quaternary-background-color: #dcdcdc;
|
||||
--ls-table-tr-even-background-color: #f7f7f7;
|
||||
--ls-table-tr-even-background-color: var(--ls-secondary-background-color);
|
||||
--ls-active-primary-color: rgb(0, 105, 182);
|
||||
--ls-active-secondary-color: #00477c;
|
||||
--ls-block-properties-background-color: #f7f7f7;
|
||||
--ls-page-properties-background-color: #f7f7f7;
|
||||
--ls-block-properties-background-color: var(--ls-secondary-background-color);
|
||||
--ls-page-properties-background-color: var(--ls-secondary-background-color);
|
||||
--ls-block-ref-link-text-color: #d8e1e8;
|
||||
--ls-border-color: #ccc;
|
||||
--ls-secondary-border-color: #e2e2e2;
|
||||
@@ -135,8 +136,8 @@ html[data-theme='light'] {
|
||||
--ls-title-text-color: var(--ls-header-button-background);
|
||||
--ls-link-text-color: #106ba3;
|
||||
--ls-link-text-hover-color: #1a537c;
|
||||
--ls-link-ref-text-color: #106ba3;
|
||||
--ls-link-ref-text-hover-color: #1a537c;
|
||||
--ls-link-ref-text-color: var(--ls-link-text-color);
|
||||
--ls-link-ref-text-hover-color: var(--ls-link-text-hover-color);
|
||||
--ls-tag-text-color: var(--ls-link-ref-text-color);
|
||||
--ls-tag-text-hover-color: var(--ls-link-ref-text-hover-color);
|
||||
--ls-slide-background-color: #fff;
|
||||
@@ -152,15 +153,16 @@ html[data-theme='light'] {
|
||||
--ls-page-blockquote-border-color: #799bbc;
|
||||
--ls-page-mark-color: #262626;
|
||||
--ls-page-mark-bg-color: #fef3ac;
|
||||
--ls-page-inline-code-bg-color: #f7f7f7;
|
||||
--ls-page-inline-code-bg-color: var(--ls-secondary-background-color);
|
||||
--ls-page-inline-code-color: var(--ls-primary-text-color);
|
||||
--ls-scrollbar-foreground-color: rgba(0, 0, 0, 0.1);
|
||||
--ls-scrollbar-background-color: rgba(0, 0, 0, 0.05);
|
||||
--ls-scrollbar-thumb-hover-color: rgba(0, 0, 0, 0.2);
|
||||
--ls-cloze-text-color: #0000cd;
|
||||
--ls-icon-color: #646464;
|
||||
--ls-search-icon-color: var(--ls-icon-color);
|
||||
--ls-a-chosen-bg: #f7f7f7;
|
||||
--ls-search-icon-color: var(--ls-primary-text-color);
|
||||
--ls-search-icon-hover-color: var(--ls-secondary-text-color);
|
||||
--ls-a-chosen-bg: var(--ls-quaternary-background-color);
|
||||
--ls-pie-bg-color: #e1e1e1;
|
||||
--ls-pie-fg-color: #0a4a5d;
|
||||
--ls-highlight-color-gray: var(--color-gray-100);
|
||||
|
||||
@@ -1923,7 +1923,8 @@
|
||||
(when bg-color
|
||||
{:style {:background-color (if (some #{bg-color} ui/block-background-colors)
|
||||
(str "var(--ls-highlight-color-" bg-color ")")
|
||||
bg-color)}
|
||||
bg-color)
|
||||
:color (when-not (some #{bg-color} ui/block-background-colors) "white")}
|
||||
:class "px-1 with-bg-color"}))
|
||||
(remove-nils
|
||||
(concat
|
||||
|
||||
@@ -18,11 +18,35 @@
|
||||
border: none;
|
||||
border-radius: unset !important;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.chosen {
|
||||
background-color: var(--ls-quaternary-background-color);
|
||||
color: var(--ls-secondary-text-color);
|
||||
.type-icon {
|
||||
color: var(--ls-search-icon-color);
|
||||
|
||||
&.highlight {
|
||||
color: var(--ls-selection-text-color);
|
||||
border-color: var(--ls-selection-background-color);
|
||||
|
||||
&:before {
|
||||
opacity: 0.5;
|
||||
background: var(--ls-selection-background-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.chosen .type-icon,
|
||||
&:hover .type-icon {
|
||||
color: var(--ls-search-icon-hover-color);
|
||||
}
|
||||
|
||||
&.chosen,
|
||||
&.chosen p {
|
||||
background-color: var(--ls-a-chosen-bg);
|
||||
color: var(--ls-secondary-text-color);
|
||||
}
|
||||
|
||||
&:hover p {
|
||||
color: var(--ls-secondary-text-color);
|
||||
}
|
||||
}
|
||||
|
||||
.command-results-wrap,
|
||||
|
||||
@@ -818,6 +818,36 @@
|
||||
(notification/show! (str (t :tips/all-done) "!") :success)
|
||||
(js/setTimeout #(refresh-fn) 200)))]]))
|
||||
|
||||
(rum/defc pagination
|
||||
"Pagination component, like `<< <Prev 1/10 Next> >>`.
|
||||
|
||||
- current: current page number
|
||||
- total: total number of items
|
||||
- per-page: number of items per page
|
||||
- on-change: callback function when page number changes"
|
||||
[& {:keys [current total per-page on-change]
|
||||
:or {current 1 per-page 40}}]
|
||||
(let [total-pages (int (Math/ceil (/ total per-page)))
|
||||
has-prev? (> current 1)
|
||||
has-next? (< current total-pages)
|
||||
prev-page (if (= 1 current) 1 (dec current))
|
||||
next-page (if (= total-pages current) total-pages (inc current))]
|
||||
[:div.flex.items-center.noselect
|
||||
(when has-prev?
|
||||
[[:a.fade-link.flex
|
||||
{:on-click #(on-change 1)}
|
||||
(ui/icon "chevrons-left")]
|
||||
[:a.fade-link.flex.items-center {:on-click #(on-change prev-page)}
|
||||
(ui/icon "caret-left") (t :paginates/prev)]])
|
||||
[:div.px-2
|
||||
[:span (str current "/" total-pages)]]
|
||||
(when has-next?
|
||||
[[:a.fade-link.flex.items-center {:on-click #(on-change next-page)}
|
||||
(t :paginates/next) (ui/icon "caret-right")]
|
||||
[:a.fade-link.flex
|
||||
{:on-click #(on-change total-pages)}
|
||||
(ui/icon "chevrons-right")]])]))
|
||||
|
||||
(rum/defcs ^:large-vars/cleanup-todo all-pages < rum/reactive
|
||||
(rum/local nil ::pages)
|
||||
(rum/local nil ::search-key)
|
||||
@@ -847,15 +877,17 @@
|
||||
*search-input (rum/create-ref)
|
||||
|
||||
*indeterminate (rum/derived-atom
|
||||
[*checks] ::indeterminate
|
||||
(fn [checks]
|
||||
(when-let [checks (vals checks)]
|
||||
(if (every? true? checks)
|
||||
1 (if (some true? checks) -1 0)))))
|
||||
[*checks] ::indeterminate
|
||||
(fn [checks]
|
||||
(when-let [checks (vals checks)]
|
||||
(if (every? true? checks)
|
||||
1 (if (some true? checks) -1 0)))))
|
||||
|
||||
mobile? (util/mobile?)
|
||||
total-items (count @*results-all)
|
||||
;; FIXME: "pages" is ambiguous here, it can be either "Logseq pages" or "result pages"
|
||||
total-pages (if-not @*results-all 0
|
||||
(js/Math.ceil (/ (count @*results-all) per-page-num)))
|
||||
(js/Math.ceil (/ total-items per-page-num)))
|
||||
to-page (fn [page]
|
||||
(when (> total-pages 1)
|
||||
(if (and (> page 0)
|
||||
@@ -878,7 +910,7 @@
|
||||
[:div.flex-1.cp__all_pages
|
||||
[:h1.title (t :all-pages)]
|
||||
|
||||
[:div.text-sm.ml-1.opacity-70.mb-4 (t :paginates/pages (count @*results-all))]
|
||||
[:div.text-sm.ml-1.opacity-70.mb-4 (t :paginates/pages total-items)]
|
||||
|
||||
(when current-repo
|
||||
|
||||
@@ -918,168 +950,153 @@
|
||||
[idx (boolean (get @*checks idx))])))
|
||||
(reset! *results pages)))
|
||||
|
||||
(let [has-prev? (> @*current-page 1)
|
||||
has-next? (not= @*current-page total-pages)]
|
||||
[:div
|
||||
[:div.actions
|
||||
{:class (util/classnames [{:has-selected (or (nil? @*indeterminate)
|
||||
(not= 0 @*indeterminate))}])}
|
||||
[:div.l.flex.items-center
|
||||
[:div.actions-wrap
|
||||
(ui/button
|
||||
[(ui/icon "trash" {:style {:font-size 15}}) (t :delete)]
|
||||
:on-click (fn []
|
||||
(let [selected (filter (fn [[_ v]] v) @*checks)
|
||||
selected (and (seq selected)
|
||||
(into #{} (for [[k _] selected] k)))]
|
||||
(when-let [pages (and selected (filter #(contains? selected (:block/idx %)) @*results))]
|
||||
(state/set-modal! (batch-delete-dialog pages false #(do
|
||||
(reset! *checks nil)
|
||||
(refresh-pages)))))))
|
||||
:class "fade-link"
|
||||
:small? true)]
|
||||
[:div
|
||||
[:div.actions
|
||||
{:class (util/classnames [{:has-selected (or (nil? @*indeterminate)
|
||||
(not= 0 @*indeterminate))}])}
|
||||
[:div.l.flex.items-center
|
||||
[:div.actions-wrap
|
||||
(ui/button
|
||||
[(ui/icon "trash" {:style {:font-size 15}}) (t :delete)]
|
||||
:on-click (fn []
|
||||
(let [selected (filter (fn [[_ v]] v) @*checks)
|
||||
selected (and (seq selected)
|
||||
(into #{} (for [[k _] selected] k)))]
|
||||
(when-let [pages (and selected (filter #(contains? selected (:block/idx %)) @*results))]
|
||||
(state/set-modal! (batch-delete-dialog pages false #(do
|
||||
(reset! *checks nil)
|
||||
(refresh-pages)))))))
|
||||
:class "fade-link"
|
||||
:small? true)]
|
||||
|
||||
[:div.search-wrap.flex.items-center.pl-2
|
||||
(let [search-fn (fn []
|
||||
(let [^js input (rum/deref *search-input)]
|
||||
(search-key (.-value input))
|
||||
(reset! *current-page 1)))
|
||||
reset-fn (fn []
|
||||
[:div.search-wrap.flex.items-center.pl-2
|
||||
(let [search-fn (fn []
|
||||
(let [^js input (rum/deref *search-input)]
|
||||
(set! (.-value input) "")
|
||||
(reset! *search-key nil)))]
|
||||
(search-key (.-value input))
|
||||
(reset! *current-page 1)))
|
||||
reset-fn (fn []
|
||||
(let [^js input (rum/deref *search-input)]
|
||||
(set! (.-value input) "")
|
||||
(reset! *search-key nil)))]
|
||||
|
||||
[(ui/button (ui/icon "search")
|
||||
:on-click search-fn
|
||||
:small? true)
|
||||
[:input.form-input {:placeholder (t :search/page-names)
|
||||
:on-key-up (fn [^js e]
|
||||
(let [^js target (.-target e)]
|
||||
(if (string/blank? (.-value target))
|
||||
(reset! *search-key nil)
|
||||
(cond
|
||||
(= 13 (.-keyCode e)) (search-fn)
|
||||
(= 27 (.-keyCode e)) (reset-fn)))))
|
||||
:ref *search-input
|
||||
:default-value ""}]
|
||||
[(ui/button (ui/icon "search")
|
||||
:on-click search-fn
|
||||
:small? true)
|
||||
[:input.form-input {:placeholder (t :search/page-names)
|
||||
:on-key-up (fn [^js e]
|
||||
(let [^js target (.-target e)]
|
||||
(if (string/blank? (.-value target))
|
||||
(reset! *search-key nil)
|
||||
(cond
|
||||
(= 13 (.-keyCode e)) (search-fn)
|
||||
(= 27 (.-keyCode e)) (reset-fn)))))
|
||||
:ref *search-input
|
||||
:default-value ""}]
|
||||
|
||||
(when (not (string/blank? @*search-key))
|
||||
[:a.cancel {:on-click reset-fn}
|
||||
(ui/icon "x")])])]]
|
||||
(when (not (string/blank? @*search-key))
|
||||
[:a.cancel {:on-click reset-fn}
|
||||
(ui/icon "x")])])]]
|
||||
|
||||
[:div.r.flex.items-center.justify-between
|
||||
[:div
|
||||
(ui/tippy
|
||||
{:html [:small (str (t :page/show-whiteboards) " ?")]
|
||||
:arrow true}
|
||||
[:a.button.whiteboard
|
||||
{:class (util/classnames [{:active (boolean @*whiteboard?)}])
|
||||
:on-click #(reset! *whiteboard? (not @*whiteboard?))}
|
||||
(ui/icon "whiteboard" {:extension? true :style {:fontSize ui/icon-size}})])]
|
||||
[:div
|
||||
(ui/tippy
|
||||
{:html [:small (str (t :page/show-journals) " ?")]
|
||||
:arrow true}
|
||||
[:a.button.journal
|
||||
{:class (util/classnames [{:active (boolean @*journal?)}])
|
||||
:on-click #(reset! *journal? (not @*journal?))}
|
||||
(ui/icon "calendar" {:size ui/icon-size})])]
|
||||
[:div.r.flex.items-center.justify-between
|
||||
[:div
|
||||
(ui/tippy
|
||||
{:html [:small (str (t :page/show-whiteboards) " ?")]
|
||||
:arrow true}
|
||||
[:a.button.whiteboard
|
||||
{:class (util/classnames [{:active (boolean @*whiteboard?)}])
|
||||
:on-click #(reset! *whiteboard? (not @*whiteboard?))}
|
||||
(ui/icon "whiteboard" {:extension? true :style {:fontSize ui/icon-size}})])]
|
||||
[:div
|
||||
(ui/tippy
|
||||
{:html [:small (str (t :page/show-journals) " ?")]
|
||||
:arrow true}
|
||||
[:a.button.journal
|
||||
{:class (util/classnames [{:active (boolean @*journal?)}])
|
||||
:on-click #(reset! *journal? (not @*journal?))}
|
||||
(ui/icon "calendar" {:size ui/icon-size})])]
|
||||
|
||||
[:div.paginates
|
||||
[:span.flex.items-center
|
||||
{:class (util/classnames [{:is-first (= 1 @*current-page)
|
||||
:is-last (= @*current-page total-pages)}])}
|
||||
(when has-prev?
|
||||
[:a.py-4.pr-2.fade-link.flex.items-center
|
||||
{:on-click #(to-page (dec @*current-page))}
|
||||
(ui/icon "caret-left") (str " " (t :paginates/prev))])
|
||||
[:span.opacity-60 (str @*current-page "/" total-pages)]
|
||||
(when has-next?
|
||||
[:a.py-4.pl-2.fade-link.flex.items-center
|
||||
{:on-click #(to-page (inc @*current-page))} (str (t :paginates/next) " ")
|
||||
(ui/icon "caret-right")])]]
|
||||
[:div.paginates
|
||||
(pagination :current @*current-page
|
||||
:total total-items
|
||||
:per-page per-page-num
|
||||
:on-change #(to-page %))]
|
||||
|
||||
(ui/dropdown-with-links
|
||||
(fn [{:keys [toggle-fn]}]
|
||||
[:a.button.fade-link
|
||||
{:on-click toggle-fn}
|
||||
(ui/icon "dots" {:size ui/icon-size})])
|
||||
[{:title (t :remove-orphaned-pages)
|
||||
:options {:on-click (fn []
|
||||
(let [orphaned-pages (model/get-orphaned-pages {})
|
||||
orphaned-pages? (seq orphaned-pages)]
|
||||
(if orphaned-pages?
|
||||
(state/set-modal!
|
||||
(batch-delete-dialog
|
||||
orphaned-pages true
|
||||
#(do
|
||||
(reset! *checks nil)
|
||||
(refresh-pages))))
|
||||
(notification/show! "Congratulations, no orphaned pages in your graph!" :success))))}
|
||||
:icon (ui/icon "file-x")}
|
||||
{:title (t :all-files)
|
||||
:options {:href (rfe/href :all-files)}
|
||||
:icon (ui/icon "files")}]
|
||||
{})]]
|
||||
(ui/dropdown-with-links
|
||||
(fn [{:keys [toggle-fn]}]
|
||||
[:a.button.fade-link
|
||||
{:on-click toggle-fn}
|
||||
(ui/icon "dots" {:size ui/icon-size})])
|
||||
[{:title (t :remove-orphaned-pages)
|
||||
:options {:on-click (fn []
|
||||
(let [orphaned-pages (model/get-orphaned-pages {})
|
||||
orphaned-pages? (seq orphaned-pages)]
|
||||
(if orphaned-pages?
|
||||
(state/set-modal!
|
||||
(batch-delete-dialog
|
||||
orphaned-pages true
|
||||
#(do
|
||||
(reset! *checks nil)
|
||||
(refresh-pages))))
|
||||
(notification/show! "Congratulations, no orphaned pages in your graph!" :success))))}
|
||||
:icon (ui/icon "file-x")}
|
||||
{:title (t :all-files)
|
||||
:options {:href (rfe/href :all-files)}
|
||||
:icon (ui/icon "files")}]
|
||||
{})]]
|
||||
|
||||
[:table.table-auto.cp__all_pages_table
|
||||
[:thead
|
||||
[:tr
|
||||
[:th.selector
|
||||
(checkbox-opt "all-pages-select-all"
|
||||
(= 1 @*indeterminate)
|
||||
{:on-change (fn []
|
||||
(let [indeterminate? (= -1 @*indeterminate)
|
||||
all? (= 1 @*indeterminate)]
|
||||
(doseq [{:block/keys [idx]} @*results]
|
||||
(swap! *checks assoc idx (or indeterminate? (not all?))))))
|
||||
:indeterminate (= -1 @*indeterminate)})]
|
||||
[:table.table-auto.cp__all_pages_table
|
||||
[:thead
|
||||
[:tr
|
||||
[:th.selector
|
||||
(checkbox-opt "all-pages-select-all"
|
||||
(= 1 @*indeterminate)
|
||||
{:on-change (fn []
|
||||
(let [indeterminate? (= -1 @*indeterminate)
|
||||
all? (= 1 @*indeterminate)]
|
||||
(doseq [{:block/keys [idx]} @*results]
|
||||
(swap! *checks assoc idx (or indeterminate? (not all?))))))
|
||||
:indeterminate (= -1 @*indeterminate)})]
|
||||
[:th.icon ""]
|
||||
(sortable-title (t :block/name) :block/name *sort-by-item *desc?)
|
||||
(when-not mobile?
|
||||
[(sortable-title (t :page/backlinks) :block/backlinks *sort-by-item *desc?)
|
||||
(sortable-title (t :page/created-at) :block/created-at *sort-by-item *desc?)
|
||||
(sortable-title (t :page/updated-at) :block/updated-at *sort-by-item *desc?)])]]
|
||||
|
||||
(sortable-title (t :block/name) :block/name *sort-by-item *desc?)
|
||||
(when-not mobile?
|
||||
[(sortable-title (t :page/backlinks) :block/backlinks *sort-by-item *desc?)
|
||||
(sortable-title (t :page/created-at) :block/created-at *sort-by-item *desc?)
|
||||
(sortable-title (t :page/updated-at) :block/updated-at *sort-by-item *desc?)])]]
|
||||
|
||||
[:tbody
|
||||
(for [{:block/keys [idx name created-at updated-at backlinks] :as page} @*results]
|
||||
(when-not (string/blank? name)
|
||||
[:tr {:key name}
|
||||
[:td.selector
|
||||
(checkbox-opt (str "label-" idx)
|
||||
(get @*checks idx)
|
||||
{:on-change (fn []
|
||||
(swap! *checks update idx not))})]
|
||||
|
||||
[:td.name [:a {:on-click (fn [e]
|
||||
[:tbody
|
||||
(for [{:block/keys [idx name created-at updated-at backlinks] :as page} @*results]
|
||||
(when-not (string/blank? name)
|
||||
[:tr {:key name}
|
||||
[:td.selector
|
||||
(checkbox-opt (str "label-" idx)
|
||||
(get @*checks idx)
|
||||
{:on-change (fn []
|
||||
(swap! *checks update idx not))})]
|
||||
[:td.icon.w-4.p-0.overflow-hidden
|
||||
(when-let [icon (get-in page [:block/properties :icon])]
|
||||
icon)]
|
||||
[:td.name [:a {:on-click (fn [e]
|
||||
(.preventDefault e)
|
||||
(let [repo (state/get-current-repo)]
|
||||
(when (gobj/get e "shiftKey")
|
||||
(state/sidebar-add-block!
|
||||
repo
|
||||
(:db/id page)
|
||||
:page))))
|
||||
:href (rfe/href :page {:name (:block/name page)})}
|
||||
(component-block/page-cp {} page)]]
|
||||
(let [repo (state/get-current-repo)]
|
||||
(when (gobj/get e "shiftKey")
|
||||
(state/sidebar-add-block!
|
||||
repo
|
||||
(:db/id page)
|
||||
:page))))
|
||||
:href (rfe/href :page {:name (:block/name page)})}
|
||||
(component-block/page-cp {} page)]]
|
||||
|
||||
(when-not mobile?
|
||||
[:td.backlinks [:span backlinks]])
|
||||
|
||||
(when-not mobile?
|
||||
(when-not mobile?
|
||||
[[:td.backlinks [:span backlinks]]
|
||||
[:td.created-at [:span (if created-at
|
||||
(date/int->local-time-2 created-at)
|
||||
"Unknown")]])
|
||||
(when-not mobile?
|
||||
"Unknown")]]
|
||||
[:td.updated-at [:span (if updated-at
|
||||
(date/int->local-time-2 updated-at)
|
||||
"Unknown")]])]))]]
|
||||
"Unknown")]]])]))]]
|
||||
|
||||
[:div.paginates
|
||||
[:span]
|
||||
[:span.flex.items-center
|
||||
(when has-prev?
|
||||
[:a.py-4.text-sm.fade-link.flex.items-center {:on-click #(to-page (dec @*current-page))}
|
||||
(ui/icon "caret-left") (str " " (t :paginates/prev))])
|
||||
(when has-next?
|
||||
[:a.py-4.pl-2.text-sm.fade-link.flex.items-center {:on-click #(to-page (inc @*current-page))} (str (t :paginates/next) " ")
|
||||
(ui/icon "caret-right")])]]]))]))
|
||||
[:div.flex.justify-end.py-4
|
||||
(pagination :current @*current-page
|
||||
:total total-items
|
||||
:per-page per-page-num
|
||||
:on-change #(to-page %))]])]))
|
||||
|
||||
@@ -195,27 +195,6 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.paginates {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 4px;
|
||||
|
||||
> span {
|
||||
color: var(--ls-primary-text-color);
|
||||
|
||||
&:last-child {
|
||||
a {
|
||||
user-select: none;
|
||||
|
||||
&:active {
|
||||
opacity: .6;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cp__vertical-menu-button {
|
||||
|
||||
@@ -11,17 +11,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
#search-wrapper svg {
|
||||
color: var(--ls-search-icon-color, #9fa6b2);
|
||||
opacity: 0.6;
|
||||
transition: .3s;
|
||||
}
|
||||
|
||||
#search-wrapper:hover svg, #search-wrapper:focus-within svg {
|
||||
color: var(--ls-link-text-hover-color, #4b5563);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
#search-wrapper {
|
||||
transition: .3s;
|
||||
padding-right: 12px;
|
||||
|
||||
@@ -440,7 +440,8 @@
|
||||
(row-with-button-action
|
||||
{:left-label (t :settings-page/customize-shortcuts)
|
||||
:button-label (t :settings-page/shortcut-settings)
|
||||
:on-click #((state/close-settings!)
|
||||
:on-click (fn []
|
||||
(state/close-settings!)
|
||||
(route-handler/redirect! {:to :shortcut-setting}))
|
||||
:-for "customize_shortcuts"}))
|
||||
|
||||
|
||||
@@ -69,30 +69,30 @@
|
||||
:or {insert-command? true notification? true}}]
|
||||
(go
|
||||
(let [{:keys [page-name properties abstract-note]} (extractor/extract item)]
|
||||
(when-not (str/blank? page-name)
|
||||
(if (db/page-exists? (str/lower-case page-name))
|
||||
(if (setting/setting :overwrite-mode?)
|
||||
(page-handler/delete!
|
||||
page-name
|
||||
(fn [] (create-page page-name properties)))
|
||||
(editor-handler/api-insert-new-block!
|
||||
""
|
||||
{:page page-name
|
||||
:properties properties}))
|
||||
(create-page page-name properties))
|
||||
|
||||
(if (db/page-exists? (str/lower-case page-name))
|
||||
(if (setting/setting :overwrite-mode?)
|
||||
(page-handler/delete!
|
||||
page-name
|
||||
(fn [] (create-page page-name properties)))
|
||||
(editor-handler/api-insert-new-block!
|
||||
""
|
||||
{:page page-name
|
||||
:properties properties}))
|
||||
(create-page page-name properties))
|
||||
(create-abstract-note! page-name abstract-note)
|
||||
|
||||
(create-abstract-note! page-name abstract-note)
|
||||
(<! (add page-name :attachments item))
|
||||
|
||||
(<! (add page-name :attachments item))
|
||||
(<! (add page-name :notes item))
|
||||
|
||||
(<! (add page-name :notes item))
|
||||
(when insert-command?
|
||||
(handle-command-zotero block-dom-id page-name)
|
||||
(editor-handler/save-current-block!))
|
||||
|
||||
(when insert-command?
|
||||
(handle-command-zotero block-dom-id page-name)
|
||||
(editor-handler/save-current-block!))
|
||||
|
||||
(when notification?
|
||||
(notification/show! (str "Successfully added zotero item to page " page-name) :success))))))
|
||||
(when notification?
|
||||
(notification/show! (str "Successfully added zotero item to page " page-name) :success)))))))
|
||||
|
||||
(defn add-all [progress]
|
||||
(go
|
||||
|
||||
@@ -111,7 +111,7 @@
|
||||
(protocol/transact-blocks! engine data)))
|
||||
|
||||
(defn- transact-pages!
|
||||
[repo data]
|
||||
[repo data]
|
||||
(when-let [engine (get-engine repo)]
|
||||
(protocol/transact-pages! engine data)))
|
||||
|
||||
@@ -216,6 +216,7 @@
|
||||
(contains? pages-to-add-set (:db/id page))) pages-result)
|
||||
(map (fn [p] (or (:block/original-name p)
|
||||
(:block/name p))))
|
||||
(remove string/blank?)
|
||||
(map search-db/original-page-name->index))
|
||||
pages-to-remove-set (->> (remove :added pages)
|
||||
(map :v))
|
||||
@@ -246,7 +247,7 @@
|
||||
{:blocks-to-remove-set blocks-to-remove-set
|
||||
:blocks-to-add blocks-to-add})))
|
||||
|
||||
(defn- get-direct-blocks-and-pages
|
||||
(defn- get-direct-blocks-and-pages
|
||||
[tx-report]
|
||||
(let [data (:tx-data tx-report)
|
||||
datoms (filter
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
|
||||
(defn- sanitize
|
||||
[content]
|
||||
(util/search-normalize content (state/enable-search-remove-accents?)))
|
||||
(some-> content
|
||||
(util/search-normalize (state/enable-search-remove-accents?))))
|
||||
|
||||
(defn- max-len
|
||||
[]
|
||||
@@ -22,10 +23,11 @@
|
||||
"Convert a block to the index for searching"
|
||||
[{:block/keys [uuid page content] :as block}]
|
||||
(when-not (> (count content) (max-len))
|
||||
{:id (:db/id block)
|
||||
:uuid (str uuid)
|
||||
:page page
|
||||
:content (sanitize content)}))
|
||||
(when-not (string/blank? content)
|
||||
{:id (:db/id block)
|
||||
:uuid (str uuid)
|
||||
:page page
|
||||
:content (sanitize content)})))
|
||||
|
||||
(defn page->index
|
||||
"Convert a page name to the index for searching (page content level)
|
||||
@@ -33,11 +35,12 @@
|
||||
[{:block/keys [uuid original-name] :as page}]
|
||||
(when-let [content (some-> (:block/file page)
|
||||
(:file/content))]
|
||||
(when-not (> (count content) (* (max-len) 10))
|
||||
{:id (:db/id page)
|
||||
:uuid (str uuid)
|
||||
;; Add page name to the index
|
||||
:content (sanitize (str "$pfts_f6ld>$ " original-name " $<pfts_f6ld$ " content))})))
|
||||
(when-not (string/blank? original-name)
|
||||
(when-not (> (count content) (* (max-len) 10))
|
||||
{:id (:db/id page)
|
||||
:uuid (str uuid)
|
||||
;; Add page name to the index
|
||||
:content (sanitize (str "$pfts_f6ld>$ " original-name " $<pfts_f6ld$ " content))}))))
|
||||
|
||||
(defn build-blocks-indice
|
||||
;; TODO: Remove repo effects fns further up the call stack. db fns need standardization on taking connection
|
||||
@@ -48,7 +51,7 @@
|
||||
(remove nil?)
|
||||
(bean/->js)))
|
||||
|
||||
(defn build-pages-indice
|
||||
(defn build-pages-indice
|
||||
[repo]
|
||||
(->> (db/get-all-pages repo)
|
||||
(map #(db/entity (:db/id %))) ;; get full file-content
|
||||
@@ -70,8 +73,9 @@
|
||||
indice))
|
||||
|
||||
(defn original-page-name->index
|
||||
[p] {:name (util/search-normalize p (state/enable-search-remove-accents?))
|
||||
:original-name p})
|
||||
[p]
|
||||
{:name (util/search-normalize p (state/enable-search-remove-accents?))
|
||||
:original-name p})
|
||||
|
||||
(defn make-pages-title-indice!
|
||||
"Build a page title indice from scratch.
|
||||
|
||||
@@ -384,14 +384,4 @@ html.is-mobile {
|
||||
background: var(--ls-primary-background-color);
|
||||
content: " ";
|
||||
}
|
||||
|
||||
&.highlight {
|
||||
color: var(--ls-selection-text-color);
|
||||
border-color: var(--ls-selection-background-color);
|
||||
|
||||
&:before {
|
||||
opacity: 0.5;
|
||||
background: var(--ls-selection-background-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
5041
static/yarn.lock
Normal file
5041
static/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@@ -36,6 +36,7 @@ export const ZoomMenu = observer(function ZoomMenu(): JSX.Element {
|
||||
className="tl-menu-item"
|
||||
onSelect={preventEvent}
|
||||
onClick={app.api.zoomToSelection}
|
||||
disabled={app.selectedShapesArray.length === 0}
|
||||
>
|
||||
Zoom to selection
|
||||
<div className="tl-menu-right-slot">
|
||||
|
||||
@@ -4,6 +4,9 @@ import type { Shape } from '../lib'
|
||||
|
||||
export function useQuickAdd() {
|
||||
return React.useCallback<TLReactCallbacks<Shape>['onCanvasDBClick']>(async app => {
|
||||
app.selectTool('logseq-portal').selectedTool.transition('creating')
|
||||
// Give a timeout so that the quick add input will not be blurred too soon
|
||||
setTimeout(() => {
|
||||
app.selectTool('logseq-portal').selectedTool.transition('creating')
|
||||
}, 100)
|
||||
}, [])
|
||||
}
|
||||
|
||||
@@ -390,6 +390,12 @@ export class LogseqPortalShape extends TLBoxShape<LogseqPortalShapeProps> {
|
||||
}
|
||||
}, [isEditing, this.props.collapsed])
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isCreating) {
|
||||
app.viewport.zoomToBounds({ ...this.bounds, minY: this.bounds.maxY + 25 })
|
||||
}
|
||||
}, [app.viewport.currentView.height])
|
||||
|
||||
const onPageNameChanged = React.useCallback((id: string) => {
|
||||
this.initialHeightCalculated = false
|
||||
const blockType = validUUID(id) ? 'B' : 'P'
|
||||
|
||||
@@ -32,9 +32,6 @@ export class CreatingState extends TLToolState<
|
||||
this.app.currentPage.addShapes(shape)
|
||||
this.app.setEditingShape(shape)
|
||||
this.app.setSelectedShapes([shape])
|
||||
if (this.app.viewport.camera.zoom < 0.8 || this.app.viewport.camera.zoom > 1.2) {
|
||||
this.app.api.resetZoomToCursor()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -95,6 +95,10 @@ html[data-theme='light'] {
|
||||
.tl-menu-icon {
|
||||
@apply absolute left-4 text-base opacity-50;
|
||||
}
|
||||
|
||||
&[data-disabled] {
|
||||
@apply opacity-50 pointer-events-none;
|
||||
}
|
||||
}
|
||||
|
||||
#tl-zoom {
|
||||
|
||||
@@ -153,7 +153,7 @@ export class TLApi<S extends TLShape = TLShape, K extends TLEventMap = TLEventMa
|
||||
|
||||
resetZoomToCursor = (): this => {
|
||||
const viewport = this.app.viewport
|
||||
viewport.update({
|
||||
viewport.animateCamera({
|
||||
zoom: 1,
|
||||
point: Vec.sub(this.app.inputs.originScreenPoint, this.app.inputs.originPoint),
|
||||
})
|
||||
|
||||
@@ -3,6 +3,14 @@ import { action, computed, makeObservable, observable } from 'mobx'
|
||||
import { FIT_TO_SCREEN_PADDING, ZOOM_UPDATE_FACTOR } from '../constants'
|
||||
import type { TLBounds } from '../types'
|
||||
|
||||
const ease = (x: number) => {
|
||||
return -(Math.cos(Math.PI * x) - 1) / 2
|
||||
}
|
||||
|
||||
const elapsedProgress = (t: number, duration = 100) => {
|
||||
return ease(Vec.clamp(t / duration, 0, 1))
|
||||
}
|
||||
|
||||
export class TLViewport {
|
||||
constructor() {
|
||||
makeObservable(this)
|
||||
@@ -73,54 +81,99 @@ export class TLViewport {
|
||||
return Vec.mul(Vec.add(point, camera.point), camera.zoom)
|
||||
}
|
||||
|
||||
onZoom = (point: number[], zoom: number): this => {
|
||||
return this.pinchZoom(point, [0, 0], zoom)
|
||||
onZoom = (point: number[], zoom: number, animate = false): this => {
|
||||
return this.pinchZoom(point, [0, 0], zoom, animate)
|
||||
}
|
||||
|
||||
/**
|
||||
* Pinch to a new zoom level, possibly together with a pan.
|
||||
*
|
||||
* @param point The current point under the cursor.
|
||||
* @param delta The movement delta.
|
||||
* @param point The current point under the cursor in the screen space. Zoom will be transformed
|
||||
* around this point.
|
||||
* @param delta The movement delta in the screen space
|
||||
* @param zoom The new zoom level
|
||||
*/
|
||||
pinchZoom = (point: number[], delta: number[], zoom: number): this => {
|
||||
pinchZoom = (point: number[], delta: number[], zoom: number, animate = false): this => {
|
||||
const { camera } = this
|
||||
|
||||
const nextPoint = Vec.sub(camera.point, Vec.div(delta, camera.zoom))
|
||||
zoom = Vec.clamp(zoom, TLViewport.minZoom, TLViewport.maxZoom)
|
||||
const p0 = Vec.sub(Vec.div(point, camera.zoom), nextPoint)
|
||||
const p1 = Vec.sub(Vec.div(point, zoom), nextPoint)
|
||||
return this.update({ point: Vec.toFixed(Vec.add(nextPoint, Vec.sub(p1, p0))), zoom })
|
||||
const p0 = Vec.div(point, camera.zoom)
|
||||
const p1 = Vec.div(point, zoom)
|
||||
|
||||
const newPoint = Vec.toFixed(Vec.add(nextPoint, Vec.sub(p1, p0)))
|
||||
|
||||
if (animate) {
|
||||
this.animateCamera({ point: newPoint, zoom })
|
||||
} else {
|
||||
this.update({ point: newPoint, zoom })
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
setZoom = (zoom: number) => {
|
||||
setZoom = (zoom: number, animate = false) => {
|
||||
const { bounds } = this
|
||||
const center = [bounds.width / 2, bounds.height / 2]
|
||||
this.onZoom(center, zoom)
|
||||
this.onZoom(center, zoom, animate)
|
||||
}
|
||||
|
||||
zoomIn = () => {
|
||||
const { camera } = this
|
||||
this.setZoom(camera.zoom / ZOOM_UPDATE_FACTOR)
|
||||
this.setZoom(camera.zoom / ZOOM_UPDATE_FACTOR, true)
|
||||
}
|
||||
|
||||
zoomOut = () => {
|
||||
const { camera, bounds } = this
|
||||
this.setZoom(camera.zoom * ZOOM_UPDATE_FACTOR)
|
||||
const { camera } = this
|
||||
this.setZoom(camera.zoom * ZOOM_UPDATE_FACTOR, true)
|
||||
}
|
||||
|
||||
resetZoom = (): this => {
|
||||
const {
|
||||
bounds,
|
||||
camera: { zoom, point },
|
||||
} = this
|
||||
const center = [bounds.width / 2, bounds.height / 2]
|
||||
const p0 = Vec.sub(Vec.div(center, zoom), point)
|
||||
const p1 = Vec.sub(Vec.div(center, 1), point)
|
||||
return this.update({ point: Vec.toFixed(Vec.add(point, Vec.sub(p1, p0))), zoom: 1 })
|
||||
this.setZoom(1, true)
|
||||
return this
|
||||
}
|
||||
|
||||
zoomToBounds = ({ width, height, minX, minY }: TLBounds): this => {
|
||||
/**
|
||||
* Animate the camera to the given position
|
||||
*/
|
||||
animateCamera = ({ point, zoom }: { point: number[]; zoom: number }) => {
|
||||
return this.animateToViewport({
|
||||
minX: -point[0],
|
||||
minY: -point[1],
|
||||
maxX: this.bounds.width / zoom - point[0],
|
||||
maxY: this.bounds.height / zoom - point[1],
|
||||
width: this.bounds.width / zoom,
|
||||
height: this.bounds.height / zoom,
|
||||
})
|
||||
}
|
||||
|
||||
animateToViewport = (view: TLBounds) => {
|
||||
const startTime = performance.now()
|
||||
const oldView = { ...this.currentView }
|
||||
|
||||
const step = () => {
|
||||
const elapsed = performance.now() - startTime
|
||||
const progress = elapsedProgress(elapsed) // 0 ~ 1
|
||||
const next = {
|
||||
minX: oldView.minX + (view.minX - oldView.minX) * progress,
|
||||
minY: oldView.minY + (view.minY - oldView.minY) * progress,
|
||||
maxX: oldView.maxX + (view.maxX - oldView.maxX) * progress,
|
||||
maxY: oldView.maxY + (view.maxY - oldView.maxY) * progress,
|
||||
}
|
||||
|
||||
const point = [-next.minX, -next.minY]
|
||||
const zoom = this.bounds.width / (next.maxX - next.minX)
|
||||
|
||||
this.update({ point, zoom })
|
||||
if (progress < 1) {
|
||||
requestAnimationFrame(step)
|
||||
}
|
||||
}
|
||||
|
||||
step()
|
||||
}
|
||||
|
||||
zoomToBounds = ({ width, height, minX, minY }: TLBounds) => {
|
||||
const { bounds, camera } = this
|
||||
let zoom = Math.min(
|
||||
(bounds.width - FIT_TO_SCREEN_PADDING) / width,
|
||||
@@ -137,6 +190,8 @@ export class TLViewport {
|
||||
(bounds.width - width * zoom) / 2 / zoom,
|
||||
(bounds.height - height * zoom) / 2 / zoom,
|
||||
]
|
||||
return this.update({ point: Vec.add([-minX, -minY], delta), zoom })
|
||||
|
||||
const point = Vec.add([-minX, -minY], delta)
|
||||
this.animateCamera({ point, zoom })
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user