Browse Source

frontend: popup input panel when input acquired

wangweimin 4 years ago
parent
commit
63e5aa8919
4 changed files with 186 additions and 20 deletions
  1. 42 6
      pywebio/html/css/app.css
  2. 19 13
      pywebio/html/index.html
  3. 7 1
      webiojs/src/handlers/input.ts
  4. 118 0
      webiojs/src/ui.ts

+ 42 - 6
pywebio/html/css/app.css

@@ -1,7 +1,47 @@
+.pywebio {
+    min-height: calc(100vh);
+}
+
 .container {
-    margin: 45px auto;
+    margin-top: 0;
     max-width: 880px;
-    min-height: calc(100vh - 70px);
+}
+
+#input-container h5.card-header {
+    padding: .4rem 1.25rem;
+}
+
+#input-cards {
+    padding-left: 25px;
+    padding-right: 25px;
+}
+
+#input-container {
+    z-index: 100;
+    background: white;
+
+    position: static;
+    height: fit-content;
+    box-shadow: none;
+
+    margin-top: 0;
+    margin-bottom: 40px; /* must equal #input-container.fixed padding-bottom */
+}
+
+#input-container.fixed {
+    position: fixed !important;
+    overflow: visible;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    height: 0;
+    box-shadow: 0 0 12px 1px rgba(80, 80, 80, 0.2);
+    margin-bottom: 0;
+    padding: 40px 0; /* must equal #input-container margin-bottom */
+}
+
+#input-container .card {
+    margin: 0 auto;
 }
 
 .footer {
@@ -27,10 +67,6 @@
     cursor: pointer;
 }
 
-#input-container {
-    margin-top: 20px;
-}
-
 #title {
     text-align: center;
     position: absolute;

+ 19 - 13
pywebio/html/index.html

@@ -14,27 +14,33 @@
     <link rel="stylesheet" href="css/app.css">
 </head>
 <body>
-<div class="container no-fix-height">
-    <div tabindex="1" class="mditor preview">
-        <div class="head">
-            <ul class="toolbar">
-                <span id="title">PyWebIO</span>
-            </ul>
-        </div>
-        <div class="body">
-            <div class="viewer">
-                <div class="markdown-body" id="markdown-body">
-                    <div id="pywebio-scope-ROOT"></div>
+<div class="pywebio">
+    <div class="container no-fix-height" id="output-container">
+        <div tabindex="1" class="mditor preview">
+            <div class="head">
+                <ul class="toolbar">
+                    <span id="title">PyWebIO</span>
+                </ul>
+            </div>
+            <div class="body">
+                <div class="viewer">
+                    <div class="markdown-body" id="markdown-body">
+                        <div id="pywebio-scope-ROOT"></div>
+                    </div>
                 </div>
             </div>
         </div>
+        <div id="end-space"></div>
+
     </div>
 
     <div id="input-container">
-
+        <div id="input-cards" class="container">
+        </div>
     </div>
 </div>
 
+
 <footer class="footer">
     Powered by <a href="https://github.com/wang0618/PyWebIO" target="_blank">PyWebIO</a>
 </footer>
@@ -97,7 +103,7 @@
         outputAnimation: !urlparams.get('_pywebio_disable_animate'),
         httpPullInterval: parseInt(urlparams.get('_pywebio_http_pull_interval') || 1000)
     };
-    WebIO.startWebIOClient($('#markdown-body'), $('#input-container'), urlparams.get('app') || 'index', config);
+    WebIO.startWebIOClient($('#markdown-body'), $('#input-cards'), urlparams.get('app') || 'index', config);
 
 </script>
 

+ 7 - 1
webiojs/src/handlers/input.ts

@@ -4,6 +4,7 @@ import {InputItem} from "../models/input/base"
 import {state} from '../state'
 import {all_input_items} from "../models/input"
 import {CommandHandler} from "./base"
