enhance(mobile): fixed position of head-bar & mobile-bar (#3360)

enhance(mobile): fixed position of head-bar & mobile-bar
Co-authored-by: charlie <xyhp915@qq.com>
pull/3355/head^2
Tienson Qin 2021-12-07 13:40:47 +08:00 committed by GitHub
parent 74c2606300
commit d9452bd739
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 201 additions and 144 deletions

View File

@ -9,6 +9,8 @@
--ls-scrollbar-width: 6px; --ls-scrollbar-width: 6px;
--ls-border-radius-low: 4px; --ls-border-radius-low: 4px;
--ls-border-radius-medium: 8px; --ls-border-radius-medium: 8px;
--ls-headbar-height: 3rem;
--ls-headbar-inner-top-padding: 0px;
--ls-left-sidebar-width: 240px; --ls-left-sidebar-width: 240px;
--ls-left-sidebar-nav-btn-size: 38px; --ls-left-sidebar-nav-btn-size: 38px;
} }

View File

@ -8,6 +8,7 @@
[frontend.components.search :as search] [frontend.components.search :as search]
[frontend.components.svg :as svg] [frontend.components.svg :as svg]
[frontend.config :as config] [frontend.config :as config]
[frontend.handler.notification :as notification]
[frontend.db :as db] [frontend.db :as db]
[frontend.extensions.zotero :as zotero] [frontend.extensions.zotero :as zotero]
[frontend.handler.editor :as editor-handler :refer [get-state]] [frontend.handler.editor :as editor-handler :refer [get-state]]
@ -217,91 +218,99 @@
(rum/defc mobile-bar < rum/reactive (rum/defc mobile-bar < rum/reactive
[parent-state parent-id] [parent-state parent-id]
[:div#mobile-editor-toolbar.bg-base-2.fix-ios-fixed-bottom (let [vw-state (state/sub :ui/visual-viewport-state)
[:div.flex.justify-around.w-full vw-pending? (state/sub :ui/visual-viewport-pending?)]
[:div [:div#mobile-editor-toolbar.bg-base-2
[:button.bottom-action {:style {:bottom (if (and vw-state)
{:on-mouse-down (fn [e] (- (.-clientHeight js/document.documentElement)
(util/stop e) (:height vw-state)
(editor-handler/indent-outdent true))} (:offset-top vw-state))
(ui/icon "arrow-bar-right" 0)}
{:style {:fontSize ui/icon-size}})]] :class (util/classnames [{:is-vw-pending (boolean vw-pending?)}])}
[:div [:div.flex.justify-around.w-full
[:button.bottom-action [:div
{:on-mouse-down (fn [e] [:button.bottom-action
(util/stop e) {:on-mouse-down (fn [e]
(editor-handler/indent-outdent false))} (util/stop e)
(ui/icon "arrow-bar-left" (editor-handler/indent-outdent true))}
{:style {:fontSize ui/icon-size}})]] (ui/icon "arrow-bar-right"
[:div {:style {:fontSize ui/icon-size}})]]
[:button.bottom-action [:div
{:on-mouse-down (fn [e] [:button.bottom-action
(util/stop e) {:on-mouse-down (fn [e]
((editor-handler/move-up-down true)))} (util/stop e)
(ui/icon "arrow-bar-to-up" (editor-handler/indent-outdent false))}
{:style {:fontSize ui/icon-size}})]] (ui/icon "arrow-bar-left"
[:div {:style {:fontSize ui/icon-size}})]]
[:button.bottom-action [:div
{:on-mouse-down (fn [e] [:button.bottom-action
(util/stop e) {:on-mouse-down (fn [e]
((editor-handler/move-up-down false)))} (util/stop e)
(ui/icon "arrow-bar-to-down" ((editor-handler/move-up-down true)))}
{:style {:fontSize ui/icon-size}})]] (ui/icon "arrow-bar-to-up"
[:div {:style {:fontSize ui/icon-size}})]]
[:button.bottom-action [:div
{:on-mouse-down (fn [e] [:button.bottom-action
(util/stop e) {:on-mouse-down (fn [e]
(commands/simple-insert! parent-id "\n" (util/stop e)
{:forward-pos 1}) ((editor-handler/move-up-down false)))}
;; TODO: should we add this focus step to `simple-insert!`? (ui/icon "arrow-bar-to-down"
(when-let [input (gdom/getElement parent-id)] {:style {:fontSize ui/icon-size}})]]
(.focus input)))} [:div
(ui/icon "arrow-back" [:button.bottom-action
{:style {:fontSize ui/icon-size}})]] {:on-mouse-down (fn [e]
[:div (util/stop e)
[:button.bottom-action (commands/simple-insert! parent-id "\n"
{:on-mouse-down (fn [e] {:forward-pos 1})
(util/stop e) ;; TODO: should we add this focus step to `simple-insert!`?
(editor-handler/cycle-todo!))} (when-let [input (gdom/getElement parent-id)]
(ui/icon "checkbox" (.focus input)))}
{:style {:fontSize ui/icon-size}})]] (ui/icon "arrow-back"
[:div {:style {:fontSize ui/icon-size}})]]
[:button.bottom-action [:div
{:on-mouse-down (fn [e] [:button.bottom-action
(util/stop e) {:on-mouse-down (fn [e]
(commands/simple-insert! (util/stop e)
parent-id "[[]]" (editor-handler/cycle-todo!))}
{:backward-pos 2 (ui/icon "checkbox"
:check-fn (fn [_ _ new-pos] {:style {:fontSize ui/icon-size}})]]
(reset! commands/*slash-caret-pos new-pos) [:div
(commands/handle-step [:editor/search-page]))}) [:button.bottom-action
(when-let [input (gdom/getElement parent-id)] {:on-mouse-down (fn [e]
(.focus input)))} (util/stop e)
(ui/icon "brackets" (commands/simple-insert!
{:style {:fontSize ui/icon-size}})]] parent-id "[[]]"
[:div {:backward-pos 2
[:button.bottom-action :check-fn (fn [_ _ new-pos]
{:on-mouse-down (fn [e] (reset! commands/*slash-caret-pos new-pos)
(util/stop e) (commands/handle-step [:editor/search-page]))})
(commands/simple-insert! (when-let [input (gdom/getElement parent-id)]
parent-id "(())" (.focus input)))}
{:backward-pos 2 (ui/icon "brackets"
:check-fn (fn [_ _ new-pos] {:style {:fontSize ui/icon-size}})]]
(reset! commands/*slash-caret-pos new-pos) [:div
(commands/handle-step [:editor/search-block]))}) [:button.bottom-action
(when-let [input (gdom/getElement parent-id)] {:on-mouse-down (fn [e]
(.focus input)))} (util/stop e)
(ui/icon "parentheses" (commands/simple-insert!
{:style {:fontSize ui/icon-size}})]] parent-id "(())"
[:div {:backward-pos 2
[:button.bottom-action :check-fn (fn [_ _ new-pos]
{:on-mouse-down (fn [e] (reset! commands/*slash-caret-pos new-pos)
(util/stop e) (commands/handle-step [:editor/search-block]))})
(commands/simple-insert! parent-id "/" {}) (when-let [input (gdom/getElement parent-id)]
(when-let [input (gdom/getElement parent-id)] (.focus input)))}
(.focus input)))} (ui/icon "parentheses"
(ui/icon "command" {:style {:fontSize ui/icon-size}})]]
{:style {:fontSize ui/icon-size}})]]]]) [:div
[:button.bottom-action
{:on-mouse-down (fn [e]
(util/stop e)
(commands/simple-insert! parent-id "/" {})
(when-let [input (gdom/getElement parent-id)]
(.focus input)))}
(ui/icon "command"
{:style {:fontSize ui/icon-size}})]]]]))
(rum/defcs input < rum/reactive (rum/defcs input < rum/reactive
(rum/local {} ::input-value) (rum/local {} ::input-value)

View File

@ -1,15 +1,15 @@
#mobile-editor-toolbar { #mobile-editor-toolbar {
position: fixed; position: fixed;
bottom: 0; bottom: 0;
width: 100%;
left: 0; left: 0;
width: 100%;
justify-content: center; justify-content: center;
height: 2.5rem; height: 2.5rem;
display: flex; display: flex;
align-items: center; align-items: center;
z-index: 9999; z-index: 9999;
transition: top 0.3s; transition: none;
button { button {
padding: 10px; padding: 10px;

View File

@ -161,6 +161,8 @@
(let [repos (->> (state/sub [:me :repos]) (let [repos (->> (state/sub [:me :repos])
(remove #(= (:url %) config/local-repo))) (remove #(= (:url %) config/local-repo)))
electron-mac? (and util/mac? (util/electron?)) electron-mac? (and util/mac? (util/electron?))
vw-state (state/sub :ui/visual-viewport-state)
vw-pending? (state/sub :ui/visual-viewport-pending?)
show-open-folder? (and (or (nfs/supported?) show-open-folder? (and (or (nfs/supported?)
(mobile-util/is-native-platform?)) (mobile-util/is-native-platform?))
(empty? repos) (empty? repos)
@ -168,15 +170,17 @@
refreshing? (state/sub :nfs/refreshing?)] refreshing? (state/sub :nfs/refreshing?)]
(rum/with-context [[t] i18n/*tongue-context*] (rum/with-context [[t] i18n/*tongue-context*]
[:div.cp__header#head [:div.cp__header#head
{:class (cond electron-mac? "electron-mac" {:class (util/classnames [{:electron-mac electron-mac?
(mobile-util/native-ios?) "native-ios" :native-ios (mobile-util/native-ios?)
(mobile-util/native-android?) "native-android") :native-android (mobile-util/native-android?)
:is-vw-pending (boolean vw-pending?)}])
:on-double-click (fn [^js e] :on-double-click (fn [^js e]
(when-let [target (.-target e)] (when-let [target (.-target e)]
(when (and (util/electron?) (when (and (util/electron?)
(or (.. target -classList (contains "cp__header")))) (or (.. target -classList (contains "cp__header"))))
(js/window.apis.toggleMaxOrMinActiveWindow)))) (js/window.apis.toggleMaxOrMinActiveWindow))))
:style {:fontSize 50}} :style {:fontSize 50
:transform (str "translateY(" (or (:offset-top vw-state) 0) "px)")}}
[:div.l.flex [:div.l.flex
(left-menu-button {:on-click (fn [] (left-menu-button {:on-click (fn []
(open-fn) (open-fn)

View File

@ -1,8 +1,10 @@
.cp__header { .cp__header {
@apply shadow z-10 h-12; @apply shadow z-10;
-webkit-app-region: drag; -webkit-app-region: drag;
padding-right: 0.5rem; padding-right: 0.5rem;
padding-top: var(--ls-headbar-inner-top-padding);
height: calc(var(--ls-headbar-height) + var(--ls-headbar-inner-top-padding));
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
@ -170,3 +172,23 @@ a.button {
.is-mac.is-electron :is(.cp__header, .cp__right-sidebar-topbar) :is(button, .button, a) { .is-mac.is-electron :is(.cp__header, .cp__right-sidebar-topbar) :is(button, .button, a) {
cursor: default !important; cursor: default !important;
} }
html.is-native-ios,
html.is-ios.is-safari {
#main-container {
padding-top: 20px;
}
.cp__header {
position: fixed !important;
background-color: var(--ls-primary-background-color);
}
.is-vw-pending {
display: none !important;
}
}
html.is-native-ios {
--ls-headbar-inner-top-padding: 36px;
}

View File

@ -568,8 +568,9 @@
:close-fn close-fn :close-fn close-fn
:route-match route-match}) :route-match route-match})
[:div.#app-container.h-screen.flex {:style {:padding-top (ui/main-content-top-padding)}} [:div.#app-container
[:div.flex-1.h-full.flex.flex-col#left-container.relative {:style {:padding-top (ui/main-content-top-padding)}}
[:div#left-container
{:class (if (state/sub :ui/sidebar-open?) "overflow-hidden" "w-full")} {:class (if (state/sub :ui/sidebar-open?) "overflow-hidden" "w-full")}
(header/header {:open-fn open-fn (header/header {:open-fn open-fn
:white? white? :white? white?

View File

@ -1,8 +1,8 @@
@supports(padding: max(0px)) { @supports (padding: max(0px)) {
.post { .post {
padding-left: max(12px, env(safe-area-inset-left)); padding-left: max(12px, env(safe-area-inset-left));
padding-right: max(12px, env(safe-area-inset-right)); padding-right: max(12px, env(safe-area-inset-right));
} }
} }
#app-container { #app-container {
@ -21,9 +21,14 @@
} }
#app-container { #app-container {
display: flex;
flex: 0 0 100%; flex: 0 0 100%;
} }
#left-container {
@apply flex flex-1 flex-col relative h-screen;
}
#main-container { #main-container {
position: relative; position: relative;
height: 100%; height: 100%;
@ -44,6 +49,12 @@ html.is-mobile {
#main-content.is-left-sidebar-open { #main-content.is-left-sidebar-open {
padding-left: 0; padding-left: 0;
} }
.left-sidebar-inner {
> .wrap {
padding-top: 64px;
}
}
} }
#left-sidebar { #left-sidebar {
@ -64,8 +75,8 @@ html.is-mobile {
#left-bar { #left-bar {
background-color: var(--ls-primary-background-color); background-color: var(--ls-primary-background-color);
width: 70vw; width: 70vw;
max-width: 300px; max-width: 300px;
> .head-wrap { > .head-wrap {
background-color: var(--ls-search-background-color); background-color: var(--ls-search-background-color);
@ -100,10 +111,11 @@ html.is-mobile {
overflow: auto; overflow: auto;
> .wrap { > .wrap {
padding-top: 24px; padding-top: 24px;
@screen md {
padding-top: 60px; @screen md {
} padding-top: 55px;
}
} }
.dropdown-wrapper { .dropdown-wrapper {

View File

@ -62,12 +62,6 @@
#(when system-theme? #(when system-theme?
(ui/setup-system-theme-effect!)) (ui/setup-system-theme-effect!))
[system-theme?]) [system-theme?])
(rum/use-effect!
#(when (mobile-util/native-ios?)
(ui/setup-patch-ios-fixed-bottom-position!))
[edit?])
[:div [:div
{:class (str theme "-theme") {:class (str theme "-theme")
:on-click on-click} :on-click on-click}

View File

@ -17,7 +17,9 @@
(ui/inject-document-devices-envs!) (ui/inject-document-devices-envs!)
(ui/inject-dynamic-style-node!) (ui/inject-dynamic-style-node!)
(plugin-handler/host-mounted!) (plugin-handler/host-mounted!)
(let [teardown-fn (ui/setup-active-keystroke!)] (let [teardown-fn (comp
(ui/setup-active-keystroke!)
(ui/setup-patch-ios-visual-viewport-state!))]
(assoc state ::teardown teardown-fn))) (assoc state ::teardown teardown-fn)))
:will-unmount (fn [state] :will-unmount (fn [state]
(let [teardown (::teardown state)] (let [teardown (::teardown state)]

View File

@ -82,6 +82,8 @@
:ui/shortcut-tooltip? (if (false? (storage/get :ui/shortcut-tooltip?)) :ui/shortcut-tooltip? (if (false? (storage/get :ui/shortcut-tooltip?))
false false
true) true)
:ui/visual-viewport-pending? false
:ui/visual-viewport-state nil
:document/mode? document-mode? :document/mode? document-mode?
@ -1578,3 +1580,11 @@
(defn get-last-key-code (defn get-last-key-code
[] []
(:editor/last-key-code @state)) (:editor/last-key-code @state))
(defn set-visual-viewport-state
[input]
(set-state! :ui/visual-viewport-state input))
(defn get-visual-viewport-state
[]
(:ui/visual-viewport-state @state))

View File

@ -50,7 +50,6 @@
:else :else
0)) 0))
(defonce icon-size (if (mobile-util/is-native-platform?) 23 20)) (defonce icon-size (if (mobile-util/is-native-platform?) 23 20))
(rum/defc ls-textarea (rum/defc ls-textarea
@ -305,35 +304,37 @@
(.appendChild js/document.head node)) (.appendChild js/document.head node))
style))) style)))
(defn setup-patch-ios-fixed-bottom-position! (defn setup-patch-ios-visual-viewport-state!
"fix a common issue about ios webpage viewport
when soft keyboard setup"
[] []
(when (and (when-let [^js vp (and (or (and (util/mobile?) (util/safari?))
(util/ios?) (mobile-util/native-ios?))
(not (nil? js/window.visualViewport))) js/window.visualViewport)]
(let [viewport js/visualViewport (let [raf-pending? (atom false)
style (get-dynamic-style-node)
sheet (.-sheet style)
raf-pending? (atom false)
set-raf-pending! #(reset! raf-pending? %) set-raf-pending! #(reset! raf-pending? %)
handler on-viewport-changed
(fn [] (fn []
(when-not @raf-pending? (let [update-vw-state
(let [f (fn [] (util/debounce 20
(set-raf-pending! false) (fn []
(let [vh (+ (.-offsetTop viewport) (.-height viewport)) (state/set-visual-viewport-state {:height (.-height vp)
rule (.. sheet -rules (item 0)) :page-top (.-pageTop vp)
set-top #(set! (.. rule -style -top) (str % "px"))] :offset-top (.-offsetTop vp)})
(set-top vh)))] (state/set-state! :ui/visual-viewport-pending? false)))]
(set-raf-pending! true) (when-not @raf-pending?
(js/window.requestAnimationFrame f))))] (let [f (fn []
(.insertRule sheet ".fix-ios-fixed-bottom {bottom:unset !important; transform: translateY(-100%); top: 100vh;}") (set-raf-pending! false)
(.addEventListener viewport "resize" handler) (update-vw-state))]
(.addEventListener viewport "scroll" handler) (set-raf-pending! true)
(state/set-state! :ui/visual-viewport-pending? true)
(js/window.requestAnimationFrame f)))))]
(.addEventListener vp "resize" on-viewport-changed)
(.addEventListener vp "scroll" on-viewport-changed)
(fn [] (fn []
(.removeEventListener viewport "resize" handler) (.removeEventListener vp "resize" on-viewport-changed)
(.removeEventListener viewport "scroll" handler))))) (.removeEventListener vp "scroll" on-viewport-changed)
(state/set-visual-viewport-state nil)))))
(defn setup-system-theme-effect! (defn setup-system-theme-effect!
[] []
@ -471,14 +472,14 @@
(str/split #" |\+")) (str/split #" |\+"))
sequence)] sequence)]
[:span.keyboard-shortcut [:span.keyboard-shortcut
(map-indexed (fn [i key] (map-indexed (fn [i key]
[:code {:key i} [:code {:key i}
;; Display "cmd" rather than "meta" to the user to describe the Mac ;; Display "cmd" rather than "meta" to the user to describe the Mac
;; mod key, because that's what the Mac keyboards actually say. ;; mod key, because that's what the Mac keyboards actually say.
(if (or (= :meta key) (= "meta" key)) (if (or (= :meta key) (= "meta" key))
(util/meta-key-name) (util/meta-key-name)
(name key))]) (name key))])
sequence)])) sequence)]))
(defn keyboard-shortcut-from-config [shortcut-name] (defn keyboard-shortcut-from-config [shortcut-name]
(let [default-binding (:binding (get shortcut-config/all-default-keyboard-shortcuts shortcut-name)) (let [default-binding (:binding (get shortcut-config/all-default-keyboard-shortcuts shortcut-name))