--- a/web/formwidgets.py Thu Apr 09 17:33:45 2009 +0200
+++ b/web/formwidgets.py Thu Apr 09 19:58:37 2009 +0200
@@ -11,14 +11,20 @@
from cubicweb.common import tags
from cubicweb.web import stdmsgs
+
class FieldWidget(object):
+ """abstract widget class"""
+ # javascript / css files required by the widget
needs_js = ()
needs_css = ()
+ # automatically set id and tabindex attributes ?
setdomid = True
settabindex = True
def __init__(self, attrs=None, setdomid=None, settabindex=None):
- self.attrs = attrs or {}
+ if attrs is None:
+ attrs = {}
+ self.attrs = attrs
if setdomid is not None:
# override class's default value
self.setdomid = setdomid
@@ -34,9 +40,14 @@
form.req.add_css(self.needs_css)
def render(self, form, field):
+ """render the widget for the given `field` of `form`.
+ To override in concrete class
+ """
raise NotImplementedError
def _render_attrs(self, form, field):
+ """return html tag name, attributes and a list of values for the field
+ """
name = form.context[field]['name']
values = form.context[field]['value']
if not isinstance(values, (tuple, list)):
@@ -50,9 +61,14 @@
class Input(FieldWidget):
+ """abstract widget class for <input> tag based widgets"""
type = None
def render(self, form, field):
+ """render the widget for the given `field` of `form`.
+
+ Generate one <input> tag for each field's value
+ """
self.add_media(form)
name, values, attrs = self._render_attrs(form, field)
inputs = [tags.input(name=name, value=value, type=self.type, **attrs)
@@ -60,11 +76,17 @@
return u'\n'.join(inputs)
+# basic html widgets ###########################################################
+
class TextInput(Input):
+ """<input type='text'>"""
type = 'text'
class PasswordInput(Input):
+ """<input type='password'> and its confirmation field (using
+ <field's name>-confirm as name)
+ """
type = 'password'
def render(self, form, field):
@@ -85,6 +107,7 @@
class FileInput(Input):
+ """<input type='file'>"""
type = 'file'
def _render_attrs(self, form, field):
@@ -94,16 +117,23 @@
class HiddenInput(Input):
+ """<input type='hidden'>"""
type = 'hidden'
setdomid = False # by default, don't set id attribute on hidden input
settabindex = False
class ButtonInput(Input):
+ """<input type='button'>
+
+ if you want a global form button, look at the Button, SubmitButton,
+ ResetButton and ImgButton classes below.
+ """
type = 'button'
class TextArea(FieldWidget):
+ """<textarea>"""
def render(self, form, field):
name, values, attrs = self._render_attrs(form, field)
attrs.setdefault('onkeypress', 'autogrow(this)')
@@ -117,6 +147,7 @@
class FCKEditor(TextArea):
+ """FCKEditor enabled <textarea>"""
def __init__(self, *args, **kwargs):
super(FCKEditor, self).__init__(*args, **kwargs)
self.attrs['cubicweb:type'] = 'wysiwyg'
@@ -127,6 +158,7 @@
class Select(FieldWidget):
+ """<select>, for field having a specific vocabulary"""
def __init__(self, attrs=None, multiple=False):
super(Select, self).__init__(attrs)
self.multiple = multiple
@@ -144,6 +176,9 @@
class CheckBox(Input):
+ """<input type='checkbox'>, for field having a specific vocabulary. One
+ input will be generated for each possible value.
+ """
type = 'checkbox'
def render(self, form, field):
@@ -161,8 +196,11 @@
class Radio(Input):
+ """<input type='radio'>, for field having a specific vocabulary. One
+ input will be generated for each possible value.
+ """
type = 'radio'
- setdomid = False
+ setdomid = False # by default, don't set id attribute on radio input
def render(self, form, field):
name, curvalues, attrs = self._render_attrs(form, field)
@@ -178,7 +216,12 @@
return '\n'.join(options)
+# javascript widgets ###########################################################
+
class DateTimePicker(TextInput):
+ """<input type='text' + javascript date/time picker for date or datetime
+ fields
+ """
monthnames = ('january', 'february', 'march', 'april',
'may', 'june', 'july', 'august',
'september', 'october', 'november', 'december')
@@ -219,7 +262,10 @@
req._('calendar'), helperid) )
+# ajax widgets ################################################################
+
class AjaxWidget(FieldWidget):
+ """simple <div> based ajax widget"""
def __init__(self, wdgtype, inputid=None, **kwargs):
super(AjaxWidget, self).__init__(**kwargs)
self.attrs.setdefault('class', 'widget')
@@ -233,8 +279,52 @@
attrs = self._render_attrs(form, field)[-1]
return tags.div(**attrs)
+
+class AutoCompletionWidget(TextInput):
+ """ajax widget for StringField, proposing matching existing values as you
+ type.
+ """
+ needs_js = ('cubicweb.widgets.js', 'jquery.autocomplete.js')
+ needs_css = ('jquery.autocomplete.css',)
+ wdgtype = 'SuggestField'
+ loadtype = 'auto'
+
+ def _render_attrs(self, form, field):
+ name, values, attrs = super(AutoCompletionWidget, self)._render_attrs(form, field)
+ if not values[0]:
+ values = (INTERNAL_FIELD_VALUE,)
+ try:
+ attrs['klass'] += ' widget'
+ except KeyError:
+ attrs['klass'] = 'widget'
+ attrs.setdefault('cubicweb:wdgtype', self.wdgtype)
+ attrs.setdefault('cubicweb:loadtype', self.loadtype)
+ # XXX entity form specific
+ attrs['cubicweb:dataurl'] = self._get_url(form.edited_entity)
+ return name, values, attrs
+
+ def _get_url(self, entity):
+ return entity.req.build_url('json', fname=entity.autocomplete_initfuncs[self.rschema],
+ pageid=entity.req.pageid, mode='remote')
+
+
+class StaticFileAutoCompletionWidget(AutoCompletionWidget):
+ """XXX describe me"""
+ wdgtype = 'StaticFileSuggestField'
+
+ def _get_url(self, entity):
+ return entity.req.datadir_url + entity.autocomplete_initfuncs[self.rschema]
+
+
+class RestrictedAutoCompletionWidget(AutoCompletionWidget):
+ """XXX describe me"""
+ wdgtype = 'RestrictedSuggestField'
+
+
+# buttons ######################################################################
class Button(Input):
+ """<input type='button'>, base class for global form buttons"""
type = 'button'
def __init__(self, label=stdmsgs.BUTTON_OK, attrs=None,
setdomid=None, settabindex=None,
@@ -266,12 +356,21 @@
class SubmitButton(Button):
+ """<input type='submit'>, main button to submit a form"""
type = 'submit'
+
class ResetButton(Button):
+ """<input type='reset'>, main button to reset a form.
+ You usually don't want this.
+ """
type = 'reset'
+
class ImgButton(object):
+ """<img> wrapped into a <a> tag with href triggering something (usually a
+ javascript call)
+ """
def __init__(self, domid, href, label, imgressource):
self.domid = domid
self.href = href
@@ -283,5 +382,4 @@
return '<a id="%(domid)s" href="%(href)s"><img src="%(imgsrc)s" alt="%(label)s"/>%(label)s</a>' % self.__dict__
-# XXX EntityLinkComboBoxWidget, AddComboBoxWidget, AutoCompletionWidget,
-# StaticFileAutoCompletionWidget, RestrictedAutoCompletionWidget...
+# XXX EntityLinkComboBoxWidget, AddComboBoxWidget, RawDynamicComboBoxWidget