Browse Source

Add compact inline

Denis K 8 years ago
parent
commit
c1ae115f69

+ 88 - 0
jet/static/jet/css/_changeform.scss

@@ -505,6 +505,11 @@ body.popup .submit-row {
   border-radius: 4px;
   border: 0;
 
+  &.compact {
+    position: relative;
+    min-height: 400px;
+  }
+
   thead th {
     padding: 8px 10px;
   }
@@ -622,6 +627,10 @@ body.popup .submit-row {
   }
 }
 
+.inline-group.compact .inline-related h3 {
+  background: transparent;
+}
+
 .inline-related.tabular fieldset.module {
   padding: 0;
 
@@ -630,6 +639,76 @@ body.popup .submit-row {
   }
 }
 
+.inline-navigation {
+  position: absolute;
+  top: 0;
+  left: 0;
+  bottom: 0;
+  width: 200px;
+  background: $content-contrast-background-color;
+
+  &-top {
+    position: absolute;
+    top: 0;
+    right: 0;
+    left: 0;
+    height: 40px;
+    background: -webkit-linear-gradient(to bottom, $content-background-color 0%, $content-contrast-background-color 100%);
+    background: linear-gradient(to bottom, $content-background-color 0%, $content-contrast-background-color 100%);
+  }
+
+  &-bottom {
+    position: absolute;
+    right: 0;
+    bottom: 0;
+    left: 0;
+    height: 40px;
+    background: -webkit-linear-gradient(to top, $content-background-color 0%, $content-contrast-background-color 100%);
+    background: linear-gradient(to top, $content-background-color 0%, $content-contrast-background-color 100%);
+  }
+
+  &-content {
+    position: absolute;
+    top: 0;
+    right: 0;
+    bottom: 0;
+    left: 0;
+    overflow-y: auto;
+  }
+
+  &-item {
+    &, &:visited, &:hover {
+      display: block;
+      white-space: nowrap;
+      text-overflow: ellipsis;
+      overflow: hidden;
+      padding: 8px 10px 8px 20px;
+      color: $dim-text-color;
+      transition: background-color $transitions-duration, color $transitions-duration;
+    }
+
+    &:hover {
+      background: $button-hover-background-color;
+      color: $button-hover-text-color;
+    }
+
+    &.empty {
+      display: none;
+    }
+
+    &.selected {
+      background: transparent;
+      color: $text-color;
+      font-weight: bold;
+      cursor: default;
+    }
+
+    &.delete {
+      text-decoration: line-through;
+    }
+  }
+}
+
 .inline-group {
   .tabular {
     overflow-x: auto;
@@ -676,6 +755,15 @@ body.popup .submit-row {
     }
   }
 
+  .compact {
+    display: none;
+    margin-left: 200px;
+
+    &.selected {
+      display: block;
+    }
+  }
+
   ul.tools {
     padding: 0;
     margin: 0;

+ 163 - 0
jet/static/jet/js/src/features/compact-inline.js

@@ -0,0 +1,163 @@
+var $ = require('jquery');
+
+var CompactInline = function($inline) {
+    this.$inline = $inline;
+    this.prefix = $inline.data('inline-prefix');
+    this.verboseName = $inline.data('inline-verbose-name');
+    this.deleteText = $inline.data('inline-delete-text');
+};
+
+CompactInline.prototype = {
+    updateLabels: function($inline) {
+        var self = this;
+        var $navigationItems = $inline.find('.inline-navigation-item');
+
+        $inline.find('.inline-related').each(function(i) {
+            var $inlineItem = $(this);
+            var $label = $inlineItem.find('.inline_label');
+            var label = $label.html().replace(/(#\d+)/g, "#" + (i + 1));
+            var $navigationItem = $navigationItems.eq(i);
+            var navigationLabel = $inlineItem.hasClass('has_original') ? label : self.verboseName + ' ' + label;
+
+            $label.html(label);
+            $navigationItem.html(navigationLabel);
+        });
+    },
+    updateFormIndex: function($form, index) {
+        var id_regex = new RegExp('(' + this.prefix + '-(\\d+|__prefix__))');
+        var replacement = this.prefix + "-" + index;
+
+        $form.find('*').each(function() {
+            var $el = $(this);
+
+            $.each(['for', 'id', 'name'], function() {
+                var attr = this;
+
+                if ($el.attr(attr)) {
+                    $el.attr(attr, $el.attr(attr).replace(id_regex, replacement));
+                }
+            });
+        });
+
+        if (!$form.hasClass('empty-form')) {
+            $form.attr('id', this.prefix + '-' + index);
+        }
+    },
+    updateFormsIndexes: function($inline) {
+        var self = this;
+        var $navigationItems = $inline.find('.inline-navigation-item');
+
+        $inline.find('.inline-related').each(function(i) {
+            var $inlineItem = $(this);
+
+            self.updateFormIndex($inlineItem, i);
+            $navigationItems.eq(i).attr('data-inline-related-id', $inlineItem.attr('id'));
+        });
+    },
+    updateTotalForms: function($inline) {
+        var $totalFormsInput = $inline.find('[name="' + this.prefix + '-TOTAL_FORMS"]');
+        var totalForms = parseInt($inline.find('.inline-related').length);
+
+        $totalFormsInput.val(totalForms);
+    },
+    addNavigationItem: function($inline, $inlineItem) {
+        var $empty = $inline.find('.inline-navigation-item.empty');
+
+        return $empty
+            .clone()
+            .removeClass('empty')
+            .attr('data-inline-related-id', $inlineItem.attr('id'))
+            .insertBefore($empty);
+    },
+    openNavigationItem: function($inline, $item) {
+        $inline
+            .find('.inline-related')
+            .removeClass('selected')
+            .filter('#' + $item.attr('data-inline-related-id'))
+            .addClass('selected');
+
+        $inline.find('.inline-navigation-item').removeClass('selected');
+        $item.addClass('selected');
+    },
+    removeItem: function($inline, $item) {
+        $item.remove();
+        $inline.find('.inline-navigation-item[data-inline-related-id="' + $item.attr('id') + '"]').remove();
+    },
+    openFirstNavigationItem: function($inline) {
+        this.openNavigationItem($inline, $inline.find('.inline-navigation-item').first());
+    },
+    initAdding: function($inline) {
+        var self = this;
+
+        $inline.find('.add-row a').on('click', function (e) {
+            e.preventDefault();
+
+            var $empty = $inline.find('.inline-related.empty-form');
+            var cloneIndex = parseInt($inline.find('.inline-related').length) - 1;
+            var $clone = $empty
+                .clone(true)
+                .removeClass('empty-form')
+                .insertBefore($empty);
+
+            self.updateTotalForms($inline);
+            self.updateFormIndex($clone, cloneIndex);
+            self.updateFormIndex($empty, cloneIndex + 1);
+
+            var navigationItem = self.addNavigationItem($inline, $clone);
+
+            self.updateLabels($inline);
+            self.openNavigationItem($inline, navigationItem);
+
+            $clone.children(':first').append('<span><a class="inline-deletelink" href="#">' + self.deleteText + "</a></span>");
+        });
+    },
+    initDeletion: function($inline) {
+        var self = this;
+
+        $inline.on('click', '.inline-deletelink', function(e) {
+            e.preventDefault();
+
+            var $inlineItem = $(this).closest('.inline-related');
+
+            self.removeItem($inline, $inlineItem);
+            self.updateFormsIndexes($inline);
+            self.updateLabels($inline);
+            self.updateTotalForms($inline);
+            self.openFirstNavigationItem($inline);
+        });
+
+        $inline.find('.inline-related').each(function() {
+            var $inlineItem = $(this);
+
+            $inlineItem.find('.delete input').on('change', function() {
+                $inline
+                    .find('.inline-navigation-item[data-inline-related-id="' + $inlineItem.attr('id') + '"]')
+                    .toggleClass('delete', $(this).is(':checked'));
+            });
+        });
+    },
+    initNavigation: function($inline) {
+        var self = this;
+
+        $inline.on('click', '.inline-navigation-item', function(e) {
+            e.preventDefault();
+
+            self.openNavigationItem($inline, $(this));
+        });
+
+        self.openFirstNavigationItem($inline);
+    },
+    run: function() {
+        var $inline = this.$inline;
+
+        try {
+            this.initAdding($inline);
+            this.initDeletion($inline);
+            this.initNavigation($inline);
+        } catch (e) {
+            console.error(e, e.stack);
+        }
+    }
+};
+
+module.exports = CompactInline;

+ 5 - 0
jet/static/jet/js/src/features/inlines.js

@@ -1,4 +1,5 @@
 var $ = require('jquery');
+var CompactInline = require('./compact-inline');
 
 var Inline = function($inline) {
     this.$inline = $inline;
@@ -14,6 +15,10 @@ Inline.prototype = {
         var $inline = this.$inline;
 
         try {
+            if ($inline.hasClass('compact')) {
+                new CompactInline($inline).run();
+            }
+
             this.initSelectsOnAddRow($inline);
         } catch (e) {
             console.error(e, e.stack);

+ 56 - 0
jet/templates/admin/edit_inline/compact.html

@@ -0,0 +1,56 @@
+{% load i18n admin_urls admin_static %}
+
+<div class="inline-group compact" id="{{ inline_admin_formset.formset.prefix }}-group" data-inline-prefix="{{ inline_admin_formset.formset.prefix }}" data-inline-verbose-name="{{ inline_admin_formset.opts.verbose_name|capfirst }}" data-inline-delete-text="{% trans "Remove" %}">
+    <h2>{{ inline_admin_formset.opts.verbose_name_plural|capfirst }}</h2>
+    {{ inline_admin_formset.formset.management_form }}
+    {{ inline_admin_formset.formset.non_form_errors }}
+
+    <div class="inline-navigation">
+        <div class="inline-navigation-top"></div>
+        <div class="inline-navigation-bottom"></div>
+        <div class="inline-navigation-content">
+            <div class="add-row">
+                <a href="#">{% blocktrans with verbose_name=inline_admin_formset.opts.verbose_name|capfirst %}Add another {{ verbose_name }}{% endblocktrans %}</a>
+            </div>
+
+            <div class="inline-navigation-items">
+                {% for inline_admin_form in inline_admin_formset %}
+                    <a href="#" class="inline-navigation-item{% if forloop.last %} empty{% endif %}" data-inline-related-id="{{ inline_admin_formset.formset.prefix }}-{% if not forloop.last %}{{ forloop.counter0 }}{% else %}empty{% endif %}">
+                        {% if inline_admin_form.original %}
+                            {{ inline_admin_form.original }}
+                        {% else %}
+                            {{ inline_admin_formset.opts.verbose_name|capfirst }} #{{ forloop.counter }}
+                        {% endif %}
+                    </a>
+                {% endfor %}
+            </div>
+        </div>
+    </div>
+
+    {% for inline_admin_form in inline_admin_formset %}
+        <div class="inline-related compact{% if inline_admin_form.original or inline_admin_form.show_url %} has_original{% endif %}{% if forloop.last %} empty-form last-related{% endif %}" id="{{ inline_admin_formset.formset.prefix }}-{% if not forloop.last %}{{ forloop.counter0 }}{% else %}empty{% endif %}">
+            <h3>
+                <b>{{ inline_admin_formset.opts.verbose_name|capfirst }}:</b>&nbsp;
+                <span class="inline_label">
+                    {% if inline_admin_form.original %}
+                        {{ inline_admin_form.original }}
+                        {% if inline_admin_form.model_admin.show_change_link and inline_admin_form.model_admin.has_registered_model %}
+                            <a href="{% url inline_admin_form.model_admin.opts|admin_urlname:'change' inline_admin_form.original.pk|admin_urlquote %}" class="inlinechangelink">{% trans "Change" %}</a>
+                        {% endif %}
+                    {% else %}
+                        #{{ forloop.counter }}
+                    {% endif %}
+                </span>
+                {% if inline_admin_form.show_url %}<a href="{{ inline_admin_form.absolute_url }}">{% trans "View on site" %}</a>{% endif %}
+                {% if inline_admin_formset.formset.can_delete and inline_admin_form.original %}<span class="delete">{{ inline_admin_form.deletion_field.field }} {{ inline_admin_form.deletion_field.label_tag }}</span>{% endif %}
+            </h3>
+            {% if inline_admin_form.form.non_field_errors %}{{ inline_admin_form.form.non_field_errors }}{% endif %}
+            {% for fieldset in inline_admin_form %}
+                {% include "admin/includes/fieldset.html" %}
+            {% endfor %}
+            {% if inline_admin_form.needs_explicit_pk_field %}{{ inline_admin_form.pk_field.field }}{% endif %}
+            {{ inline_admin_form.fk_field.field }}
+        </div>
+    {% endfor %}
+</div>
+