From be0bb4e123240ac3ce02431b974c82f8f05d843b Mon Sep 17 00:00:00 2001 From: luxagraf Date: Wed, 9 Jan 2019 13:29:17 -0600 Subject: latest changes --- TODO | 29 +- apps/accounts/forms.py | 11 +- .../accounts/migrations/0002_auto_20190108_2115.py | 35 ++ apps/accounts/models.py | 12 +- apps/accounts/urls.py | 6 + apps/accounts/views.py | 2 +- apps/notes/models.py | 4 +- apps/notes/notebook_urls.py | 2 + apps/notes/views.py | 48 ++- apps/utils/views.py | 2 + config/base_urls.py | 10 +- config/settings.py | 11 +- design/sass/_choices.scss | 382 +++++++++++++++++++++ design/sass/_forms.scss | 38 +- design/sass/_global.scss | 16 + design/sass/_header.scss | 13 + design/sass/_notes.scss | 125 ++++++- design/sass/screenv1.scss | 1 + design/templates/accounts/change-settings.html | 33 ++ design/templates/accounts/profile.html | 14 +- design/templates/base.html | 2 +- design/templates/notes/notebook_detail.html | 18 +- design/templates/notes/notebook_list.html | 63 +++- design/templates/notes/notes_detail.html | 3 +- design/templates/notes/notes_list.html | 40 +-- design/templates/notes/partials/list_header.html | 19 + design/templates/notes/partials/notebook_form.html | 4 +- scripts/babel.config.js | 13 +- scripts/package.json | 2 +- scripts/src/account-edit.js | 1 + scripts/src/note-edit.js | 8 +- scripts/src/note-list.js | 66 ++-- scripts/src/notebook-edit.js | 136 ++++---- scripts/src/util.js | 49 ++- 34 files changed, 969 insertions(+), 249 deletions(-) create mode 100644 apps/accounts/migrations/0002_auto_20190108_2115.py create mode 100644 design/sass/_choices.scss create mode 100644 design/templates/accounts/change-settings.html create mode 100644 design/templates/notes/partials/list_header.html create mode 100644 scripts/src/account-edit.js diff --git a/TODO b/TODO index 751e018..eb97713 100644 --- a/TODO +++ b/TODO @@ -1,20 +1,14 @@ ## Note and Notebooks + * add overlay "are you sure" then delete notebook * side by side note view for comparing/adding annotations * add delete note option to list - * notebook main -- add form for editing all notebooks - * add search to notebook list on create and edit note pages - * covert notebook and tag menus to searchable dropdown - - menu default as an overflow div maybe? Select list? - - hide select list with JS, pull data into div - - add event to button to reveal menu - - add input box to top of menu for search - - make search hide menu items as you search - - make return open link + * add image parsing routine to avoid hotlinking ## Accounts - * make settings actually editable - * add settings for showing color + * make photo display in bio page and show file name in upload form + * make full name edittable + * add password change field * hook into groups to see if user is allowed to add more notebooks * add payment via stripe @@ -25,13 +19,24 @@ - position - dates - ## Outlines + * generate outline from tag/notebook??? + # Done + * add search to notebook list on create and edit note pages * port modal login to new modal system * add defailt "trash" notebook and note manager to not show notes in the trash. * refactor models to get rid of null on TextFields * add loading animation when saving, hide when done. - need new type of modal with no close btn - need to be resizeable + * notebook main -- add form for editing all notebooks + * get rid of flexbox for notebook list + * covert notebook and tag menus to searchable dropdown + - menu default as an overflow div maybe? Select list? + - hide select list with JS, pull data into div + - add event to button to reveal menu + - add input box to top of menu for search + - make search hide menu items as you search + - make return open link diff --git a/apps/accounts/forms.py b/apps/accounts/forms.py index d53e754..2da08cb 100644 --- a/apps/accounts/forms.py +++ b/apps/accounts/forms.py @@ -1,4 +1,5 @@ from django import forms +from django.utils.translation import ugettext_lazy as _ from django_registration.forms import RegistrationForm from .models import User, UserProfile @@ -12,7 +13,15 @@ class UserForm(RegistrationForm): class ProfileForm(forms.ModelForm): class Meta: model = UserProfile - fields = '__all__' + fields = ['bio', 'photo', 'website'] + labels = { + "photo": _("Profile photo"), + "bio": _("Bio. A little about you. links are fine, line breaks are not. Keep it short and sweet, 350 characters max"), + "website": _("If you have a personal website, plug it in here."), + } + widgets = { + 'bio': forms.Textarea(attrs={'cols': 104, 'rows': 10, 'class': 'textarea-rounded'}), + } def __init__(self, *args, **kwargs): self.user = kwargs.pop("user", None) diff --git a/apps/accounts/migrations/0002_auto_20190108_2115.py b/apps/accounts/migrations/0002_auto_20190108_2115.py new file mode 100644 index 0000000..1ebb280 --- /dev/null +++ b/apps/accounts/migrations/0002_auto_20190108_2115.py @@ -0,0 +1,35 @@ +# Generated by Django 2.1.2 on 2019-01-09 03:15 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('accounts', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='userprofile', + name='bio', + field=models.CharField(blank=True, default='', max_length=350), + ), + migrations.AlterField( + model_name='userprofile', + name='location', + field=models.CharField(blank=True, default='', max_length=300), + ), + migrations.AlterField( + model_name='userprofile', + name='user', + field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='profile', to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='userprofile', + name='website', + field=models.CharField(blank=True, default='', max_length=300), + ), + ] diff --git a/apps/accounts/models.py b/apps/accounts/models.py index 1b62ffd..feb20bf 100644 --- a/apps/accounts/models.py +++ b/apps/accounts/models.py @@ -1,4 +1,5 @@ from django.db import models +from django.urls import reverse from django.contrib.auth.models import AbstractUser from django.utils.functional import cached_property @@ -15,15 +16,18 @@ class User(AbstractUser): class UserProfile(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile') photo = models.ImageField(upload_to='profile', null=True, blank=True) - website = models.CharField(max_length=300, null=True, blank=True, default='') - location = models.CharField(max_length=300, null=True, blank=True, default='') - bio = models.TextField(null=True, blank=True, default='') + website = models.CharField(max_length=300, blank=True, default='') + location = models.CharField(max_length=300, blank=True, default='') + bio = models.CharField(max_length=350, blank=True, default='') + #default_note_folder = models.ForeignKey('notes.Notebook', null=True, on_delete=models.SET_NULL) #default_note_public = models.BooleanField(default=False) - #default_note_folder = models.ForeignKey('notes.Folder', null=True, on_delete=models.SET_NULL) def __str__(self): return self.user.username + def get_absolute_url(self): + return reverse("accounts:settings") + @cached_property def get_notebook_list(self): return Notebook.objects.filter(owner=self.user).select_related().annotate(note_count=models.Count('note'))[:8] diff --git a/apps/accounts/urls.py b/apps/accounts/urls.py index d9ee327..5cad311 100644 --- a/apps/accounts/urls.py +++ b/apps/accounts/urls.py @@ -2,8 +2,14 @@ from django.urls import path from . import views +app_name = "accounts" urlpatterns = [ + path( + r'change-profile/', + views.ProfileView.as_view(), + name="change-profile" + ), path( r'', views.SettingsListView.as_view(), diff --git a/apps/accounts/views.py b/apps/accounts/views.py index d463522..75bb933 100644 --- a/apps/accounts/views.py +++ b/apps/accounts/views.py @@ -18,7 +18,7 @@ class UpdateViewWithUser(UpdateView): class ProfileView(UpdateViewWithUser): model = UserProfile form_class = ProfileForm - template_name = "accounts/profile.html" + template_name = "accounts/change-settings.html" def get_object(self): return self.request.user.profile diff --git a/apps/notes/models.py b/apps/notes/models.py index bfcb8ba..c4b1df6 100644 --- a/apps/notes/models.py +++ b/apps/notes/models.py @@ -54,13 +54,13 @@ class Notebook(models.Model): class Meta: unique_together = ("owner", "name") + ordering = ("name", "date_created", "date_updated") def __str__(self): return self.name def save(self, **kwargs): - if self._state.adding: - self.slug = unique_slug_generator(self) + self.slug = unique_slug_generator(self) super(Notebook, self).save() @cached_property diff --git a/apps/notes/notebook_urls.py b/apps/notes/notebook_urls.py index b433d15..8e3133e 100644 --- a/apps/notes/notebook_urls.py +++ b/apps/notes/notebook_urls.py @@ -3,11 +3,13 @@ from django.urls import path from .views import ( NotebookListView, NotebookDetailView, + NotebookUpdateView, ) app_name = "notebooks" urlpatterns = [ + path(r'/update', NotebookUpdateView.as_view(), name='update',), path(r'', NotebookDetailView.as_view(), name='detail',), path(r'', NotebookListView.as_view(), name='list',), ] diff --git a/apps/notes/views.py b/apps/notes/views.py index 245e2ab..280020a 100644 --- a/apps/notes/views.py +++ b/apps/notes/views.py @@ -90,6 +90,7 @@ class NoteDetailView(LoggedInViewWithUser, AjaxableResponseMixin, UpdateView): return context def form_valid(self, form): + print(self.request.META) if "trash" in self.request.POST: form.instance.notebook = Notebook.include_trash.get(owner=self.request.user, name="Trash") self.object = form.save() @@ -155,6 +156,8 @@ class NoteTagView(BaseListView): context = super().get_context_data(**kwargs) #context['notes_list'] = Note.objects.filter(owner=self.request.user) context['tags'] = LuxTag.objects.filter(slug__in=self.tag_list) + context['tag_list'] = LuxTag.objects.filter(note__owner=self.request.user).annotate(note_count=Count('note')) + context['notebook_list'] = Notebook.objects.filter(owner=self.request.user).annotate(note_count=Count('note')) return context @@ -174,23 +177,48 @@ class NotebookListView(LoggedInViewWithUser, CreateView): def form_valid(self, form): form.instance.owner = self.request.user - self.object = form.save() + if "create" in self.request.POST: + self.object = form.save() + messages.info(self.request, 'The notebook %s has been created, you may edit it below.' % (self.object.name)) + return redirect('notebooks:list') return super(NotebookListView, self).form_valid(form) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - NotebookFormSet = modelformset_factory(Notebook, form=NotebookForm, extra=0) - context['notebook_form_list'] = NotebookFormSet(queryset=Notebook.objects.filter(owner=self.request.user).select_related().annotate(note_count=Count('note'))) - #context['notebook_list'] = Notebook.objects.filter(owner=self.request.user).exclude(name="Trash").select_related().annotate(note_count=Count('note')) - #context['notes_list'] = Note.objects.filter(owner=self.request.user).select_related() + forms = [] + for n in Notebook.objects.filter(owner=self.request.user).prefetch_related('note_set').prefetch_related('note_set__tags').annotate(note_count=Count('note')): + #notes = [] + #tags = set(tag for tag in notes.tags_set.all().distinct() for notes in n.note_set.all()) + #n.tag_list = tags + n.tag_list = LuxTag.objects.filter(note__in=n.note_set.all()).distinct()[:3] + forms.append(NotebookForm(instance=n)) + context['notebook_form_list'] = forms + #context['notebook_list'] = Notebook.objects.filter(owner=self.request.user).select_related().annotate(note_count=Count('note')) return context - def get_success_url(self): - return reverse_lazy('notebooks:detail', kwargs={'slug': self.object.slug}) +class NotebookUpdateView(LoggedInViewWithUser, UpdateView): + model = Notebook + form_class = NotebookForm + template_name = 'notes/notebook_list.html' -class NotebookDetailView(BaseDetailView): + def get_queryset(self): + if not self.request.user.is_anonymous: + return Notebook.objects.filter(owner=self.request.user) + + def form_valid(self, form): + form.instance.owner = self.request.user + self.object = form.save() + data = { + 'notebook': {'name': self.object.name, 'color': self.object.color_rgb, 'permalink': self.object.slug} + } + return JsonResponse(data, safe=True) + + +class NotebookDetailView(LoggedInViewWithUser, AjaxableResponseMixin, UpdateView): model = Notebook + form_class = NotebookForm + template_name = 'notes/notebook_detail.html' def get_queryset(self): if not self.request.user.is_anonymous: @@ -200,7 +228,7 @@ class NotebookDetailView(BaseDetailView): return Notebook.objects.filter(owner=self.request.user) def get_object(self): - notebook = get_object_or_404(self.get_queryset().select_related(), owner=self.request.user, slug=self.kwargs["slug"]) + notebook = get_object_or_404(self.get_queryset().prefetch_related('note_set').prefetch_related('note_set__tags'), owner=self.request.user, slug=self.kwargs["slug"]) self.form = NotebookForm(instance=notebook) return notebook @@ -208,6 +236,8 @@ class NotebookDetailView(BaseDetailView): context = super().get_context_data(**kwargs) #context['notes_list'] = Note.objects.filter(owner=self.request.user).select_related() context['form'] = self.form + context['tag_list'] = LuxTag.objects.filter(note__owner=self.request.user).annotate(note_count=Count('note')) + context['notebook_list'] = Notebook.objects.filter(owner=self.request.user).annotate(note_count=Count('note')) return context diff --git a/apps/utils/views.py b/apps/utils/views.py index ec3a902..c752706 100644 --- a/apps/utils/views.py +++ b/apps/utils/views.py @@ -59,6 +59,8 @@ class AjaxableResponseMixin: # We make sure to call the parent's form_valid() method because # it might do some processing (in the case of CreateView, it will # call form.save() for example). + print(self.request.META) + print('x request header', self.request.META['HTTP_X_REQUESTED_WITH']) response = super().form_valid(form) if self.request.is_ajax(): data = { diff --git a/config/base_urls.py b/config/base_urls.py index efbe233..ed4bd91 100644 --- a/config/base_urls.py +++ b/config/base_urls.py @@ -45,12 +45,12 @@ urlpatterns += [ #path(r'//', PageDetailView.as_view(), name="pages"), path(r'api-auth/', include('rest_framework.urls', namespace='rest_framework')) ] -if settings.DEBUG: - import debug_toolbar - urlpatterns = [ - path('__debug__/', include(debug_toolbar.urls)), +#if settings.DEBUG: + #import debug_toolbar + #urlpatterns = [ + # path('__debug__/', include(debug_toolbar.urls)), # For django versions before 2.0: # url(r'^__debug__/', include(debug_toolbar.urls)), - ] + urlpatterns + #] + urlpatterns diff --git a/config/settings.py b/config/settings.py index c7bef91..dcd14ee 100644 --- a/config/settings.py +++ b/config/settings.py @@ -24,9 +24,14 @@ DEBUG = config('DEBUG', cast=bool) AUTH_PROFILE_MODULE = "accounts.UserProfile" AUTH_USER_MODEL = "accounts.User" -DEFAULT_FILE_STORAGE = config('DEFAULT_FILE_STORAGE') INTERNAL_IPS = config('INTERNAL_IPS') + +DEFAULT_FILE_STORAGE = config('DEFAULT_FILE_STORAGE') +AWS_ACCESS_KEY_ID = config('AWS_ACCESS_KEY_ID') +AWS_SECRET_ACCESS_KEY = config('AWS_SECRET_ACCESS_KEY') +AWS_STORAGE_BUCKET_NAME = config('AWS_STORAGE_BUCKET_NAME') +AWS_DEFAULT_ACL = config('AWS_DEFAULT_ACL') AWS_S3_OBJECT_PARAMETERS = { 'CacheControl': 'max-age=86400', } @@ -65,7 +70,7 @@ THIRD_PARTY_APPS = [ 'taggit_serializer', 'django_extensions', 'rest_framework', - 'debug_toolbar' + #'debug_toolbar' ] LOCAL_APPS = [ 'utils', @@ -82,7 +87,7 @@ MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', - 'debug_toolbar.middleware.DebugToolbarMiddleware', + #'debug_toolbar.middleware.DebugToolbarMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', diff --git a/design/sass/_choices.scss b/design/sass/_choices.scss new file mode 100644 index 0000000..8073d77 --- /dev/null +++ b/design/sass/_choices.scss @@ -0,0 +1,382 @@ +.choices-wrapper { + max-width: inherit; +} +.choices-wrapper-notebooks { + width: 190px; +} +.choices-wrapper-tags { + width: 160px; + margin-left: 2rem; + .choices__list--dropdown { + width: 109%; + padding-right: 1px; + } +} +.choices { + position: relative; + margin-bottom: 24px; + font-size: 16px; + + &:focus { + outline: none; + } + + &:last-child { + margin-bottom: 0; + } + + &.is-disabled { + .choices__inner, .choices__input { + background-color: #eaeaea; + cursor: not-allowed; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + } + + .choices__item { + cursor: not-allowed; + } + } + + &[data-type*=select-one] { + cursor: pointer; + + .choices__inner { + padding-bottom: 7.5px; + } + + .choices__input { + display: block; + width: 100%; + padding: 10px; + border-bottom: 1px solid #ddd; + background-color: #fff; + margin: 0; + } + + .choices__button { + background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMSAyMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjMDAwIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik0yLjU5Mi4wNDRsMTguMzY0IDE4LjM2NC0yLjU0OCAyLjU0OEwuMDQ0IDIuNTkyeiIvPjxwYXRoIGQ9Ik0wIDE4LjM2NEwxOC4zNjQgMGwyLjU0OCAyLjU0OEwyLjU0OCAyMC45MTJ6Ii8+PC9nPjwvc3ZnPg==); + padding: 0; + background-size: 8px; + position: absolute; + top: 50%; + right: 0; + margin-top: -10px; + margin-right: 25px; + height: 20px; + width: 20px; + border-radius: 10em; + opacity: .5; + + &:focus, &:hover { + opacity: 1; + } + + &:focus { + box-shadow: 0 0 0 2px #00bcd4; + } + } + + &:after { + content: ""; + height: 0; + width: 0; + border-style: solid; + border-color: #333 transparent transparent transparent; + border-width: 5px; + position: absolute; + right: 11.5px; + top: 50%; + margin-top: -2.5px; + pointer-events: none; + } + + &.is-open:after { + border-color: transparent transparent #333 transparent; + margin-top: -7.5px; + } + + &[dir=rtl] { + &:after { + left: 11.5px; + right: auto; + } + + .choices__button { + right: auto; + left: 0; + margin-left: 25px; + margin-right: 0; + } + } + } + + &[data-type*=select-multiple] .choices__inner, &[data-type*=text] .choices__inner { + cursor: text; + } + + &[data-type*=select-multiple] .choices__button, &[data-type*=text] .choices__button { + position: relative; + display: inline-block; + margin: 0 -4px 0 8px; + padding-left: 16px; + border-left: 1px solid #008fa1; + background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMSAyMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjRkZGIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik0yLjU5Mi4wNDRsMTguMzY0IDE4LjM2NC0yLjU0OCAyLjU0OEwuMDQ0IDIuNTkyeiIvPjxwYXRoIGQ9Ik0wIDE4LjM2NEwxOC4zNjQgMGwyLjU0OCAyLjU0OEwyLjU0OCAyMC45MTJ6Ii8+PC9nPjwvc3ZnPg==); + background-size: 8px; + width: 8px; + line-height: 1; + opacity: .75; + border-radius: 0; + } + + &[data-type*=select-multiple] .choices__button { + &:focus, &:hover { + opacity: 1; + } + } + + &[data-type*=text] .choices__button { + &:focus, &:hover { + opacity: 1; + } + } +} + +.choices__inner { + display: inline-block; + vertical-align: top; + width: 100%; + background-color: #f9f9f9; + padding: 7.5px 7.5px 3.75px; + border: 1px solid #ddd; + border-radius: 2.5px; + font-size: 14px; + overflow: hidden; +} + +.is-focused .choices__inner { + border-color: #b7b7b7; +} + +.is-open .choices__inner { + border-color: #b7b7b7; + border-radius: 2.5px 2.5px 0 0; +} + +.is-flipped.is-open .choices__inner { + border-radius: 0 0 2.5px 2.5px; +} + +.choices__list { + margin: 0; + padding-left: 0; + min-width: inherit; + list-style: none; +} + +.choices__list--single { + display: inline-block; + padding: 4px 16px 4px 4px; + width: 100%; +} + +[dir=rtl] .choices__list--single { + padding-right: 4px; + padding-left: 16px; +} + +.choices__list--single .choices__item { + width: 100%; +} + +.choices__list--multiple { + display: inline; + + .choices__item { + display: inline-block; + vertical-align: middle; + border-radius: 20px; + padding: 4px 10px; + font-size: 12px; + font-weight: 500; + margin-right: 3.75px; + margin-bottom: 3.75px; + background-color: #00bcd4; + border: 1px solid #00a5bb; + color: #fff; + word-break: break-all; + + &[data-deletable] { + padding-right: 5px; + } + } +} + +[dir=rtl] .choices__list--multiple .choices__item { + margin-right: 0; + margin-left: 3.75px; +} + +.choices__list--multiple .choices__item.is-highlighted { + background-color: #00a5bb; + border: 1px solid #008fa1; +} + +.is-disabled .choices__list--multiple .choices__item { + background-color: #aaa; + border: 1px solid #919191; +} + +.choices__list--dropdown { + display: none; + z-index: 1; + position: absolute; + width: 108%; + background-color: #fff; + border: 1px solid #ddd; + top: 100%; + margin-top: -1px; + border-bottom-left-radius: 2.5px; + border-bottom-right-radius: 2.5px; + overflow: hidden; + word-break: break-all; + //padding-right: 1px; + &.is-active { + display: block; + } +} + +.is-open .choices__list--dropdown { + border-color: #b7b7b7; +} + +.is-flipped .choices__list--dropdown { + top: auto; + bottom: 100%; + margin-top: 0; + margin-bottom: -1px; + border-radius: .25rem .25rem 0 0; +} + +.choices__list--dropdown { + .choices__list { + position: relative; + max-height: 300px; + overflow: auto; + -webkit-overflow-scrolling: touch; + will-change: scroll-position; + } + + .choices__item { + position: relative; + padding: 10px; + font-size: 14px; + } +} + +[dir=rtl] .choices__list--dropdown .choices__item { + text-align: right; +} + +@media (min-width: 640px) { + .choices__list--dropdown .choices__item--selectable { + + } + + [dir=rtl] .choices__list--dropdown .choices__item--selectable { + text-align: right; + padding-left: 100px; + padding-right: 10px; + + &:after { + right: auto; + left: 10px; + } + } +} + +.choices__list--dropdown .choices__item--selectable.is-highlighted { + background-color: #f2f2f2; + + &:after { + opacity: .5; + } +} + +.choices__item { + cursor: default; +} + +.choices__item--selectable { + cursor: pointer; +} + +.choices__item--disabled { + cursor: not-allowed; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + opacity: .5; +} + +.choices__heading { + font-weight: 600; + font-size: 12px; + padding: 10px; + border-bottom: 1px solid #f7f7f7; + color: gray; +} + +.choices__button { + text-indent: -9999px; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + border: 0; + background-color: transparent; + background-repeat: no-repeat; + background-position: center; + cursor: pointer; + + &:focus { + outline: none; + } +} + +.choices__input { + display: inline-block; + vertical-align: baseline; + background-color: #f9f9f9; + font-size: 14px; + margin-bottom: 5px; + border: 0; + border-radius: 0; + max-width: 100%; + padding: 4px 0 4px 2px; + + &:focus { + outline: 0; + } +} + +[dir=rtl] .choices__input { + padding-right: 2px; + padding-left: 0; +} + +.choices__placeholder { + opacity: .5; +} + +.choices { + &[data-type*=select-multiple] .choices__input.is-hidden, &[data-type*=select-one] .choices__input.is-hidden { + display: none; + } +} + +.choices__input.is-hidden { + display: none; +} diff --git a/design/sass/_forms.scss b/design/sass/_forms.scss index 152e0ac..f9b8b21 100644 --- a/design/sass/_forms.scss +++ b/design/sass/_forms.scss @@ -16,18 +16,45 @@ form fieldset label { -o-transform: translate3d(10px, 100%); transform: translate3d(10px, 100%); @include fontsize(14); + @include fancy_sans; color: lighten($body_font_color, 25); line-height: 14px; top: .7rem; left: .75rem; z-index: 1; } -input { +input, .textarea-rounded { border: 1px solid #b3b3b3; border-radius: 4px; padding: 2.2rem 0 .75rem .75rem; width: 99%; @include fontsize(24); + @include fancy_sans; +} +.file-upload-fieldset { + border: 1px solid #b3b3b3; + border-radius: 4px; + padding: 6px 0 0 4px; + label { + position: relative; + display: block; + margin-bottom: 1rem; + } + .file-upload { + position: relative; + overflow: hidden; + margin: 10px; + } + .file-upload input.upload { + position: absolute; + top: 0; + right: 0; + margin: 0; + font-size: 20px; + cursor: pointer; + opacity: 0; + filter: alpha(opacity=0); + } } form .error input { border: 1px solid $link_color; @@ -90,6 +117,15 @@ table { @include fontsize(10); @include smcaps; } +.btn-more { + padding: 5px 7px; + border: none; + outline: $link_color; + background: $body_font_light !important; + &:hover { + border: none; //1px solid; + } +} .btn-subtle { padding: 3px 5px; border: none; //1px solid $body_font_light; diff --git a/design/sass/_global.scss b/design/sass/_global.scss index 41a1682..ff23500 100644 --- a/design/sass/_global.scss +++ b/design/sass/_global.scss @@ -207,12 +207,19 @@ h3 { .vertical li { display: block; } +.strong { + @include fancy_sans_bold; + font-weight: 500; +} .block { display: block; } .inline-block { display: inline-block; } +.inline{ + display: inline; +} .single-col { display: block; @include constrain(66%); @@ -233,6 +240,15 @@ h3 { .float-right { float: right; } +.float-left { + float: left; +} +.row-wrapper { + @extends %clearfix; +} +.space-between { + justify-content: space-between; +} //************** other global classes ************************ .sans { @include generic_sans; diff --git a/design/sass/_header.scss b/design/sass/_header.scss index 8df6667..1214f9a 100644 --- a/design/sass/_header.scss +++ b/design/sass/_header.scss @@ -4,6 +4,8 @@ background: #fff; } header { + display: flex; + justify-content: space-between; @include constrain_wide; .left { float: left; @@ -20,6 +22,17 @@ header { margin-top: -2px; } } +.logo { + display: block; + margin: 3px 0 0 0; + width: 60px; + height: 60px; + background: #fff url('/media/img/logo-sm.svg') no-repeat; + //background: #fff url('/media/img/logo-blk.svg') no-repeat; + //background: #fff url('/media/img/logo-org.svg') no-repeat; + background-size: 60px 60px; + text-align: center; +} nav { letter-spacing: 1px; margin: 0 0 10px; diff --git a/design/sass/_notes.scss b/design/sass/_notes.scss index 59e76ce..df25c3c 100644 --- a/design/sass/_notes.scss +++ b/design/sass/_notes.scss @@ -31,10 +31,10 @@ main { max-width: 320px; .list-note-preview { li { - height: 4.5rem; + height: 4.6rem; } h4 { - @include fontsize(15); + @include fontsize(18); } } .note-preview { @@ -65,11 +65,10 @@ main { list-style-type: none; li { @include fontsize(13); - padding: 8px 0 12px; + padding: 16px 0 16px 16px; min-height: 4.5rem; box-shadow: 0 -1px 0 #e7e2ee inset; margin: 0; - padding: 6px 12px; &:hover { background: #f7f7f7; } @@ -79,7 +78,7 @@ main { } } h4 { - @include fontsize(16); + @include fontsize(18); margin: 0; padding-bottom: 8px; font-weight: normal; @@ -249,7 +248,7 @@ main { height: 20px; border-radius: 4px; border: 1px solid $body_font_light; - #id_color_rgb { + #id_color_rgb, .input-color-rgb { display: block; text-indent: -1000em; padding: 0; @@ -257,6 +256,9 @@ main { background: transparent; cursor: pointer; } + .input-color-rgb { + display: inline; + } } #nb-create-form { .color-picker-fieldset { @@ -269,9 +271,9 @@ main { } } .nb-name { - margin: 1rem 2rem 1rem 0; - width: 90%; -} + margin: 1rem 2rem 1rem 0; + width: 90%; + } } .small-circle { width: 18px; @@ -286,7 +288,19 @@ main { margin: auto 5px; width: 8px; } - +.small-circle.push-top { + margin-top: 24px; +} +.note-container .flex-wrapper .hed-small { + flex-grow: 1; +} +#list-header { + margin-top: 10px; +} +.choices__list .nb-name { + @include fontsize(14); + margin: 0 .25rem; +} .url-field { input { @@ -307,25 +321,100 @@ main { } .nb-list { - display: flex; - flex-wrap: wrap; - align-items: center; - margin-top: 0; } .nb-list-item { + @extend %clearfix; list-style-type: none; - padding: 2rem; - margin: 1rem; - flex-grow: 1; + padding: .5rem 1rem; + margin: 1rem 1rem 1rem 0; border: 1px #e7e2ee solid; border-radius: 4px; - min-width: 160px; + a { + text-decoration: none; + color: $body_font_color; + } + p { + margin: 0; + @include fontsize(13); + @include fancy_sans_bold; + text-transform: uppercase; + + } + ul { + list-style-type: none; + padding: 0; + margin-left: .25rem; + margin-top: .5rem; + position: relative; + overflow-y: hidden; + .more { + position: absolute; + bottom: 0; + left: 0; + width: 100%; + margin: 0 ; + padding: 15px 0 0 0; + /* "transparent" only works here because == rgba(0,0,0,0) */ + background-image: linear-gradient(to bottom, transparent, white); + } + li { + + } + } + .nb-name { + flex-grow: 1; + margin: 0; + a { + line-height: 2.3; + @include fontsize(22); + } + } + .tags { + @include fontsize(13); + @include fancy_sans; + text-transform: uppercase; + margin: 1rem 0 0 .25rem; + ul { + @include fancy_sans; + text-transform: none; + } + + } + .trash { + margin-top: 4px; + color: #333; + } } .color-picker-inner { width: 100%; height: 100%; } +.slideup, .slidedown { + height: 120px; + overflow-y: hidden; + -webkit-transition: height .5s ease-in; + -moz-transition: height .5s ease-in; + -o-transition: height .5s ease-in; + transition: height .5s ease-in; +} +.slidedown { + height: 220px; + max-height: 260px; +} +.newnnbslideup, .newnbslidedown { + height: 0px; + overflow-y: hidden; + -webkit-transition: height .5s ease-in; + -moz-transition: height .5s ease-in; + -o-transition: height .5s ease-in; + transition: height .5s ease-in; +} +.newnbslidedown { + height: 220px; + max-height: 260px; +} + .dropmenu-search { margin: 0; padding: 0; diff --git a/design/sass/screenv1.scss b/design/sass/screenv1.scss index de2593e..b55f9d9 100644 --- a/design/sass/screenv1.scss +++ b/design/sass/screenv1.scss @@ -3,6 +3,7 @@ @import "_queries.scss"; @import "_global.scss"; @import "_icons.scss"; +@import "_choices.scss"; @import "_header.scss"; @import "_footer.scss"; @import "_forms.scss"; diff --git a/design/templates/accounts/change-settings.html b/design/templates/accounts/change-settings.html new file mode 100644 index 0000000..a6e6ea6 --- /dev/null +++ b/design/templates/accounts/change-settings.html @@ -0,0 +1,33 @@ +{% extends 'base.html' %} +{% block content %} +
+

