Skip to content

Commit 923e760

Browse files
committed
only use bootstrap_select when desktop and multiple=True
1 parent a646a72 commit 923e760

File tree

7 files changed

+35
-22
lines changed

7 files changed

+35
-22
lines changed

pywebio/input.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -330,9 +330,10 @@ def _set_options_selected(options, value):
330330
return options
331331

332332

333-
def select(label: str = '', options: List[Union[Dict[str, Any], Tuple, List, str]] = None, *, multiple: bool = None, validate: Callable[[Any], Optional[str]] = None,
334-
name: str = None, value: Union[List, str] = None, onchange: Callable[[Any], None] = None, required: bool = None,
335-
help_text: str = None, **other_html_attrs):
333+
def select(label: str = '', options: List[Union[Dict[str, Any], Tuple, List, str]] = None, *, multiple: bool = None,
334+
validate: Callable[[Any], Optional[str]] = None, name: str = None, value: Union[List, str] = None,
335+
onchange: Callable[[Any], None] = None, native: bool = True, required: bool = None, help_text: str = None,
336+
**other_html_attrs):
336337
r"""Drop-down selection
337338
338339
By default, only one option can be selected at a time, you can set ``multiple`` parameter to enable multiple selection.
@@ -361,6 +362,8 @@ def select(label: str = '', options: List[Union[Dict[str, Any], Tuple, List, str
361362
You can also set the initial selected option by setting the ``selected`` field in the ``options`` list item.
362363
:type value: list or str
363364
:param bool required: Whether to select at least one item, only available when ``multiple=True``
365+
:param bool native: Using browser's native select component rather than
366+
`bootstrap-select <https://github.com/snapappointments/bootstrap-select>`_. This is the default behavior.
364367
:param - label, validate, name, onchange, help_text, other_html_attrs: Those arguments have the same meaning as for `input()`
365368
:return: If ``multiple=True``, return a list of the values in the ``options`` selected by the user;
366369
otherwise, return the single value selected by the user.

pywebio/pin.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@
9797
9898
You can use attribute or key index of ``pin`` object to get the current value of a pin widget.
9999
By default, when accessing the value of a widget that does not exist, it returns ``None`` instead of
100-
throwing an exception.
100+
throwing an exception. You can enable the error raising by ``pin.use_strict()`` method.
101101
102102
You can also use the ``pin`` object to set the value of pin widget:
103103
@@ -170,13 +170,20 @@ def put_textarea(name: str, *, label: str = '', rows: int = 6, code: Union[bool,
170170

171171

172172
def put_select(name: str, options: List[Union[Dict[str, Any], Tuple, List, str]] = None, *, label: str = '',
173-
multiple: bool = None, value: Union[List, str] = None, help_text: str = None,
173+
multiple: bool = None, value: Union[List, str] = None, native: bool = None, help_text: str = None,
174174
scope: str = None, position: int = OutputPosition.BOTTOM) -> Output:
175-
"""Output a select widget. Refer to: `pywebio.input.select()`"""
175+
"""Output a select widget. Refer to: `pywebio.input.select()`
176+
177+
.. note::
178+
179+
Unlike `pywebio.input.select()`, when ``multiple=True`` and the user is using PC/macOS, `put_select()` will use
180+
`bootstrap-select <https://github.com/snapappointments/bootstrap-select>`_ by default. Setting
181+
``native=True`` will force PyWebIO to use native select component on all platforms and vice versa.
182+
"""
176183
from pywebio.input import select
177184
check_dom_name_value(name, 'pin `name`')
178185
single_input_return = select(name=name, options=options, label=label, multiple=multiple,
179-
value=value, help_text=help_text)
186+
value=value, help_text=help_text, native=native)
180187
return _pin_output(single_input_return, scope, position)
181188

182189

pywebio/session/__init__.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -517,8 +517,6 @@ def set_env(**env_info):
517517
* ``input_auto_focus`` (bool): Whether to focus on input automatically after showing input panel, default is ``True``
518518
* ``output_max_width`` (str): The max width of the page content area (in pixel or percentage,
519519
e.g. ``'1080px'``, ``'80%'``. Default is 880px).
520-
* ``native_select`` (bool): Whether to use native select component, default is ``False`` . It's a fallback in case
521-
the default select component is not rendered correctly in some cases.
522520
523521
Example::
524522
@@ -530,8 +528,7 @@ def set_env(**env_info):
530528
"""
531529
from ..io_ctrl import send_msg
532530
assert all(k in ('title', 'output_animation', 'auto_scroll_bottom', 'http_pull_interval', 'output_max_width',
533-
'input_panel_min_height', 'input_panel_init_height', 'input_panel_fixed', 'input_auto_focus',
534-
'native_select')
531+
'input_panel_min_height', 'input_panel_init_height', 'input_panel_fixed', 'input_auto_focus')
535532
for k in env_info.keys())
536533
send_msg('set_env', spec=env_info)
537534

