widgets.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. # -*- coding: utf-8 -*-
  2. """
  3. flaskbb.utils.widgets
  4. ~~~~~~~~~~~~~~~~~~~~~
  5. Additional widgets for wtforms
  6. :copyright: (c) 2014 by the FlaskBB Team.
  7. :license: BSD, see LICENSE for more details.
  8. """
  9. import simplejson as json
  10. from datetime import datetime
  11. from wtforms.widgets.core import Select, HTMLString, html_params
  12. class SelectBirthdayWidget(object):
  13. """Renders a DateTime field with 3 selects.
  14. For more information see: http://stackoverflow.com/a/14664504
  15. """
  16. FORMAT_CHOICES = {
  17. '%d': [(x, str(x)) for x in range(1, 32)],
  18. '%m': [(x, str(x)) for x in range(1, 13)]
  19. }
  20. FORMAT_CLASSES = {
  21. '%d': 'select_date_day',
  22. '%m': 'select_date_month',
  23. '%Y': 'select_date_year'
  24. }
  25. def __init__(self, years=range(1930, datetime.utcnow().year + 1)):
  26. """Initialzes the widget.
  27. :param years: The min year which should be chooseable.
  28. Defatuls to ``1930``.
  29. """
  30. super(SelectBirthdayWidget, self).__init__()
  31. self.FORMAT_CHOICES['%Y'] = [(x, str(x)) for x in years]
  32. def __call__(self, field, **kwargs):
  33. field_id = kwargs.pop('id', field.id)
  34. html = []
  35. allowed_format = ['%d', '%m', '%Y']
  36. surrounded_div = kwargs.pop('surrounded_div', None)
  37. css_class = kwargs.get('class', None)
  38. for date_format in field.format.split():
  39. if date_format in allowed_format:
  40. choices = self.FORMAT_CHOICES[date_format]
  41. id_suffix = date_format.replace('%', '-')
  42. id_current = field_id + id_suffix
  43. if css_class is not None: # pragma: no cover
  44. select_class = "{} {}".format(
  45. css_class, self.FORMAT_CLASSES[date_format]
  46. )
  47. else:
  48. select_class = self.FORMAT_CLASSES[date_format]
  49. kwargs['class'] = select_class
  50. try:
  51. del kwargs['placeholder']
  52. except KeyError:
  53. pass
  54. if surrounded_div is not None:
  55. html.append('<div class="%s">' % surrounded_div)
  56. html.append('<select %s>' % html_params(name=field.name,
  57. id=id_current,
  58. **kwargs))
  59. if field.data:
  60. current_value = int(field.data.strftime(date_format))
  61. else:
  62. current_value = None
  63. for value, label in choices:
  64. selected = (value == current_value)
  65. # Defaults to blank
  66. if value == 1 or value == 1930:
  67. html.append(Select.render_option("None", " ", selected))
  68. html.append(Select.render_option(value, label, selected))
  69. html.append('</select>')
  70. if surrounded_div is not None:
  71. html.append("</div>")
  72. html.append(' ')
  73. return HTMLString(''.join(html))
  74. class MultiSelect(object):
  75. """
  76. Renders a megalist-multiselect widget.
  77. The field must provide an `iter_choices()` method which the widget will
  78. call on rendering; this method must yield tuples of
  79. `(value, label, selected)`.
  80. """
  81. def __call__(self, field, **kwargs):
  82. kwargs.setdefault('id', field.id)
  83. src_list, dst_list = [], []
  84. for val, label, selected in field.iter_choices():
  85. if selected:
  86. dst_list.append({'label':label, 'listValue':val})
  87. else:
  88. src_list.append({'label':label, 'listValue':val})
  89. kwargs.update(
  90. {
  91. 'data-provider-src':json.dumps(src_list),
  92. 'data-provider-dst':json.dumps(dst_list)
  93. }
  94. )
  95. html = ['<div %s>' % html_params(name=field.name, **kwargs)]
  96. html.append('</div>')
  97. return HTMLString(''.join(html))