Browse Source

feat: add `put_widget()` to output custom widget

wangweimin 5 years ago
parent
commit
73ae91b19c

File diff suppressed because it is too large
+ 0 - 0
pywebio/html/js/pywebio.min.js


File diff suppressed because it is too large
+ 0 - 0
pywebio/html/js/pywebio.min.js.map


+ 44 - 1
pywebio/output.py

@@ -33,6 +33,8 @@ r"""输出内容到用户浏览器
 .. autofunction:: put_buttons
 .. autofunction:: put_image
 .. autofunction:: put_file
+
+.. autofunction:: put_widget
 """
 import io
 import logging
@@ -54,7 +56,7 @@ logger = logging.getLogger(__name__)
 __all__ = ['Position', 'set_title', 'set_output_fixed_height', 'set_auto_scroll_bottom', 'remove', 'scroll_to',
            'put_text', 'put_html', 'put_code', 'put_markdown', 'use_scope', 'set_scope', 'clear', 'remove',
            'put_table', 'table_cell_buttons', 'put_buttons', 'put_image', 'put_file', 'PopupSize', 'popup',
-           'close_popup']
+           'close_popup', 'put_widget']
 
 
 # popup尺寸
@@ -497,6 +499,47 @@ def put_file(name, content, scope=Scope.Current, position=OutputPosition.BOTTOM)
     return OutputReturn(spec)
 
 
+@safely_destruct_output_when_exp('data')
+def put_widget(template, data, scope=Scope.Current, position=OutputPosition.BOTTOM) -> OutputReturn:
+    """输出自定义的控件
+
+    :param template: html模版,使用 `mustache.js <https://github.com/janl/mustache.js>`_ 语法
+    :param dict data:  渲染模版使用的数据.
+
+       数据可以包含输出函数( ``put_xxx()`` )的返回值, 可以使用 ``pywebio_output_parse`` 函数来解析 ``put_xxx()`` 内容.
+
+       ⚠️:使用 ``pywebio_output_parse`` 函数时,需要关闭mustache的html转义: ``{{& pywebio_output_parse}}`` , 参见下文示例.
+    :param int scope, position: 与 `put_text` 函数的同名参数含义一致
+
+    :Example:
+    ::
+
+        tpl = '''
+        <details>
+            <summary>{{title}}</summary>
+            {{#contents}}
+                {{& pywebio_output_parse}}
+            {{/contents}}
+        </details>
+        '''
+
+        put_widget(tpl, {
+            "title": 'More content',
+            "contents": [
+                put_text('text'),
+                put_markdown('~~删除线~~'),
+                put_table([
+                    ['商品', '价格'],
+                    ['苹果', '5.5'],
+                    ['香蕉', '7'],
+                ])
+            ]
+        })
+    """
+    spec = _get_output_spec('custom_widget', template=template, data=data, scope=scope, position=position)
+    return OutputReturn(spec)
+
+
 @safely_destruct_output_when_exp('content')
 def popup(title, content, size=PopupSize.NORMAL, implicit_close=True, closable=True):
     """popup(title, content, size=PopupSize.NORMAL, implicit_close=True, closable=True)

+ 41 - 19
webiojs/src/models/output.ts

@@ -34,18 +34,21 @@ let Markdown = {
     }
 };
 
+// 将html字符串解析成jQuery对象
+function parseHtml(html_str: string) {
+    let nodes = $.parseHTML(html_str, null, true);
+    let elem;
+    if (nodes.length != 1)
+        elem = $(document.createElement('div')).append(nodes);
+    else
+        elem = $(nodes[0]);
+    return elem;
+}
+
 let Html = {
     handle_type: 'html',
     get_element: function (spec: any) {
-        let nodes = $.parseHTML(spec.content, null, true);
-        let elem;
-        if (nodes.length > 1)
-            elem = $('<div><div/>').append(nodes);
-        else if (nodes.length === 1)
-            elem = $(nodes[0]);
-        else
-            elem = $(nodes);
-        return elem;
+        return parseHtml(spec.content);
     }
 };
 
@@ -87,6 +90,19 @@ let File = {
     }
 };
 
+// 将output指令的spec字段解析成html字符串
+function outputSpecToHtml(spec: any) {
+    let html = '';
+    try {
+        let nodes = getWidgetElement(spec);
+        for (let node of nodes)
+            html += node.outerHTML || '';
+    } catch (e) {
+        console.error('Get sub widget html error,', e, spec);
+    }
+    return html;
+}
+
 let Table = {
     handle_type: 'table',
     get_element: function (spec: { data: string[][], span: { [i: string]: { col: number, row: number } } }) {
@@ -123,15 +139,7 @@ let Table = {
 
                 // 处理复合类型单元格,即单元格不是简单的html,而是一个output命令的spec
                 if (typeof data === 'object') {
-                    let html = '';
-                    try {
-                        let nodes = getWidgetElement(data);
-                        for (let node of nodes)
-                            html += node.outerHTML || '';
-                    } catch (e) {
-                        console.error('Get sub widget html error,', e, data);
-                    }
-                    data = html;
+                    data = outputSpecToHtml(data);
                 }
 
                 table_data[row_id].push({
@@ -148,7 +156,21 @@ let Table = {
     }
 };
 
-let all_widgets: Widget[] = [Text, Markdown, Html, Buttons, File, Table];
+let CustomWidget = {
+    handle_type: 'custom_widget',
+    get_element: function (spec: { template: string, data: { [i: string]: any } }) {
+        spec.data['pywebio_output_parse'] = function () {
+            if (this.type)
+                return outputSpecToHtml(this);
+            else
+                return this
+        };
+        let html = Mustache.render(spec.template, spec.data);
+        return parseHtml(html);
+    }
+};
+
+let all_widgets: Widget[] = [Text, Markdown, Html, Buttons, File, Table, CustomWidget];
 
 
 let type2widget: { [i: string]: Widget } = {};

Some files were not shown because too many files changed in this diff