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