yafowil.widget.autocomplete 2.0.0__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- yafowil/widget/autocomplete/__init__.py +118 -0
- yafowil/widget/autocomplete/example.py +275 -0
- yafowil/widget/autocomplete/resources/bootstrap5/widget.js +481 -0
- yafowil/widget/autocomplete/resources/bootstrap5/widget.min.css +1 -0
- yafowil/widget/autocomplete/resources/bootstrap5/widget.min.js +1 -0
- yafowil/widget/autocomplete/resources/default/widget.js +369 -0
- yafowil/widget/autocomplete/resources/default/widget.min.css +1 -0
- yafowil/widget/autocomplete/resources/default/widget.min.js +1 -0
- yafowil/widget/autocomplete/tests/test_widget.py +161 -0
- yafowil/widget/autocomplete/widget.py +106 -0
- yafowil_widget_autocomplete-2.0.0.dist-info/METADATA +66 -0
- yafowil_widget_autocomplete-2.0.0.dist-info/RECORD +15 -0
- yafowil_widget_autocomplete-2.0.0.dist-info/WHEEL +5 -0
- yafowil_widget_autocomplete-2.0.0.dist-info/entry_points.txt +3 -0
- yafowil_widget_autocomplete-2.0.0.dist-info/licenses/LICENSE.rst +27 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
from yafowil.base import factory
|
|
2
|
+
from yafowil.utils import entry_point
|
|
3
|
+
import os
|
|
4
|
+
import webresource as wr
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
resources_dir = os.path.join(os.path.dirname(__file__), 'resources')
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
##############################################################################
|
|
11
|
+
# Default
|
|
12
|
+
##############################################################################
|
|
13
|
+
|
|
14
|
+
# webresource ################################################################
|
|
15
|
+
|
|
16
|
+
resources = wr.ResourceGroup(
|
|
17
|
+
name='yafowil.widget.autocomplete',
|
|
18
|
+
directory=resources_dir,
|
|
19
|
+
path='yafowil-autocomplete'
|
|
20
|
+
)
|
|
21
|
+
resources.add(wr.ScriptResource(
|
|
22
|
+
name='yafowil-autocomplete-js',
|
|
23
|
+
directory=os.path.join(resources_dir, 'default'),
|
|
24
|
+
path='yafowil-autocomplete/default',
|
|
25
|
+
depends='jquery-js',
|
|
26
|
+
resource='widget.js',
|
|
27
|
+
compressed='widget.min.js'
|
|
28
|
+
))
|
|
29
|
+
resources.add(wr.StyleResource(
|
|
30
|
+
name='yafowil-autocomplete-css',
|
|
31
|
+
directory=os.path.join(resources_dir, 'default'),
|
|
32
|
+
path='yafowil-autocomplete/default',
|
|
33
|
+
resource='widget.min.css'
|
|
34
|
+
))
|
|
35
|
+
|
|
36
|
+
# B/C resources ##############################################################
|
|
37
|
+
|
|
38
|
+
js = [{
|
|
39
|
+
'group': 'yafowil.widget.autocomplete.common',
|
|
40
|
+
'resource': 'default/widget.js',
|
|
41
|
+
'order': 21,
|
|
42
|
+
}]
|
|
43
|
+
css = [{
|
|
44
|
+
'group': 'yafowil.widget.autocomplete.common',
|
|
45
|
+
'resource': 'default/widget.min.css',
|
|
46
|
+
'order': 21,
|
|
47
|
+
}]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
##############################################################################
|
|
51
|
+
# Bootstrap 5
|
|
52
|
+
##############################################################################
|
|
53
|
+
|
|
54
|
+
# webresource ################################################################
|
|
55
|
+
bootstrap5_resources = wr.ResourceGroup(
|
|
56
|
+
name='yafowil.widget.autocomplete',
|
|
57
|
+
directory=resources_dir,
|
|
58
|
+
path='yafowil-autocomplete'
|
|
59
|
+
)
|
|
60
|
+
bootstrap5_js = wr.ScriptResource(
|
|
61
|
+
name='yafowil-autocomplete-js',
|
|
62
|
+
directory=os.path.join(resources_dir, 'bootstrap5'),
|
|
63
|
+
path='yafowil-autocomplete/bootstrap5',
|
|
64
|
+
depends='jquery-js',
|
|
65
|
+
resource='widget.js',
|
|
66
|
+
compressed='widget.min.js'
|
|
67
|
+
)
|
|
68
|
+
bootstrap5_resources.add(bootstrap5_js)
|
|
69
|
+
bootstrap5_resources.add(wr.StyleResource(
|
|
70
|
+
name='yafowil-autocomplete-css',
|
|
71
|
+
directory=os.path.join(resources_dir, 'bootstrap5'),
|
|
72
|
+
path='yafowil-autocomplete/bootstrap5',
|
|
73
|
+
resource='widget.min.css'
|
|
74
|
+
))
|
|
75
|
+
|
|
76
|
+
# B/C resources ##############################################################
|
|
77
|
+
|
|
78
|
+
bootstrap5_css = [{
|
|
79
|
+
'group': 'yafowil.widget.autocomplete.common',
|
|
80
|
+
'resource': 'bootstrap5/widget.min.css',
|
|
81
|
+
'order': 20,
|
|
82
|
+
}]
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
##############################################################################
|
|
86
|
+
# Registration
|
|
87
|
+
##############################################################################
|
|
88
|
+
|
|
89
|
+
@entry_point(order=10)
|
|
90
|
+
def register():
|
|
91
|
+
from yafowil.widget.autocomplete import widget # noqa
|
|
92
|
+
|
|
93
|
+
widget_name = 'yafowil.widget.autocomplete'
|
|
94
|
+
|
|
95
|
+
# Default
|
|
96
|
+
factory.register_theme(
|
|
97
|
+
'default',
|
|
98
|
+
widget_name,
|
|
99
|
+
resources_dir,
|
|
100
|
+
js=js,
|
|
101
|
+
css=css
|
|
102
|
+
)
|
|
103
|
+
factory.register_resources('default', widget_name, resources)
|
|
104
|
+
|
|
105
|
+
# Bootstrap 5
|
|
106
|
+
factory.register_theme(
|
|
107
|
+
['bootstrap5'],
|
|
108
|
+
widget_name,
|
|
109
|
+
resources_dir,
|
|
110
|
+
js=bootstrap5_js,
|
|
111
|
+
css=bootstrap5_css
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
factory.register_resources(
|
|
115
|
+
['bootstrap5'],
|
|
116
|
+
widget_name,
|
|
117
|
+
bootstrap5_resources
|
|
118
|
+
)
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
from yafowil.base import factory
|
|
2
|
+
from yafowil.compat import IS_PY2
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
if IS_PY2:
|
|
7
|
+
from urlparse import urlparse
|
|
8
|
+
from urlparse import parse_qs
|
|
9
|
+
else:
|
|
10
|
+
from urllib.parse import urlparse
|
|
11
|
+
from urllib.parse import parse_qs
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
lipsum = """Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do
|
|
15
|
+
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
|
|
16
|
+
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
|
|
17
|
+
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum
|
|
18
|
+
dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident,
|
|
19
|
+
sunt in culpa qui officia deserunt mollit anim id est laborum."""
|
|
20
|
+
lipsum = sorted(set(lipsum.lower().replace('.', '').replace(',', '').split()))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def json_response(url):
|
|
24
|
+
purl = urlparse(url)
|
|
25
|
+
qs = parse_qs(purl.query)
|
|
26
|
+
data = json_data(qs.get('term', [''])[0])
|
|
27
|
+
return {
|
|
28
|
+
'body': json.dumps(data),
|
|
29
|
+
'header': [('Content-Type', 'application/json')]
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def json_data(term):
|
|
34
|
+
data = lipsum
|
|
35
|
+
if term:
|
|
36
|
+
data = [_ for _ in data if _.startswith(term)]
|
|
37
|
+
return data
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
actions_source = [
|
|
41
|
+
{
|
|
42
|
+
'id': 'default',
|
|
43
|
+
'title': 'Default Suggestion'
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
'id': 'custom',
|
|
47
|
+
'title': 'Custom Alert Item',
|
|
48
|
+
'ignore_filter': True,
|
|
49
|
+
'factory': 'yafowil_demo.CustomSuggestion'
|
|
50
|
+
},
|
|
51
|
+
'String Suggestion'
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def json_response_actions(url):
|
|
56
|
+
purl = urlparse(url)
|
|
57
|
+
qs = parse_qs(purl.query)
|
|
58
|
+
data = actions_json_data(qs.get('term', [''])[0])
|
|
59
|
+
return {
|
|
60
|
+
'body': json.dumps(data),
|
|
61
|
+
'header': [('Content-Type', 'application/json')]
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def actions_json_data(term):
|
|
66
|
+
filtered_data = []
|
|
67
|
+
|
|
68
|
+
for item in actions_source:
|
|
69
|
+
if isinstance(item, dict):
|
|
70
|
+
title = item['title']
|
|
71
|
+
if title.lower().startswith(term.lower()) or \
|
|
72
|
+
('ignore_filter' in item and item['ignore_filter'] == True):
|
|
73
|
+
filtered_data.append(item)
|
|
74
|
+
else:
|
|
75
|
+
if item.lower().startswith(term.lower()):
|
|
76
|
+
filtered_data.append(item)
|
|
77
|
+
return filtered_data
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
DOC_STATIC = """\
|
|
82
|
+
Autocomplete with static vocabulary
|
|
83
|
+
-----------------------------------
|
|
84
|
+
|
|
85
|
+
Autocomplete can make use of a static vocabulary source. Here words of blind
|
|
86
|
+
text Lorem Ipsum are used. First the static list of words is generated
|
|
87
|
+
|
|
88
|
+
.. code-block:: python
|
|
89
|
+
|
|
90
|
+
lipsum = '''Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do
|
|
91
|
+
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
|
|
92
|
+
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
|
|
93
|
+
commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit
|
|
94
|
+
esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
|
|
95
|
+
cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
|
|
96
|
+
est laborum.'''
|
|
97
|
+
lipsum = sorted(set(lipsum.lower().replace('.', '').replace(',', '').split()))
|
|
98
|
+
|
|
99
|
+
Next, the field is defined as
|
|
100
|
+
|
|
101
|
+
.. code-block:: python
|
|
102
|
+
|
|
103
|
+
field = factory('#field:autocomplete', props={
|
|
104
|
+
'label': 'Enter some text (local, lorem ipsum)',
|
|
105
|
+
'value': '',
|
|
106
|
+
'source': lipsum,
|
|
107
|
+
})
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
DOC_JSON = """\
|
|
111
|
+
Autocomplete with dynamic json vocabulary
|
|
112
|
+
-----------------------------------------
|
|
113
|
+
|
|
114
|
+
Instead of static lists autocomplete may ask the server for a list of words
|
|
115
|
+
matching a given term. The source is a string and as such interpreted as a
|
|
116
|
+
absolute or relative url
|
|
117
|
+
|
|
118
|
+
.. code-block:: python
|
|
119
|
+
|
|
120
|
+
field = factory('#field:autocomplete', props={
|
|
121
|
+
'label': 'Enter some text (remote listdir)',
|
|
122
|
+
'value': '',
|
|
123
|
+
'source': 'yafowil.widget.autocomplete.json',
|
|
124
|
+
'minLength': 1,
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
The server answers with a JSON response, here the example does it using WSGI
|
|
128
|
+
and ``webob`` way. This code needs modification depending on the framework
|
|
129
|
+
used
|
|
130
|
+
|
|
131
|
+
.. code-block:: python
|
|
132
|
+
|
|
133
|
+
def json_response(environ, start_response):
|
|
134
|
+
data = lipsum
|
|
135
|
+
if environ['QUERY_STRING'].startswith('term='):
|
|
136
|
+
qsts = environ['QUERY_STRING'][5:]
|
|
137
|
+
data = [_ for _ in data if _.startswith(qsts)]
|
|
138
|
+
response = Response(content_type='application/json',
|
|
139
|
+
body=json.dumps(data))
|
|
140
|
+
return response(environ, start_response)
|
|
141
|
+
"""
|
|
142
|
+
|
|
143
|
+
DOC_ACTIONS = """\
|
|
144
|
+
Suggestion Factories and Data types
|
|
145
|
+
-----------------------------------
|
|
146
|
+
|
|
147
|
+
Remote Autocomplete widgets may receive dict-like items. The key
|
|
148
|
+
of the currently selected item will be available in a hidden input field.
|
|
149
|
+
|
|
150
|
+
This also allows the Autocomplete Widget to initialize Suggestions using factories.
|
|
151
|
+
Setting the 'ignore_filter' flag won't validate a Suggestion client-side,
|
|
152
|
+
as long as it is present in the data returned from server.
|
|
153
|
+
|
|
154
|
+
Item types can be mixed and matched in one widget. Make sure to adjust your
|
|
155
|
+
backend validation method for filtering multiple types.
|
|
156
|
+
|
|
157
|
+
The following example shows how to extend or create your own Suggestion class
|
|
158
|
+
based on yafowil.widget.autocomplete:
|
|
159
|
+
|
|
160
|
+
.. code-block:: js
|
|
161
|
+
|
|
162
|
+
window.yafowil_demo = { // your desired path
|
|
163
|
+
CustomSuggestion: class CustomSuggestion extends window.yafowil_autocomplete.AutocompleteSuggestion {
|
|
164
|
+
|
|
165
|
+
constructor(widget, source, val) {
|
|
166
|
+
super(widget, source, val);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
select() {
|
|
170
|
+
super.select();
|
|
171
|
+
alert('The Suggestion has been selected.');
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
compile() {
|
|
175
|
+
super.compile();
|
|
176
|
+
this.elem.css('background-color', '#ffe00057');
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
Your JSON data should return data formatted similar to the following example:
|
|
182
|
+
|
|
183
|
+
.. code-block:: json
|
|
184
|
+
|
|
185
|
+
[
|
|
186
|
+
{
|
|
187
|
+
'id': 'default',
|
|
188
|
+
'title': 'Default Suggestion'
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
'id': 'custom',
|
|
192
|
+
'title': 'Custom Alert Item',
|
|
193
|
+
'ignore_filter': True,
|
|
194
|
+
'factory': 'yafowil_demo.CustomSuggestion'
|
|
195
|
+
},
|
|
196
|
+
'String Suggestion'
|
|
197
|
+
]
|
|
198
|
+
|
|
199
|
+
Now initialize your widget:
|
|
200
|
+
|
|
201
|
+
.. code-block:: python
|
|
202
|
+
|
|
203
|
+
field = factory('#field:autocomplete', props={
|
|
204
|
+
'label': 'Enter some text or select an Action',
|
|
205
|
+
'value': '',
|
|
206
|
+
'source': 'yafowil.widget.autocomplete_actions.json',
|
|
207
|
+
'minLength': 1,
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
"""
|
|
211
|
+
|
|
212
|
+
DOC_DISPLAY = """\
|
|
213
|
+
Display Mode
|
|
214
|
+
------------
|
|
215
|
+
|
|
216
|
+
The widget's display mode renders the widget value inside an uneditable div.
|
|
217
|
+
|
|
218
|
+
The wrapper div can receive additional classes via the ``display_class``
|
|
219
|
+
widget attribute.
|
|
220
|
+
|
|
221
|
+
.. code-block:: python
|
|
222
|
+
|
|
223
|
+
field = factory('#field:autocomplete', mode='display', value='Lorem', props={
|
|
224
|
+
'label': 'Display Mode',
|
|
225
|
+
'source': lipsum,
|
|
226
|
+
# 'display_class': 'my_additional_class'
|
|
227
|
+
})
|
|
228
|
+
"""
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def get_example():
|
|
233
|
+
static_ac = factory('#field:autocomplete', name='static', props={
|
|
234
|
+
'label': 'Enter some text (local, lorem ipsum)',
|
|
235
|
+
'source': lipsum,
|
|
236
|
+
})
|
|
237
|
+
json_ac = factory('#field:autocomplete', name='json', props={
|
|
238
|
+
'label': 'Enter some text (remote listdir)',
|
|
239
|
+
'value': '',
|
|
240
|
+
'source': 'yafowil.widget.autocomplete.json',
|
|
241
|
+
'minLength': 1,
|
|
242
|
+
})
|
|
243
|
+
actions_ac = factory('#field:autocomplete', name='actions', props={
|
|
244
|
+
'label': 'Enter some text or select an Action',
|
|
245
|
+
'value': '',
|
|
246
|
+
'source': 'yafowil.widget.autocomplete_actions.json',
|
|
247
|
+
'minLength': 1,
|
|
248
|
+
})
|
|
249
|
+
display_ac = factory('#field:autocomplete', name='display', mode='display',
|
|
250
|
+
value='Lorem', props={
|
|
251
|
+
'label': 'Display Mode',
|
|
252
|
+
'source': lipsum,
|
|
253
|
+
})
|
|
254
|
+
routes = {
|
|
255
|
+
'yafowil.widget.autocomplete.json': json_response,
|
|
256
|
+
'yafowil.widget.autocomplete_actions.json': json_response_actions,
|
|
257
|
+
}
|
|
258
|
+
return [{
|
|
259
|
+
'widget': static_ac,
|
|
260
|
+
'doc': DOC_STATIC,
|
|
261
|
+
'title': 'Static autocomplete',
|
|
262
|
+
}, {
|
|
263
|
+
'widget': json_ac,
|
|
264
|
+
'routes': routes,
|
|
265
|
+
'doc': DOC_JSON,
|
|
266
|
+
'title': 'JSON data autocomplete',
|
|
267
|
+
}, {
|
|
268
|
+
'widget': actions_ac,
|
|
269
|
+
'doc': DOC_ACTIONS,
|
|
270
|
+
'title': 'Suggestion Factories and Data types',
|
|
271
|
+
}, {
|
|
272
|
+
'widget': display_ac,
|
|
273
|
+
'doc': DOC_DISPLAY,
|
|
274
|
+
'title': 'Display Mode',
|
|
275
|
+
}]
|