Improve Lookup field to be more intuitive and show initial value. Move css out of base.html and into new static get_together.css. Fixes #57

This commit is contained in:
Michael Hall 2018-03-25 14:40:29 -04:00
parent 80b6374f4b
commit 3d5046e0c4
8 changed files with 101 additions and 73 deletions

View file

@ -4,29 +4,13 @@ from django.forms.widgets import TextInput, Media
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.models import User
from .models.locale import Country, SPR, City
from .models.profiles import Team, UserProfile
from .models.events import Event, EventComment ,CommonEvent, Place, EventPhoto
from datetime import time
from time import strptime, strftime
class LookupMedia(Media):
def render(self):
return mark_safe('''<script type="text/javascript"><script>
$(document).ready(function(){
$("#{{ widget.name }}_search").keyup(function() {
var searchText = this.value;
$.getJSON("{{ widget.source }}?q="+searchText, function(data) {
var selectField = $("#{{ widget.name }}_select");
selectField.empty();
$.each(data, function(){
selectField.append('<option value="'+ this.{{ widget.key }} +'">'+ this.{{ widget.label }} + '</option>')
});
});
});
});
</script>''')
class Lookup(TextInput):
input_type = 'text'
template_name = 'forms/widgets/lookup.html'
@ -34,7 +18,7 @@ class Lookup(TextInput):
checked_attribute = {'selected': True}
option_inherits_attrs = False
def __init__(self, source='#', key="id", label="name", attrs=None):
def __init__(self, source, key="id", label='__str__', attrs=None):
super().__init__(attrs)
self.source = source
self.key = key
@ -42,14 +26,18 @@ class Lookup(TextInput):
def get_context(self, name, value, attrs):
context = super().get_context(name, value, attrs)
context['widget']['source'] = self.source
context['widget']['key'] = self.key
context['widget']['label'] = self.label
return context
def format_value(self, value):
if value is not None:
return mark_safe('<option value="%s">%s</option>' % (value, _('No change')))
lookup_query = {self.key: value}
lookup_object = self.source.objects.get(**lookup_query)
lookup_field = getattr(lookup_object, self.label)
if callable(lookup_field):
lookup_value = lookup_field()
else:
lookup_value = lookup_field
return mark_safe('<option value="%s">%s</option>' % (value, lookup_value))
else:
return mark_safe('<option value="">--------</option>')
@ -152,7 +140,7 @@ class TeamForm(forms.ModelForm):
model = Team
fields = ['name', 'description', 'category', 'city', 'web_url', 'tz']
widgets = {
'city': Lookup(source='/api/cities/', label='name'),
'city': Lookup(source=City),
}
raw_id_fields = ('city')
def __init__(self, *args, **kwargs):
@ -164,7 +152,7 @@ class NewTeamForm(forms.ModelForm):
model = Team
fields = ['name', 'description', 'category', 'city', 'web_url', 'tz']
widgets = {
'city': Lookup(source='/api/cities/', label='name'),
'city': Lookup(source=City),
}
raw_id_fields = ('city')
def __init__(self, *args, **kwargs):
@ -179,7 +167,7 @@ class TeamEventForm(forms.ModelForm):
model = Event
fields = ['name', 'start_time', 'end_time', 'summary', 'place', 'web_url', 'announce_url', 'tags']
widgets = {
'place': Lookup(source='/api/places/', label='name'),
'place': Lookup(source=Place),
'start_time': DateTimeWidget,
'end_time': DateTimeWidget
}
@ -211,7 +199,7 @@ class NewPlaceForm(forms.ModelForm):
model = Place
fields = ['name', 'address', 'city', 'longitude', 'latitude', 'place_url', 'tz']
widgets = {
'city': Lookup(source='/api/cities/', label='name'),
'city': Lookup(source=City),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@ -244,11 +232,11 @@ class SendNotificationsForm(forms.ModelForm):
}
class SearchForm(forms.Form):
city = forms.IntegerField(required=False, widget=Lookup(source='/api/cities/', label='name'))
city = forms.IntegerField(required=False, widget=Lookup(source=City, label='name'))
distance = forms.IntegerField(label=_("Distance(km)"), required=True)
class Meta:
widgets ={
'city': Lookup(source='/api/cities/', label='name'),
'city': Lookup(source=City, label='name'),
}
class NewCommonEventForm(forms.ModelForm):
@ -272,10 +260,10 @@ class NewCommonEventForm(forms.ModelForm):
'tags',
]
widgets ={
'country': Lookup(source='/api/countries/', label='name'),
'spr': Lookup(source='/api/spr/', label='name'),
'city': Lookup(source='/api/cities/', label='name'),
'place': Lookup(source='/api/places/', label='name'),
'country': Lookup(source=Country, label='name'),
'spr': Lookup(source=SPR, label='name'),
'city': Lookup(source=City, label='name'),
'place': Lookup(source=Place, label='name'),
'start_time': DateTimeWidget,
'end_time': DateTimeWidget
}

View file