+import {close_input, show_input} from "../ui";
 
 /*
 * 整个输入区域的控制类
@@ -25,11 +26,13 @@ export class InputHandler implements CommandHandler {
     private _after_show_form() {
         // 解决表单显示后动态添加内容,表单宽高不变的问题
         setTimeout(() => {
-            let curr_card = $('#input-container > .card')[0];
+            let curr_card = $('#input-cards > .card')[0];
             curr_card.style.height = "unset";
             curr_card.style.width = "unset";
         }, 50);
 
+        show_input();
+
         let old_ctrls = this.form_ctrls.get_top();
         if (old_ctrls)
             old_ctrls[old_ctrls.length - 1].after_show();
@@ -107,6 +110,8 @@ export class InputHandler implements CommandHandler {
             if (old_ctrls === target_ctrls) {
                 deleted.element.hide(100, () => {
                     deleted.element.remove();
+                    close_input();
+
                     let t = this.form_ctrls.get_top();
                     if (t) t[t.length - 1].element.show(state.ShowDuration, () => {
                         this._after_show_form()
@@ -114,6 +119,7 @@ export class InputHandler implements CommandHandler {
                 });
             } else {
                 deleted.element.remove();
+                close_input();
             }
         }
     }

+ 118 - 0
webiojs/src/ui.ts

@@ -0,0 +1,118 @@
+let has_input = false;
+let input_panel = $('#input-container');
+let input_cards = $('#input-cards');
+let end_space = $('#end-space');
+
+export function show_input() {
+    has_input = true;
+    // 40 = #input-container padding-top
+    if (($(window).height() - input_panel[0].getBoundingClientRect().top < input_min_fixed_height() - 40)) {
+        toggle_input_panel_style(true);
+    }
+}
+
+export function close_input() {
+    has_input = false;
+    if (input_panel.hasClass('fixed')) {
+        input_panel.removeClass('fixed');
+    }
+    input_panel.height('unset');
+    end_space.height(0);
+}
+
+
+function input_min_fixed_height() { // 返回当前的输入panel的最小高度
+    const min_fixed_height = 300;
+
+    //80 = #input-container.fixed padding-top + padding-bottom
+    let now_height = input_cards.height() + 80;
+
+    return now_height > min_fixed_height ? min_fixed_height : now_height;
+}
+
+
+function toggle_input_panel_style(fixed: boolean) {
+    if (!fixed) {
+        input_panel.removeClass('fixed');
+        end_space.height(0);
+        input_panel.height('unset');
+    } else {
+        let min = input_min_fixed_height();
+        end_space.height(min - 40);  // 40 =  #input-container.fixed padding-top
+        input_panel.height(min);
+        input_panel.addClass('fixed');
+    }
+}
+
+
+function move_input_panel(step: number) {
+    let new_height = input_panel.height() + step;
+    if (new_height >= input_cards.height())
+        new_height = input_cards.height();
+    else if (new_height <= input_min_fixed_height() - 80)   //80 = #input-container.fixed padding-top + padding-bottom
+        new_height = input_min_fixed_height() - 80;
+
+    input_panel.height(new_height);
+    end_space.height(new_height - 40); // 40 =  #input-container.fixed padding-top
+}
+
+
+$(function () {
+    input_panel = $('#input-container');
+    input_cards = $('#input-cards');
+    end_space = $('#end-space');
+
+
+    let lastScrollTop = 0; // to Detecting scroll direction.  Credits: https://stackoverflow.com/questions/31223341/detecting-scroll-direction
+    $(window).on('scroll', function () {
+        let st = window.pageYOffset || document.documentElement.scrollTop; // Credits: "https://github.com/qeremy/so/blob/master/so.dom.js#L426"
+        let downmove = st < lastScrollTop;
+        let upmove = st > lastScrollTop;
+
+        if (!input_panel.hasClass('fixed')) {
+            // 40 =  #input-container.fixed padding-top
+            if (($(window).height() - input_panel[0].getBoundingClientRect().top < input_min_fixed_height() - 40) && downmove && has_input) {
+                toggle_input_panel_style(true);
+                st = window.pageYOffset || document.documentElement.scrollTop;
+            }
+        } else {
+            // 到达底部
+            // 50 = footer height
+            if ($(window).scrollTop() + window.innerHeight > $(document).height() - 50 && upmove) {  // issue $(window).height() < window.innerHeight in mobile phone
+                toggle_input_panel_style(false);
+                st = window.pageYOffset || document.documentElement.scrollTop;
+            }
+        }
+        lastScrollTop = st <= 0 ? 0 : st; // For Mobile or negative scrolling
+    });
+
+    input_panel[0].addEventListener("wheel", function (e) {
+        // Credits: https://stackoverflow.com/questions/30892830/detect-scrolling-on-pages-without-a-scroll-bar
+
+        if (!input_panel.hasClass('fixed')) return;
+        e.preventDefault();
+
+        // to make it work on IE or Chrome
+        // @ts-ignore
+        let variation = parseInt(e.deltaY);
+        move_input_panel(variation);
+    });
+
+    let last_y = -1;
+    input_panel[0].addEventListener("touchstart", function (evt) {
+        if (evt.changedTouches.length > 1)
+            return;
+        last_y = evt.changedTouches[0].screenY;
+    });
+
+
+    input_panel[0].addEventListener("touchmove", function (evt) {
+        if (!input_panel.hasClass('fixed')) return;
+        if (evt.changedTouches.length > 1)
+            return;
+        evt.preventDefault();
+        let variation = last_y - evt.changedTouches[0].screenY;
+        last_y = evt.changedTouches[0].screenY;
+        move_input_panel(variation);
+    });
+});