Procházet zdrojové kódy

maint: make `scrollable` widget native

wangweimin před 3 roky
rodič
revize
a92a26e9b6
3 změnil soubory, kde provedl 53 přidání a 36 odebrání
  1. 12 0
      pywebio/html/css/app.css
  2. 5 35
      pywebio/output.py
  3. 36 1
      webiojs/src/models/output.ts

+ 12 - 0
pywebio/html/css/app.css

@@ -289,4 +289,16 @@ details[open]>summary {
 
 
 .pywebio-clickable{
 .pywebio-clickable{
     cursor: pointer;
     cursor: pointer;
+}
+
+/* scrollable widget */
+.webio-scrollable{
+    overflow-y: scroll;
+    padding: 10px;
+    margin-bottom: 10px;
+}
+
+.webio-scrollable.scrollable-border{
+    border: 1px solid rgba(0,0,0,.125);
+    box-shadow: inset 0 0 2px 0 rgba(0,0,0,.1);
 }
 }

+ 5 - 35
pywebio/output.py

@@ -1067,6 +1067,7 @@ def put_scrollable(content=[], height=400, keep_bottom=False, horizon_scroll=Fal
     :param int/tuple height: The height of the area (in pixels).
     :param int/tuple height: The height of the area (in pixels).
        ``height`` parameter also accepts ``(min_height, max_height)`` to indicate the range of height, for example,
        ``height`` parameter also accepts ``(min_height, max_height)`` to indicate the range of height, for example,
        ``(100, 200)`` means that the area has a minimum height of 100 pixels and a maximum of 200 pixels.
        ``(100, 200)`` means that the area has a minimum height of 100 pixels and a maximum of 200 pixels.
+    :param bool keep_bottom: Whether to keep the content area scrolled to the bottom when updated.
     :param bool horizon_scroll: Whether to use the horizontal scroll bar
     :param bool horizon_scroll: Whether to use the horizontal scroll bar
     :param bool border: Whether to show border
     :param bool border: Whether to show border
     :param int scope, position: Those arguments have the same meaning as for `put_text()`
     :param int scope, position: Those arguments have the same meaning as for `put_text()`
@@ -1106,41 +1107,10 @@ def put_scrollable(content=[], height=400, keep_bottom=False, horizon_scroll=Fal
     except Exception:
     except Exception:
         min_height, max_height = height, height
         min_height, max_height = height, height
 
 
-    dom_id = 'pywebio-%s' % random_str(10)
-
-    tpl = """<div id="{{dom_id}}" {{#keep_bottom}}tabindex="0"{{/keep_bottom}}
-        style="min-height: {{min_height}}px; max-height: {{max_height}}px;
-            overflow-y: scroll;
-            {{#horizon_scroll}}overflow-x: scroll;{{/horizon_scroll}}
-            {{#border}} 
-            border: 1px solid rgba(0,0,0,.125);
-            box-shadow: inset 0 0 2px 0 rgba(0,0,0,.1); 
-            {{/border}}
-            padding: 10px;
-            margin-bottom: 10px;">
-
-        {{#contents}}
-            {{& pywebio_output_parse}}
-        {{/contents}}
-    </div>"""
-    if keep_bottom:
-        tpl += """
-        <script>
-            (function(){
-                let div = document.getElementById(%r), stop=false;
-                $(div).on('focusin', function(e){ stop=true }).on('focusout', function(e){ stop=false });;
-                new MutationObserver(function (mutations, observe) {
-                    if(!stop) $(div).stop().animate({ scrollTop: $(div).prop("scrollHeight")}, 200);
-                }).observe(div, { childList: true, subtree:true });
-            })();
-        </script>
-        """ % dom_id
-
-    return put_widget(template=tpl,
-                      data=dict(dom_id=dom_id, contents=content, min_height=min_height,
-                                max_height=max_height, keep_bottom=keep_bottom,
-                                horizon_scroll=horizon_scroll, border=border),
-                      scope=scope, position=position).enable_context_manager()
+    spec = _get_output_spec('scrollable', contents=content, min_height=min_height, max_height=max_height,
+                            keep_bottom=keep_bottom, horizon_scroll=horizon_scroll, border=border, scope=scope,
+                            position=position)
+    return Output(spec)
 
 
 
 
 @safely_destruct_output_when_exp('tabs')
 @safely_destruct_output_when_exp('tabs')

+ 36 - 1
webiojs/src/models/output.ts

@@ -201,6 +201,40 @@ let TabsWidget = {
     }
     }
 };
 };
 
 
+const SCROLLABLE_TPL = `<div>
+<div class="webio-scrollable{{#border}} scrollable-border{{/border}}" {{#keep_bottom}}tabindex="0"{{/keep_bottom}} 
+    style="min-height: {{min_height}}px; max-height: {{max_height}}px;{{#horizon_scroll}}overflow-x: scroll;{{/horizon_scroll}}">
+    {{#contents}}
+        {{& pywebio_output_parse}}
+    {{/contents}}
+</div>
+</div>`;
+
+let ScrollableWidget = {
+    handle_type: 'scrollable',
+    get_element: function (spec: {
+            contents: any, min_height: string,
+            max_height: string, keep_bottom: boolean,
+            horizon_scroll: boolean, border: boolean
+    }) {
+        let elem = render_tpl(SCROLLABLE_TPL, spec);
+        let container = elem.find('> div');
+        if (spec.keep_bottom) {
+            let stop = false;
+            container.on('focusin mouseenter', function (e) {
+                stop = true
+            }).on('focusout mouseleave', function (e) {
+                stop = false
+            });
+            console.log(container)
+            new MutationObserver(function (mutations, observe) {
+                if (!stop) container.stop().animate({scrollTop: container.prop("scrollHeight")}, 200);
+            }).observe(container[0], {childList: true, subtree: true});
+        }
+        return elem;
+    }
+};
+
 
 
 const SCOPE_TPL = `<div>
 const SCOPE_TPL = `<div>
     {{#contents}}
     {{#contents}}
@@ -233,7 +267,8 @@ let CustomWidget = {
     }
     }
 };
 };
 
 
-let all_widgets: Widget[] = [Text, Markdown, Html, Buttons, File, Table, CustomWidget, TabsWidget, PinWidget, ScopeWidget];
+let all_widgets: Widget[] = [Text, Markdown, Html, Buttons, File, Table, CustomWidget, TabsWidget, PinWidget,
+    ScopeWidget, ScrollableWidget];
 
 
 
 
 let type2widget: { [i: string]: Widget } = {};
 let type2widget: { [i: string]: Widget } = {};