@ -44,12 +44,14 @@ class Country(models.Model):
return 'no_country'
class CountrySerializer(serializers.ModelSerializer):
display = serializers.CharField(source='__str__', read_only=True)
class Meta:
model = Country
fields = (
'id',
'name',
'code'
'code',
'display',
)
class SPR(models.Model):
@ -94,6 +96,13 @@ class City(models.Model):
longitude = models.FloatField(help_text=_('Longitude in Degrees East'), null=True, blank=True)
latitude = models.FloatField(help_text=_('Latitude in Degrees North'), null=True, blank=True)
@property
def short_name(self):
if self.spr.country.name == 'United States':
return u'%s, %s' % (self.name, self.spr.name)
else:
return u'%s, %s' % (self.name, self.spr.country.name)
def __str__(self):
return u'%s, %s, %s' % (self.name, self.spr.name, self.spr.country.name)
@ -112,6 +121,7 @@ class CitySerializer(serializers.ModelSerializer):
fields = (
'id',
'name',
'short_name',
'spr',
'tz',
'slug',

View file

@ -1,4 +1,6 @@
<select autocomplete="false" id="{{ widget.name }}_select" name="{{ widget.name }}"{% include "django/forms/widgets/attrs.html" %} style="width: 200px">
{{ widget.value }}
<span class="gt_lookup">
<select autocomplete="false" id="{{ widget.name }}_select" name="{{ widget.name }}"{% include "django/forms/widgets/attrs.html" %} >
{{ widget.value }}
</select>
</span>

View file

@ -0,0 +1,40 @@
body {
padding-top: 5rem;
}
.footer {
padding-top: 1em;
padding-bottom: 1em;
border-top: #cfcfcf 1px solid;
background-color: #f5f5f5;
color: #aaaaaa;
}
.footer ul {
display: inline;
list-style-type: none;
margin-left: 0.5em;
}
.footer ul li {
display: inline;
margin-left: 0.5em;
}
.footer a {
text-decoration: none;
color: inherit;
}
.starter-template {
padding: 3rem 1.5rem;
text-align: center;
}
form {
display: inline;
}
.ui-selectmenu-menu .ui-menu {
height: 200px;
}
.gt_lookup .ui-selectmenu-icon.ui-icon {
background-position: 0px 0px;
background-image: url(/static/img/search_icon.png);
width: 16px;
height: 16px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 783 B

View file

@ -22,7 +22,7 @@
_drawSearch: function() {
var self = this;
var selectField = this.element[0];
this.searchField = $('<input type="text" placeholder="Search" style="width: 100%;">');
this.searchField = $('<input type="text" class="ui-lookup-search border border-primary" placeholder="Search" style="width: 100%;">');
//this._addClass( this.searchField, "ui-selectmenu-menu", "ui-front" );
this.searchField.keyup(function() {
self.options.search(this.value, function(searchText, results){
@ -39,8 +39,11 @@
});
});
this.menuWrap.prepend(this.searchField);
},
open: function(event) {
this._super()
this.searchField.focus()
}
});
}(jQuery));

View file

@ -28,43 +28,11 @@
<link rel="stylesheet" href="{% static 'css/bootstrap/css/bootstrap.min.css' %}">
<link href="{% static 'js/tether/css/tether.min.css' %}" rel="stylesheet">
<link href="{% static 'js/jquery-ui/jquery-ui.min.css' %}" rel="stylesheet">
<link href="{% static 'css/get_together.css' %}" rel="stylesheet">
<!-- style overrides -->
{%block styles %}{% endblock %}
<!-- Bootstrap style overrides -->
<style>
body {
padding-top: 5rem;
}
.footer {
padding-top: 1em;
padding-bottom: 1em;
border-top: #cfcfcf 1px solid;
background-color: #f5f5f5;
color: #aaaaaa;
}
.footer ul {
display: inline;
list-style-type: none;
margin-left: 0.5em;
}
.footer ul li {
display: inline;
margin-left: 0.5em;
}
.footer a {
text-decoration: none;
color: inherit;
}
.starter-template {
padding: 3rem 1.5rem;
text-align: center;
}
form {
display: inline;
}
.ui-selectmenu-menu .ui-menu {
height: 200px;
}
</style>
</style>
</head>
<body>

View file

@ -43,6 +43,7 @@ def home(request, *args, **kwards):
near_distance = int(request.GET.get("distance", DEFAULT_NEAR_DISTANCE))
context['distance'] = near_distance
city=None
ll = None
if "city" in request.GET and request.GET.get("city"):
context['city_search'] = True
@ -56,6 +57,22 @@ def home(request, *args, **kwards):
if g.latlng is not None and g.latlng[0] is not None and g.latlng[1] is not None:
ll = g.latlng
context['geoip_lookup'] = True
try:
city_distance = 1 #km
while city is None and city_distance < 100:
minlat = ll[0]-(city_distance/KM_PER_DEGREE_LAT)
maxlat = ll[0]+(city_distance/KM_PER_DEGREE_LAT)
minlng = ll[1]-(city_distance/(KM_PER_DEGREE_LNG*math.cos(math.radians(ll[0]))))
maxlng = ll[1]+(city_distance/(KM_PER_DEGREE_LNG*math.cos(math.radians(ll[0]))))
nearby_cities = City.objects.filter(latitude__gte=minlat, latitude__lte=maxlat, longitude__gte=minlng, longitude__lte=maxlng)
if len(nearby_cities) == 0:
city_distance += 1
else:
city = nearby_cities[0]
except:
pass # City lookup failed
except Exception as err:
context['geoip_lookup'] = False
print("Geocoder lookup failed for %s" % client_ip, err)
@ -84,7 +101,7 @@ def home(request, *args, **kwards):
print("Error looking up nearby teams and events", err)
traceback.print_exc()
search_form = SearchForm(initial={'distance': near_distance})
search_form = SearchForm(initial={'city': city.id, 'distance': near_distance})
context['search_form'] = search_form
return render(request, 'get_together/index.html', context)