Update Your Account Settings

+
{% csrf_token %} + {% for field in form %} +
{% if field.name == 'photo' %} + {{field.label_tag}} +
+
+ Upload + +
+ +
+ {% else %} + {{field.label_tag}} + {{field}} + {%endif%}
+ {% endfor %} +

+
+
+ +{% endblock %} diff --git a/design/templates/accounts/profile.html b/design/templates/accounts/profile.html index e8ab65e..4e52da1 100644 --- a/design/templates/accounts/profile.html +++ b/design/templates/accounts/profile.html @@ -31,22 +31,12 @@ Bio {{object.bio}} - Change + Change Photo {{object.photo}} - Change - - - Website - {{object.website}} - Change - - - Location - {{object.location}} - Change + Change diff --git a/design/templates/base.html b/design/templates/base.html index adcf599..e02719b 100644 --- a/design/templates/base.html +++ b/design/templates/base.html @@ -51,7 +51,7 @@
  • Community
  • Tour
  • How to
  • {% if not request.user.is_anonymous %} -
  • Account +
  • Account
    • Account settings
    • diff --git a/design/templates/notes/notebook_detail.html b/design/templates/notes/notebook_detail.html index 37c8f8a..49717ca 100644 --- a/design/templates/notes/notebook_detail.html +++ b/design/templates/notes/notebook_detail.html @@ -7,14 +7,15 @@ {% block content %}
      + {% include "notes/partials/list_header.html" with notebook_list=notebook_list tag_list=tag_list %}
      {% if object.name != 'Trash'%}
      {% csrf_token %} {{ form.non_field_errors }}
      {% for field in form %}{% if field.name == 'color_rgb' %} -
      - +
      + {% if field.errors %}{{field.errors}}{% endif %}
      {% else %}
      @@ -33,19 +34,6 @@ {%endif%}
      - - - {% csrf_token %} - {{ form.non_field_errors }} - {% for field in form %} -
      - {{field.label_tag}} - {{field}} - {% if field.errors %}{{field.errors}}{% endif %} -
      - {% endfor %} -

      -
        {% for object in object.note_set.all %} {% include "notes/partials/note_list.html" with object=object hidecolor=True hidenotebook=True %} {% endfor %}
      diff --git a/design/templates/notes/notebook_list.html b/design/templates/notes/notebook_list.html index 6f25f62..5f1220f 100644 --- a/design/templates/notes/notebook_list.html +++ b/design/templates/notes/notebook_list.html @@ -6,32 +6,69 @@ {% endblock %} {% block content %}
      -
      -

      Add a Notebook

      - {% include 'notes/partials/notebook_form.html' with form=form %} +
      +

      Notebooks

      - + New +
      + {% if messages %} +
        + {% for message in messages %} + + {% if 'safe' in message.tags %}{{ message|safe }}{% else %}{{ message }}{% endif %} + + {% endfor %} +
      + {% endif %} +
      + {% include 'notes/partials/notebook_form.html' with form=form url='' %} +
        {% for form in notebook_form_list %}
      • -
        {% for field in form %}{% if field.name == 'color_rgb' %} -
        - + {% csrf_token %} + {% for field in form %}{% if field.name == 'color_rgb' %} +
        +
        + {% if field.errors %}{{field.errors}}{% endif %}
        - href="{% url 'notebooks:detail' form.instance.slug %}" -

        {{form.instance.name}}

        - {% else %} +

        {{form.instance.name}}

        + +
        + {% elif field.name == 'name' %}
        + + +
        + {% else %} {% if field.field.widget.input_type != 'hidden' %}{{field.label_tag}}{% endif %} {{field}} {% if field.errors %}{{field.errors}}{% endif %} -
        {% endif %}{% endfor %} + {% endif %}{% endfor %} + +
        +

        {{form.instance.note_count}} notes{% if form.instance.note_count > 5 %}, most recent:{%endif%}

        + {% if form.instance.note_count > 0 %}
          {% for note in form.instance.note_set.all %} +
        • {{note.title}}
        • + {%endfor%} + {% if form.instance.note_count > 5 %}
        • {%endif%} +
        {%endif%} + {% if form.instance.tag_list %}
        Common tags:
        {%endif%} + +
        - -
      • {%endfor%}
      + + + + + {% comment %}
        {% for object in notebook_list %}
      • diff --git a/design/templates/notes/notes_detail.html b/design/templates/notes/notes_detail.html index cce13b7..0099f3d 100644 --- a/design/templates/notes/notes_detail.html +++ b/design/templates/notes/notes_detail.html @@ -69,7 +69,8 @@ -->
      - {% include 'notes/partials/notebook_form.html' with form=notebook_form %} + {% url 'notebook-api-list' as create_url %} + {% include 'notes/partials/notebook_form.html' with form=notebook_form url=create_url %}
      {% endblock %} diff --git a/design/templates/notes/notes_list.html b/design/templates/notes/notes_list.html index 94e82ec..0799a1c 100644 --- a/design/templates/notes/notes_list.html +++ b/design/templates/notes/notes_list.html @@ -1,42 +1,20 @@ {% extends 'base.html' %} - {% block content %}
      + {% include "notes/partials/list_header.html" with notebook_list=notebook_list tag_list=tag_list %}

      Notes

      {% if messages %} -
        - {% for message in messages %} - - {% if 'safe' in message.tags %}{{ message|safe }}{% else %}{{ message }}{% endif %} - - {% endfor %} -
      +
        + {% for message in messages %} + + {% if 'safe' in message.tags %}{{ message|safe }}{% else %}{{ message }}{% endif %} + + {% endfor %} +
      {% endif %} {% if tags|length >= 1%}

      Tagged with: {% for tag in tags%}{{tag.name}}{%endfor%}

      {%endif%} - -
      - - -
      -
      {%comment%} + diff --git a/scripts/babel.config.js b/scripts/babel.config.js index 0280c3d..a5d3541 100644 --- a/scripts/babel.config.js +++ b/scripts/babel.config.js @@ -1,16 +1,5 @@ const presets = [ - [ - "@babel/env", - { - targets: { - edge: "15", - firefox: "40", - chrome: "47", - safari: "10.1", - }, - useBuiltIns: "usage", - }, - ], + ["@babel/preset-env", {"modules": false}] ]; module.exports = { presets }; diff --git a/scripts/package.json b/scripts/package.json index 4b55662..6edaf7b 100644 --- a/scripts/package.json +++ b/scripts/package.json @@ -21,7 +21,7 @@ "devDependencies": { "@babel/cli": "^7.1.5", "@babel/core": "^7.1.6", - "@babel/preset-env": "^7.1.6", + "@babel/preset-env": "^7.2.3", "@babel/standalone": "^7.2.5", "babel-plugin-transform-es2015-modules-commonjs": "^6.26.2", "babel-plugin-transform-runtime": "^6.23.0", diff --git a/scripts/src/account-edit.js b/scripts/src/account-edit.js new file mode 100644 index 0000000..c9d3e9a --- /dev/null +++ b/scripts/src/account-edit.js @@ -0,0 +1 @@ +getElementById('FileField').value = getElementById('file-upload-wrapper').value; diff --git a/scripts/src/note-edit.js b/scripts/src/note-edit.js index 3d01794..390b93d 100644 --- a/scripts/src/note-edit.js +++ b/scripts/src/note-edit.js @@ -75,6 +75,8 @@ function edit_note(btn, form, title, quill, qcontainer, url){ var modal = modalBox(saveModal); document.getElementById('close-btn').classList.add('hide'); //send the request to the REST API + console.log(request); + console.log(form); request.send(new FormData(form)); } title.setAttribute('contenteditable', false); @@ -99,7 +101,8 @@ function notebookCreate(form) { request.open('POST', url); var csrftoken = getCookie('csrftoken'); request.setRequestHeader('X-CSRFToken', csrftoken); - request.setRequestHeader('HTTP_X_REQUESTED_WITH', 'XMLHttpRequest'); + request.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); + request.setRequestHeader("X-Requested-With", "XMLHttpRequest"); request.onload = function() { if (request.status >= 200 && request.status < 400) { var data = JSON.parse(request.responseText); @@ -161,9 +164,8 @@ if (typeof(document.getElementById('note-edit-form')) != 'undefined' && document e.preventDefault(); edit_note(e.target, form, title, quill, qcontainer, window.location.href ); }, false); - var notebook_form = document.getElementById('nb-create-form'); - initColorPicker(notebook_form); var notebookAddForm = document.getElementById('nb-create-form'); + initColorPicker(notebookAddForm); ajaxHijack(notebookAddForm, notebookCreate) var create_notebook_btn = document.getElementById('add_id_notebook'); addHandler(create_notebook_btn); diff --git a/scripts/src/note-list.js b/scripts/src/note-list.js index 90c89d9..2b82da1 100644 --- a/scripts/src/note-list.js +++ b/scripts/src/note-list.js @@ -1,33 +1,39 @@ -/*! List.js v1.5.0 (http://listjs.com) by Jonny Strömberg (http://javve.com) */ -var List=function(t){function e(n){if(r[n])return r[n].exports;var i=r[n]={i:n,l:!1,exports:{}};return t[n].call(i.exports,i,i.exports,e),i.l=!0,i.exports}var r={};return e.m=t,e.c=r,e.i=function(t){return t},e.d=function(t,r,n){e.o(t,r)||Object.defineProperty(t,r,{configurable:!1,enumerable:!0,get:n})},e.n=function(t){var r=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(r,"a",r),r},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=11)}([function(t,e,r){function n(t){if(!t||!t.nodeType)throw new Error("A DOM element reference is required");this.el=t,this.list=t.classList}var i=r(4),s=/\s+/;Object.prototype.toString;t.exports=function(t){return new n(t)},n.prototype.add=function(t){if(this.list)return this.list.add(t),this;var e=this.array(),r=i(e,t);return~r||e.push(t),this.el.className=e.join(" "),this},n.prototype.remove=function(t){if(this.list)return this.list.remove(t),this;var e=this.array(),r=i(e,t);return~r&&e.splice(r,1),this.el.className=e.join(" "),this},n.prototype.toggle=function(t,e){return this.list?("undefined"!=typeof e?e!==this.list.toggle(t,e)&&this.list.toggle(t):this.list.toggle(t),this):("undefined"!=typeof e?e?this.add(t):this.remove(t):this.has(t)?this.remove(t):this.add(t),this)},n.prototype.array=function(){var t=this.el.getAttribute("class")||"",e=t.replace(/^\s+|\s+$/g,""),r=e.split(s);return""===r[0]&&r.shift(),r},n.prototype.has=n.prototype.contains=function(t){return this.list?this.list.contains(t):!!~i(this.array(),t)}},function(t,e,r){var n=window.addEventListener?"addEventListener":"attachEvent",i=window.removeEventListener?"removeEventListener":"detachEvent",s="addEventListener"!==n?"on":"",a=r(5);e.bind=function(t,e,r,i){t=a(t);for(var o=0;o0?setTimeout(function(){e(r,n,i)},1):(t.update(),n(i))};return e}},function(t,e){t.exports=function(t){return t.handlers.filterStart=t.handlers.filterStart||[],t.handlers.filterComplete=t.handlers.filterComplete||[],function(e){if(t.trigger("filterStart"),t.i=1,t.reset.filter(),void 0===e)t.filtered=!1;else{t.filtered=!0;for(var r=t.items,n=0,i=r.length;nv.page,a=new m(t[i],void 0,n),v.items.push(a),r.push(a)}return v.update(),r}},this.show=function(t,e){return this.i=t,this.page=e,v.update(),v},this.remove=function(t,e,r){for(var n=0,i=0,s=v.items.length;i-1&&r.splice(n,1),v},this.trigger=function(t){for(var e=v.handlers[t].length;e--;)v.handlers[t][e](v);return v},this.reset={filter:function(){for(var t=v.items,e=t.length;e--;)t[e].filtered=!1;return v},search:function(){for(var t=v.items,e=t.length;e--;)t[e].found=!1;return v}},this.update=function(){var t=v.items,e=t.length;v.visibleItems=[],v.matchingItems=[],v.templater.clear();for(var r=0;r=v.i&&v.visibleItems.lengthe},innerWindow:function(t,e,r){return t>=e-r&&t<=e+r},dotted:function(t,e,r,n,i,s,a){return this.dottedLeft(t,e,r,n,i,s)||this.dottedRight(t,e,r,n,i,s,a)},dottedLeft:function(t,e,r,n,i,s){return e==r+1&&!this.innerWindow(e,i,s)&&!this.right(e,n)},dottedRight:function(t,e,r,n,i,s,a){return!t.items[a-1].values().dotted&&(e==n&&!this.innerWindow(e,i,s)&&!this.right(e,n))}},a=function(e,r,n){i.bind(e,"click",function(){t.show((r-1)*n+1,n)})};return function(r){var n=new s(t.listContainer.id,{listClass:r.paginationClass||"pagination",item:"
    • ",valueNames:["page","dotted"],searchClass:"pagination-search-that-is-not-supposed-to-exist",sortClass:"pagination-sort-that-is-not-supposed-to-exist"});t.on("updated",function(){e(n,r)}),e(n,r)}}},function(t,e,r){t.exports=function(t){var e=r(2)(t),n=function(t){for(var e=t.childNodes,r=[],n=0,i=e.length;n0?setTimeout(function(){s(e,r)},1):(t.update(),t.trigger("parseComplete"))};return t.handlers.parseComplete=t.handlers.parseComplete||[],function(){var e=n(t.list),r=t.valueNames;t.indexAsync?s(e,r):i(e,r)}}},function(t,e){t.exports=function(t){var e,r,n,i,s={resetList:function(){t.i=1,t.templater.clear(),i=void 0},setOptions:function(t){2==t.length&&t[1]instanceof Array?r=t[1]:2==t.length&&"function"==typeof t[1]?(r=void 0,i=t[1]):3==t.length?(r=t[1],i=t[2]):r=void 0},setColumns:function(){0!==t.items.length&&void 0===r&&(r=void 0===t.searchColumns?s.toArray(t.items[0].values()):t.searchColumns)},setSearchString:function(e){e=t.utils.toString(e).toLowerCase(),e=e.replace(/[-[\]{}()*+?.,\\^$|#]/g,"\\$&"),n=e},toArray:function(t){var e=[];for(var r in t)e.push(r);return e}},a={list:function(){for(var e=0,r=t.items.length;e-1))},reset:function(){t.reset.search(),t.searched=!1}},o=function(e){return t.trigger("searchStart"),s.resetList(),s.setSearchString(e),s.setOptions(arguments),s.setColumns(),""===n?a.reset():(t.searched=!0,i?i(n,r):a.list()),t.update(),t.trigger("searchComplete"),t.visibleItems};return t.handlers.searchStart=t.handlers.searchStart||[],t.handlers.searchComplete=t.handlers.searchComplete||[],t.utils.events.bind(t.utils.getByClass(t.listContainer,t.searchClass),"keyup",function(e){var r=e.target||e.srcElement,n=""===r.value&&!t.searched;n||o(r.value)}),t.utils.events.bind(t.utils.getByClass(t.listContainer,t.searchClass),"input",function(t){var e=t.target||t.srcElement;""===e.value&&o("")}),o}},function(t,e){t.exports=function(t){var e={els:void 0,clear:function(){for(var r=0,n=e.els.length;r]/g.exec(e)){var s=document.createElement("tbody");return s.innerHTML=e,s.firstChild}if(e.indexOf("<")!==-1){var a=document.createElement("div");return a.innerHTML=e,a.firstChild}var o=document.getElementById(t.item);if(o)return o}},this.get=function(e,n){r.create(e);for(var i={},s=0,a=n.length;s=1;)t.list.removeChild(t.list.firstChild)},n()};t.exports=function(t){return new r(t)}},function(t,e){t.exports=function(t,e){var r=t.getAttribute&&t.getAttribute(e)||null;if(!r)for(var n=t.attributes,i=n.length,s=0;s=48&&t<=57}function i(t,e){for(var r=(t+="").length,i=(e+="").length,s=0,l=0;s32)return!1;var o=i,l=function(){var t,r={};for(t=0;t=p;b--){var w=l[t.charAt(b-1)];if(0===g?y[b]=(y[b+1]<<1|1)&w:y[b]=(y[b+1]<<1|1)&w|((v[b+1]|v[b])<<1|1)|v[b+1],y[b]&f){var x=n(g,b-1);if(x<=u){if(u=x,c=b-1,!(c>o))break;p=Math.max(1,2*o-c)}}}if(n(g+1,o)>u)break;v=y}return!(c<0)}}]); -function initNotebookSearch() { - var dmenu = document.getElementById('notebook-drop-menu'); - var tmenu = document.getElementById('tags-drop-menu'); - var dbtn = document.getElementById('notebook-drop-btn'); - var tbtn = document.getElementById('tags-drop-btn'); - dbtn.addEventListener('click', menuOpener, false); - tbtn.addEventListener('click', menuOpener, false); - dbtn.menu = document.getElementById('notebook-drop-menu'); - tbtn.menu = tmenu; - var input = document.getElementById('notebook-input'); - var ul = document.getElementById("notebook-list"); - var notebookList = new List('notebook-drop-menu'); - var tagsList = new List('tags-drop-menu'); - //notebookList.on('updated', myFunct, false); - //tagsList.on('updated', myFunct, false); - function myFunct(e) { - console.log('done'); - } -// Pass single element: -//var el = document.getElementById('choices-single-default'); -//var resetSimple = new Choices(el); +function choicePageChanger(value, keyCode){ + document.location.href = value['detail']['choice']['value']; } -function menuOpener(e) { - e.preventDefault(); - e.target.menu.classList.toggle('active'); - console.log(e.target.menu.classList); - hideOnClickOutsided(e.target.menu, e.target); + +function initChoicesMenu(obj) { + var w = document.createElement('div'); + w.classList.add(...obj.wrapper_classes); + var s = document.createElement('select'); + s.classList.add('form-control'); + s.setAttribute("name", obj.choices_name); + s.setAttribute("id", obj.choices_id); + w.appendChild(s); + var c = document.getElementById('choices-container'); + c.appendChild(w); + var choices_holder = s + var choices = new Choices(choices_holder, { + itemSelectText: '', + choices: obj.data, + }); + obj.replace_el.classList.add('hide'); + choices_holder.addEventListener('choice', choicePageChanger); } -if (typeof(document.getElementById('notebook-list')) != 'undefined' && document.getElementById('notebook-list') != null) { - initNotebookSearch(); + +if (typeof(document.getElementById('choices-container')) != 'undefined' && document.getElementById('choices-container') != null) { + initChoicesMenu({ + data: window.nbdata, + wrapper_classes: ['choices-wrapper', 'choices-wrapper-notebooks'], + replace_el: document.getElementById('notebook-list'), + choices_name: "choices-notebooks", + choices_id: "choices-notebooks" + }); + initChoicesMenu({ + data: window.tagdata, + wrapper_classes: ['choices-wrapper', 'choices-wrapper-tags'], + replace_el: document.getElementById('tags-list'), + choices_name: "choices-tags", + choices_id: "choices-tags" + }); } diff --git a/scripts/src/notebook-edit.js b/scripts/src/notebook-edit.js index 802ec79..7d0aa8f 100644 --- a/scripts/src/notebook-edit.js +++ b/scripts/src/notebook-edit.js @@ -19,13 +19,12 @@ function initNotebookEditor() { btn.classList.remove('hide'); btn.addEventListener('click', function(e){ e.preventDefault(); - edit_notebook(this, title, form, picker, fname, window.url); + edit_notebook(title, form, picker, fname, window.url); }, false); } -function initNotebookCreator(btn, title, url) { - var notebook_form = document.querySelector('#nb-create-form'); - var notebook_form_inputs = notebook_form.getElementsByTagName('input'); - var picker = notebook_form.getElementsByTagName('fieldset')['color-picker']; +function activatePicker(form, picker) { + var inputs = form.getElementsByTagName('input'); + var picker = picker; picker.popup = new Picker({ parent: picker, color: 'blue', @@ -34,87 +33,82 @@ function initNotebookCreator(btn, title, url) { editorFormat: 'hex', onDone: function(color) { this.settings['parent'].style.backgroundColor = color.rgbString; - notebook_form_inputs['color_rgb'].value = color.rgbString; + inputs['color_rgb'].value = color.rgbString; }, }); +} +function initMultiNotebookEditor(notebooks){ + // Takes an array of elements containing forms var forms = []; var btns = []; - var notebooks = document.getElementsByClassName('nb-list-item'); window.bookediting = false; for (var i = 0; i < notebooks.length; i++) { - var fname = notebooks[i].getElementsByTagName('input')['id_form-'+i+'-name']; + var nb = notebooks[i]; + var form = forms[i]; + var btn = btns[i]; + // hide the form elements + var fname = nb.getElementsByTagName('input')['id_name-'+i]; fname.parentNode.classList.add('hide'); - notebooks[i].getElementsByTagName('input')['i-'+i].classList.add('hide'); - forms[i] = notebooks[i].getElementsByTagName('form')[0]; - forms[i].getElementsByTagName('input')['color_rgb'].disabled = true; - forms[i].addEventListener('input', function () { + nb.getElementsByTagName('input')['i-'+i].classList.add('hide'); + form = nb.getElementsByTagName('form')[0]; + form.getElementsByTagName('input')['color_rgb'].disabled = true; + form.addEventListener('input', function () { window.formchange = true; console.log('adding change'); }); - btns[i] = notebooks[i].getElementsByTagName('a')['edit-toggle-btn-'+i]; - btns[i].addEventListener('click', myFunc, false); - btns[i].t = notebooks[i].getElementsByTagName('h2')[0]; - btns[i].picker = notebooks[i].getElementsByTagName('fieldset')['color-picker-'+i]; - btns[i].fname = fname; - btns[i].form = forms[i]; - btns[i].url = forms[i]['action']; - function myFunc(e) { + btn = nb.getElementsByTagName('a')['edit-toggle-btn-'+i]; + btn.addEventListener('click', clickHandler, false); + btn.t = nb.getElementsByTagName('h2')[0].getElementsByTagName('a')[0]; + btn.picker = nb.getElementsByTagName('fieldset')['color-picker-'+i]; + btn.fname = fname; + btn.form = form; + btn.url = form['action']; + btn.trash = nb.getElementsByTagName('button')['trash'] + btn.trash.classList.add('hide') + btn.isHidden = true; + function clickHandler(e) { e.preventDefault(); - edit_notebook(e.target, e.target.t, e.target.form, e.target.picker, e.target.fname, e.target.url); + e.target.textContent = e.target.isHidden ? 'Save' : 'Edit'; + e.target.isHidden = !e.target.isHidden; + e.target.classList.toggle('save'); + e.target.trash.classList.toggle('hide'); + edit_notebook(e.target.t, e.target.form, e.target.picker, e.target.fname, e.target.url, 'POST'); }; + var more = form.getElementsByTagName('button')['more-button-'+i] + if (more !== undefined) { + more.slider = form.getElementsByTagName('ul')['n-list-'+i] + var isHidden = true; + more.addEventListener('click', moreclickHandler, false); + function moreclickHandler(e) { + console.log(e.target.slider); + e.preventDefault(); + e.target.slider.classList.toggle('slideup'); + e.target.slider.classList.toggle('slidedown'); + e.target.textContent = isHidden ? 'Less' : 'More'; + isHidden = !isHidden; + } + } } } -function edit_notebook(btn, title, form, picker, fname, url){ +function edit_notebook(title, form, picker, fname, url, method){ + var method = method || 'PUT'; var form_inputs = form.getElementsByTagName('input'); - console.log(picker); if (window.bookediting === false) { - picker.popup= new Picker({ - parent: picker, - color: 'blue', - alpha: false, - //editor: false, - editorFormat: 'hex', - onDone: function(color) { - this.settings['parent'].style.backgroundColor = color.rgbString; - form_inputs['color_rgb'].value = color.rgbString; - }, - }); + activatePicker(form, picker); title.setAttribute('contenteditable', true); title.classList.add('highlight'); form_inputs['color_rgb'].disabled = false; - btn.innerHTML = 'Save'; - btn.classList.add('save'); window.bookediting = true; window.titlecontents = title.innerHTML; window.colorcontent = form_inputs['color_rgb'].value; } else { if (window.titlecontents !== title.innerHTML || window.colorcontent !== form_inputs['color_rgb'].value || window.formchange) { - console.log('changed'); - console.log(form_inputs); fname.value = title.innerHTML; - //const data = formToJSON(form.elements); - //console.log(JSON.stringify(data, null, ' ')); - var request = new XMLHttpRequest(); - request.open('PATCH', url); - var csrftoken = getCookie('csrftoken'); - request.setRequestHeader('X-CSRFToken', csrftoken); - request.onload = function() { - if (request.status >= 200 && request.status < 400) { - console.log(request); - } else { - console.log(request); - console.log('server error'); - } - }; - request.onerror = function() { - console.log('error on request'); - }; - request.send(new FormData(form)); + var data = new FormData(form) + getJSONFromDjango(method, url, notebookCallback.bind(null, title), data); } title.setAttribute('contenteditable', false); title.classList.remove('highlight'); - btn.innerHTML = 'Edit'; - btn.classList.remove('save'); form_inputs['color_rgb'].disabled = true; document.body.focus(); window.bookediting = false; @@ -122,9 +116,31 @@ function edit_notebook(btn, title, form, picker, fname, url){ } return false; } +function notebookCallback(title, request) { + if (request.status >= 200 && request.status < 400) { + if (title.href) { + var data = JSON.parse(request.responseText); + title.href = '/nb/'+ data['notebook']['permalink']; + console.log(title.href); + } + if (window.overlay !== undefined) { + window.overlay.destroy(); + } + } else { + console.log(request); + console.log('server error'); + } +} if (typeof(document.getElementById('nb-edit-form')) != 'undefined' && document.getElementById('nb-edit-form') != null) { initNotebookEditor(); } -if (typeof(document.getElementById('nb-create-form')) != 'undefined' && document.getElementById('nb-create-form') != null && document.getElementById('note-edit-form') === null) { - initNotebookCreator(); +if (typeof(document.getElementById('nb-create-form')) != 'undefined' && document.getElementById('nb-create-form') != null && document.getElementById('nb-list-home') !== null) { + var newnotebook = document.getElementById('notebook-wrapper'); + newnotebook.classList.add('hide'); + var notebookAddForm = document.getElementById('nb-create-form'); + var btn = document.getElementById('add_id_notebook'); + addNotebookModal(notebookAddForm, btn); + var create_form = document.getElementById('nb-create-form'); + activatePicker(create_form, create_form.getElementsByTagName('fieldset')['color-picker']); + initMultiNotebookEditor(document.getElementsByClassName('nb-list-item')); } diff --git a/scripts/src/util.js b/scripts/src/util.js index 144deae..6d3769f 100644 --- a/scripts/src/util.js +++ b/scripts/src/util.js @@ -6,21 +6,26 @@ function buildComponent(tag, id, html){ return el; }; -function getJSON(method, url, callback) { +function getJSONFromDjango(method, url, callback, data) { + var data = data || null var request = new XMLHttpRequest(); - request.addEventListener('load', callback); + var csrftoken = getCookie('csrftoken'); + request.addEventListener('load', callback.bind(null, request)); request.open(method, url, true); - request.onload = function() { - if (request.status >= 200 && request.status < 400) { - //console.log(request.responseText); - } else { - console.log('server error'); - } - }; + request.setRequestHeader('X-CSRFToken', csrftoken); request.onerror = function() { console.log('error on request'); }; - request.send(); + //create a modal to let the user know we're saving + var saveModal = document.createElement('div'); + var loader = document.createElement('div'); + loader.classList.add('loader'); + saveModal.appendChild(loader); + saveModal.close = false; //no close button + saveModal.modalHed = "Saving changes"; //modal text content + saveModal.wrapperClass = 'loading'; //add a wrapper class to the modal for styling + var modal = modalBox(saveModal); + request.send(data); } // hijack a form and submit with ajax @@ -132,13 +137,16 @@ function modalBox(content, btn){ headline.className = 'hed'; content.classList.remove('hide'); var content = buildComponent('div', 'content-wrapper', options.content); + var hed_wrapper = buildComponent('div', 'hed-wrapper'); + hed_wrapper.appendChild(headline); + console.log("close var is: ",options.content.close); var actions = buildComponent('div', 'close-btn'); var cancel = buildComponent('button', 'modalCancel', options.cancelText); cancel.classList.add('btn', 'btn-hollow', 'btn-light'); actions.appendChild(cancel); - var hed_wrapper = buildComponent('div', 'hed-wrapper'); - hed_wrapper.appendChild(headline); - hed_wrapper.appendChild(actions); //actions includes action buttons + if (options.content.close !== false) { + hed_wrapper.appendChild(actions); //actions includes action buttons + } innermodal.appendChild(hed_wrapper); innermodal.appendChild(content); overlay.appendChild(outermodal); @@ -182,3 +190,18 @@ function isEmpty(quill) { const re = /^

      (
      |||\s+|\r)<\/p>$/gm; return re.test(commentText); }; + +function addNotebookModal(notebookAddForm, btn) { + initColorPicker(notebookAddForm); + ajaxHijack(notebookAddForm, notebookCreate) + btn.content = notebookAddForm.parentNode; + addHandler(btn); + function addHandler(el){ + el.addEventListener('click', function(e){ + e.preventDefault(); + var modalContent = e.target.content; + modalContent.wrapperClass = 'overlay'; //add a wrapper class to the modal for styling + var modal = modalBox(modalContent, el); + }); + } +} -- cgit v1.2.3-70-g09d2