Show More
@@ -0,0 +1,137 b'' | |||
|
1 | # backport of broken code for python 3.11 on webhelpers2 | |
|
2 | ||
|
3 | import collections | |
|
4 | from webhelpers2.misc import NotGiven | |
|
5 | from webhelpers2.html.tags import _set_id_attr, NL, _OptionsList, OptGroup, HTML | |
|
6 | ||
|
7 | ||
|
8 | class Options(_OptionsList): | |
|
9 | """A list of options and/or optgroups for a select or datalist. | |
|
10 | ||
|
11 | I'm a subclass of ``list``. My elements are ``Option`` and/or ``OptGroup`` | |
|
12 | instances. Do not add other element types or they may cause | |
|
13 | ``options.render`` or ``str(options)`` to fail. | |
|
14 | """ | |
|
15 | ||
|
16 | def __init__(self, options=None, prompt=None): | |
|
17 | """Construct an ``Options`` instance. | |
|
18 | ||
|
19 | **options**: An iterable of ``Option`` instances, ``OptGroup`` | |
|
20 | instances and/or strings. If you pass strings, they will be converted | |
|
21 | to simple ``Option`` instances (i.e., the label will be the string, and | |
|
22 | the value will be ``None``). | |
|
23 | ||
|
24 | **prompt**: If passed, this will be turned into an extra option before | |
|
25 | the others. The string argument will become the option's label, and | |
|
26 | the option's value will be the empty string. | |
|
27 | """ | |
|
28 | if prompt: | |
|
29 | self.add_option(prompt, "") | |
|
30 | if options: | |
|
31 | self._parse_options(options) | |
|
32 | ||
|
33 | def __repr__(self): | |
|
34 | classname = self.__class__.__name__ | |
|
35 | return "{0}({1!r})".format(classname, list(self)) | |
|
36 | ||
|
37 | def add_optgroup(self, label, options=None): | |
|
38 | """Create an ``OptGroup``, append it, and return it. | |
|
39 | ||
|
40 | The return value is the ``OptGroup`` instance. Call its | |
|
41 | ``.add_option`` method to add options to the group. | |
|
42 | """ | |
|
43 | group = OptGroup(label, options) | |
|
44 | self.append(group) | |
|
45 | return group | |
|
46 | ||
|
47 | def render(self, selected_values=None): | |
|
48 | """ | |
|
49 | Render the options as a concatenated literal of <option> and/or | |
|
50 | <optgroup> tags, with a newline after each. | |
|
51 | ||
|
52 | **selected_values**: The value(s) that should be preselected. You | |
|
53 | can pass a string, int, bool, or other scalar type, or a sequence of | |
|
54 | these. If you pass a scalar it will be standardized to a one-element | |
|
55 | tuple. If you don't pass anything, it will default to ``("",)`` (a tuple | |
|
56 | containing the empty string); this will correctly preselect prompts. | |
|
57 | ||
|
58 | Note that ``selected_values`` *does not do type conversions*. If you | |
|
59 | pass an int, the corresponding ``Option.value`` must also be an int or | |
|
60 | otherwise equal to it. (The actual comparision operator is ``in``.) | |
|
61 | ||
|
62 | Calling ``str(options)`` or ``options.__html__()`` is the same as | |
|
63 | calling ``options.render()`` without arguments. This is only useful if | |
|
64 | you don't want to pass any selected values. | |
|
65 | """ | |
|
66 | ||
|
67 | selected_values = self._parse_selected_values(selected_values) | |
|
68 | return self._render(self, selected_values) | |
|
69 | ||
|
70 | __str__ = __html__ = render | |
|
71 | ||
|
72 | def _render(self, options, selected_values): | |
|
73 | tags = [] | |
|
74 | for opt in options: | |
|
75 | if isinstance(opt, OptGroup): | |
|
76 | content = self._render(opt, selected_values) | |
|
77 | tag = HTML.tag("optgroup", NL, content, label=opt.label) | |
|
78 | tags.append(tag) | |
|
79 | else: | |
|
80 | value = opt.value if opt.value is not None else opt.label | |
|
81 | selected = value in selected_values | |
|
82 | tag = HTML.tag("option", opt.label, value=opt.value, selected=selected) | |
|
83 | tags.append(tag) | |
|
84 | return HTML(*tags, nl=True) | |
|
85 | ||
|
86 | @staticmethod | |
|
87 | def _parse_selected_values(values): | |
|
88 | if values is None: | |
|
89 | return ("",) | |
|
90 | is_string = isinstance(values, str) | |
|
91 | is_seq = isinstance(values, collections.abc.Sequence) | |
|
92 | if is_string or not is_seq: | |
|
93 | return (values,) | |
|
94 | else: | |
|
95 | return values | |
|
96 | ||
|
97 | ||
|
98 | def raw_select(name, selected_values, options, id=NotGiven, **attrs): | |
|
99 | """Create a dropdown selection box. | |
|
100 | ||
|
101 | Arguments: | |
|
102 | ||
|
103 | * **name**: the name of this control. | |
|
104 | ||
|
105 | * **selected_values**: the value(s) that should be preselected. | |
|
106 | A string or list of strings. Some other types are allowed; see the | |
|
107 | ``Options.render`` method for details. | |
|
108 | ||
|
109 | * **options**: an ``Options`` instance, or an iterable to pass to the | |
|
110 | its constructor. See the ``Options`` class for allowed element types. | |
|
111 | ||
|
112 | * **id**: the HTML ID attribute. This should be a keyword argument if | |
|
113 | passed. By default the ID is the same as the name. filtered through | |
|
114 | ``_make_safe_id_component()``. Pass None to suppress the | |
|
115 | ID attribute entirely. | |
|
116 | ||
|
117 | The following options may only be keyword arguments: | |
|
118 | ||
|
119 | * **multiple**: if true, this control will allow multiple | |
|
120 | selections. | |
|
121 | ||
|
122 | * **prompt**: An extra option that will be prepended to the list. | |
|
123 | The argument is the option label; e.g., "Please choose ...". The generated | |
|
124 | option's value will be the empty string (""), which is equivalent to not | |
|
125 | making a selection. If you specify this and also pass an ``Options`` | |
|
126 | instance in ``options``, it will combine the two into a new ``Options`` | |
|
127 | object rather than reusing the existing one. | |
|
128 | ||
|
129 | Any other keyword args will become HTML attributes for the <select>. | |
|
130 | """ | |
|
131 | ||
|
132 | _set_id_attr(attrs, id, name) | |
|
133 | attrs["name"] = name | |
|
134 | prompt = attrs.pop("prompt", None) | |
|
135 | if prompt or not isinstance(options, Options): | |
|
136 | options = Options(options, prompt=prompt) | |
|
137 | return HTML.tag("select", NL, options.render(selected_values), **attrs) No newline at end of file |
General Comments 0
You need to be logged in to leave comments.
Login now