webiojs/src/handlers/env.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,6 @@ export class EnvSettingHandler implements CommandHandler {
2626
}
2727
}
2828

29-
if (spec.native_select !== undefined) {
30-
config.disableSelectPicker = spec.native_select;
31-
}
32-
3329
if (spec.http_pull_interval !== undefined) {
3430
if (state.CurrentSession instanceof HttpSession)
3531
state.CurrentSession.change_pull_interval(spec.http_pull_interval);

webiojs/src/models/input/select.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import {InputItem} from "./base";
2-
import {deep_copy, make_set} from "../../utils"
3-
import {config} from "../../state";
2+
import {deep_copy, is_mobile, make_set} from "../../utils"
43

54
const options_tpl = `
65
{{#options}}
@@ -24,8 +23,13 @@ $.fn.selectpicker.Constructor.BootstrapVersion = '4';
2423
export class Select extends InputItem {
2524
static accept_input_types: string[] = ["select"];
2625

26+
use_bootstrap_select: boolean = false;
27+
2728
constructor(spec: any, task_id: string, on_input_event: (event_name: string, input_item: InputItem) => void) {
2829
super(spec, task_id, on_input_event);
30+
if (spec.native === false || (spec.native === undefined && spec.multiple && !is_mobile())) {
31+
this.use_bootstrap_select = true;
32+
}
2933
}
3034

3135
create_element(): JQuery {
@@ -37,7 +41,7 @@ export class Select extends InputItem {
3741
this.element = $(html);
3842
this.setup_select_options(this.element, spec.options);
3943

40-
if (!config.disableSelectPicker) {
44+
if (this.use_bootstrap_select) {
4145
// @ts-ignore
4246
this.element.find('select').selectpicker();
4347
}
@@ -71,7 +75,7 @@ export class Select extends InputItem {
7175
input_elem.attr(key, this.spec[key]);
7276
}
7377

74-
if (!config.disableSelectPicker) {
78+
if (this.use_bootstrap_select) {
7579
// @ts-ignore
7680
input_elem.selectpicker('refresh');
7781
}
@@ -99,7 +103,7 @@ export class Select extends InputItem {
99103
$(this).prop('selected', true);
100104
}
101105
});
102-
if (!config.disableSelectPicker) {
106+
if (this.use_bootstrap_select) {
103107
// @ts-ignore
104108
this.element.find('select').selectpicker('render');
105109
}
@@ -111,7 +115,7 @@ export class Select extends InputItem {
111115

112116
on_reset(e: any) {
113117
// need to wait some time to get the select element be reset, and then update `selectpicker`
114-
if (!config.disableSelectPicker)
118+
if (this.use_bootstrap_select)
115119
setTimeout(() => {
116120
// @ts-ignore
117121
this.element.find('select').selectpicker('render');

webiojs/src/state.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ export let config = {
1616
codeMirrorModeURL: "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.52.2/mode/%N/%N.min.js",
1717
codeMirrorThemeURL: "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.52.2/theme/%N.min.css",
1818
outputAnimation: true, // 启用内容输出动画
19-
disableSelectPicker: false,
2019
httpPullInterval: 1000, // HttpSession 拉取消息的周期(ms)
2120
debug: false, // 调试模式, 打印所有交互的消息
2221
};

webiojs/src/utils.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,4 +176,11 @@ function int2bytes(num: number) {
176176
dataView.setUint32(0, (num / 4294967296) | 0); // 4294967296 == 2^32
177177
dataView.setUint32(4, num | 0);
178178
return buf;
179+
}
180+
181+
export function is_mobile() {
182+
// @ts-ignore
183+
if (navigator.userAgentData) return navigator.userAgentData.mobile;
184+
const ipadOS = (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1); /* iPad OS 13 */
185+
return /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(navigator.userAgent.toLowerCase()) || ipadOS;
179186
}

0 commit comments

Comments
 (0)