]> _ Git - fluidbook-html5.git/commitdiff
wip #4285 @2.5
authorVincent Vanwaelscappel <vincent@cubedesigners.com>
Sun, 21 Feb 2021 19:44:27 +0000 (20:44 +0100)
committerVincent Vanwaelscappel <vincent@cubedesigners.com>
Sun, 21 Feb 2021 19:44:27 +0000 (20:44 +0100)
js/libs/fluidbook/cart/fluidbook.cart.grandvision.js
js/libs/jquery/jquery.multi-select.js [new file with mode: 0644]
style/cart/grandvision.less

index a420325315e53912f92d317b4e2f4a810f6bca0a..e4530941688e5182d6205b1699ed31f21a40643f 100644 (file)
@@ -3,6 +3,7 @@ function FluidbookCartGrandVision(cart) {
     this.cart = cart;
     this.fluidbook = this.cart.fluidbook;
     this.data = this.fluidbook.settings.basketReferences;
+    this.filters = {};
     this.init();
 }
 
@@ -37,7 +38,43 @@ FluidbookCartGrandVision.prototype = {
         return this.openSelection(cb);
     },
 
+    updateFilters: function () {
+        var hasVisible = false;
+        $('#grandvision-selection .item').each(function () {
+            var hide = false;
+            var a = $(this);
+            $("#grandvision-selection .filters select").each(function () {
+                var vals = $(this).val();
+                if (vals.length === 0) {
+                    return;
+                }
+                var name = $(this).attr('name');
+                var attrvalues = $(a).attr('data-' + name).split(',');
+                var inter = vals.filter(function (n) {
+                    return attrvalues.indexOf(n) !== -1;
+                });
+                if (inter.length === 0) {
+                    hide = true;
+                }
+            });
+            if (hide) {
+                $(a).hide();
+            } else {
+                hasVisible = true;
+                $(a).show();
+            }
+
+        });
+
+        if (hasVisible) {
+            $("#emptyres").hide();
+        } else {
+            $("#emptyres").show();
+        }
+    },
+
     openSelection: function (cb) {
+        var $this = this;
 
         var res = '<div id="grandvision-cart">';
         res += this.fluidbook.menu.getCaption('', true);
@@ -48,17 +85,41 @@ FluidbookCartGrandVision.prototype = {
         res += '</div>';
         this.fluidbook.menu.viewWrap(res, 'cart-grandvision-selection', 'data-max-width="1200" data-min-width="1200"');
 
+
+        $("#grandvision-cart select").each(function () {
+            var allValues = $this.filters[$(this).attr('name')].values;
+            $this.filters[$(this).attr('name')].multiselect = $(this).multiSelect({
+                'noneText': $(this).data('label'),
+                presets: [
+                    {
+                        name: 'All',
+                        options: allValues,
+                    },
+                ]
+            }).on('change', function () {
+                $this.updateFilters();
+            });
+        });
+
+
         if (cb !== undefined) {
             cb();
         }
     },
 
+
+
     cartSelection: function () {
         var $this = this;
-        var res = '<div id="grandvision-selection">';
-        res += '<h2>My Selection</h2>';
-        res += '<div class="filters"></div>';
-        res += '<div class="items">';
+
+        this.filters = {
+            brand: {col: 'BRAND', label: 'Brand', values: []},
+            category: {col: 'Category', label: 'Category', values: []},
+            gender: {col: 'Gender', label: 'Gender', values: []},
+            material: {col: 'Material', label: 'Material', values: []}
+        };
+
+        var selection = '';
         $.each(this.items, function (k, item) {
             var data = $this.getProductData(item);
             var infos = ['Brand: ' + data.BRAND, 'Color: ' + data['Color Code']];
@@ -67,13 +128,38 @@ FluidbookCartGrandVision.prototype = {
             }
             infos.push('EAN: ' + item);
 
-            res += '<div class="item" data-ref="' + item + '">';
-            res += '<div class="img"><img src="data/commerce/' + item + '-front.jpg" /></div>';
-            res += '<h3>' + data['Model Code'] + '</h3>';
-            res += '<div class="infos">' + infos.join(' | ') + '</div>';
-            res += '</div>';
+            $.each($this.filters, function (fname, filter) {
+                if ($this.filters[fname].values.indexOf(data[filter.col]) === -1) {
+                    $this.filters[fname].values.push(data[filter.col]);
+                }
+            });
+
+            selection += '<div class="item" data-ref="' + item + '" data-brand="' + data.BRAND + '" data-category="' + data.Category + '" data-gender="' + data.Gender + '" data-material="' + data.Material + '">';
+            selection += '<div class="img">';
+            if (data.front) {
+                selection += '<img src="data/commerce/' + item + '-front.jpg" />';
+            }
+            selection += '</div>';
+            selection += '<div class="infos"><h3>' + data['Model Code'] + '</h3>';
+            selection += '<p>' + infos.join(' | ') + '</p></div>';
+            selection += '</div>';
         });
+
+        var res = '<div id="grandvision-selection">';
+        res += '<h2>My Selection</h2>';
+        res += '<div class="filters">';
+        $.each(this.filters, function (name, data) {
+            res += '<select multiple data-label="' + data.label + '" name="' + name + '">';
+            $.each(data.values, function (k, v) {
+                res += '<option value="' + v + '">' + v + '</option>';
+            });
+            res += '</select>';
+        });
+        res += '</div>';
+        res += '<div class="items">';
+        res += selection;
         res += '</div>';
+        res += '<div id="emptyres">No selected item match filters</div>';
         res += '</div>';
         return res;
     },
diff --git a/js/libs/jquery/jquery.multi-select.js b/js/libs/jquery/jquery.multi-select.js
new file mode 100644 (file)
index 0000000..8143f30
--- /dev/null
@@ -0,0 +1,442 @@
+// jquery.multi-select.js
+// by mySociety
+// https://github.com/mysociety/jquery-multi-select
+
+;(function ($) {
+
+    "use strict";
+
+    var pluginName = "multiSelect",
+        defaults = {
+            'containerHTML': '<div class="multi-select-container">',
+            'menuHTML': '<div class="multi-select-menu">',
+            'buttonHTML': '<span class="multi-select-button">',
+            'menuItemsHTML': '<div class="multi-select-menuitems">',
+            'menuItemHTML': '<label class="multi-select-menuitem">',
+            'presetsHTML': '<div class="multi-select-presets">',
+            'modalHTML': undefined,
+            'menuItemTitleClass': 'multi-select-menuitem--titled',
+            'activeClass': 'multi-select-container--open',
+            'noneText': '-- Select --',
+            'allText': undefined,
+            'presets': undefined,
+            'positionedMenuClass': 'multi-select-container--positioned',
+            'positionMenuWithin': undefined,
+            'viewportBottomGutter': 20,
+            'menuMinHeight': 200
+        };
+
+    /**
+     * @constructor
+     */
+    function MultiSelect(element, options) {
+        this.element = element;
+        this.$element = $(element);
+        this.settings = $.extend({}, defaults, options);
+        this._defaults = defaults;
+        this._name = pluginName;
+        this.init();
+    }
+
+    function arraysAreEqual(array1, array2) {
+        if (array1.length != array2.length) {
+            return false;
+        }
+
+        array1.sort();
+        array2.sort();
+
+        for (var i = 0; i < array1.length; i++) {
+            if (array1[i] !== array2[i]) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    $.extend(MultiSelect.prototype, {
+
+        init: function () {
+            this.checkSuitableInput();
+            this.findLabels();
+            this.constructContainer();
+            this.constructButton();
+            this.constructMenu();
+            this.constructModal();
+
+            this.setUpBodyClickListener();
+            this.setUpLabelsClickListener();
+
+            this.$element.hide();
+        },
+
+        checkSuitableInput: function (text) {
+            if (this.$element.is('select[multiple]') === false) {
+                throw new Error('$.multiSelect only works on <select multiple> elements');
+            }
+        },
+
+        findLabels: function () {
+            this.$labels = $('label[for="' + this.$element.attr('id') + '"]');
+        },
+
+        constructContainer: function () {
+            this.$container = $(this.settings['containerHTML']);
+            this.$element.data('multi-select-container', this.$container);
+            this.$container.insertAfter(this.$element);
+        },
+
+        constructButton: function () {
+            var _this = this;
+            this.$button = $(this.settings['buttonHTML']);
+            this.$button.attr({
+                'role': 'button',
+                'aria-haspopup': 'true',
+                'tabindex': 0,
+                'aria-label': this.$labels.eq(0).text()
+            })
+                .on('keydown.multiselect', function (e) {
+                    var key = e.which;
+                    var returnKey = 13;
+                    var escapeKey = 27;
+                    var spaceKey = 32;
+                    var downArrow = 40;
+                    if ((key === returnKey) || (key === spaceKey)) {
+                        e.preventDefault();
+                        _this.$button.click();
+                    } else if (key === downArrow) {
+                        e.preventDefault();
+                        _this.menuShow();
+                        var group = _this.$presets || _this.$menuItems;
+                        group.children(":first").focus();
+                    } else if (key === escapeKey) {
+                        _this.menuHide();
+                    }
+                }).on('click.multiselect', function (e) {
+                _this.menuToggle();
+            })
+                .appendTo(this.$container);
+
+            this.$element.on('change.multiselect', function () {
+                _this.updateButtonContents();
+            });
+
+            this.updateButtonContents();
+        },
+
+        updateButtonContents: function () {
+            var _this = this;
+            var options = [];
+            var selected = [];
+
+            this.$element.find('option').each(function () {
+                var text = /** @type string */ ($(this).text());
+                options.push(text);
+                if ($(this).is(':selected')) {
+                    selected.push($.trim(text));
+                }
+            });
+
+            this.$button.empty();
+
+            if (selected.length == 0) {
+                this.$button.removeClass('active').text(this.settings['noneText']);
+            } else if ((selected.length === options.length) && this.settings['allText']) {
+                this.$button.removeClass('active').text(this.settings['allText']);
+            } else {
+                this.$button.addClass('active').text(this.settings['noneText'] + ' : ' + selected.join(', '));
+            }
+        },
+
+        constructMenu: function () {
+            var _this = this;
+
+            this.$menu = $(this.settings['menuHTML']);
+            this.$menu.attr({
+                'role': 'menu'
+            }).on('keyup.multiselect', function (e) {
+                var key = e.which;
+                var escapeKey = 27;
+                if (key === escapeKey) {
+                    _this.menuHide();
+                    _this.$button.focus();
+                }
+            })
+                .appendTo(this.$container);
+
+            this.constructMenuItems();
+
+            if (this.settings['presets']) {
+                this.constructPresets();
+            }
+        },
+
+        constructMenuItems: function () {
+            var _this = this;
+
+            this.$menuItems = $(this.settings['menuItemsHTML']);
+            this.$menu.append(this.$menuItems);
+
+            this.$element.on('change.multiselect', function (e, internal) {
+                // Don't need to update the menu items if this
+                // change event was fired by our tickbox handler.
+                if (internal !== true) {
+                    _this.updateMenuItems();
+                }
+            });
+
+            this.updateMenuItems();
+        },
+
+        updateMenuItems: function () {
+            var _this = this;
+            this.$menuItems.empty();
+
+            this.$element.children('optgroup,option').each(function (index, element) {
+                var $item;
+                if (element.nodeName === 'OPTION') {
+                    $item = _this.constructMenuItem($(element), index);
+                    _this.$menuItems.append($item);
+                } else {
+                    _this.constructMenuItemsGroup($(element), index);
+                }
+            });
+        },
+
+        upDown: function (type, e) {
+            var key = e.which;
+            var upArrow = 38;
+            var downArrow = 40;
+
+            if (key === upArrow) {
+                e.preventDefault();
+                var prev = $(e.currentTarget).prev();
+                if (prev.length) {
+                    prev.focus();
+                } else if (this.$presets && type === 'menuitem') {
+                    this.$presets.children(':last').focus();
+                } else {
+                    this.$button.focus();
+                }
+            } else if (key === downArrow) {
+                e.preventDefault();
+                var next = $(e.currentTarget).next();
+                if (next.length || type === 'menuitem') {
+                    next.focus();
+                } else {
+                    this.$menuItems.children(':first').focus();
+                }
+            }
+        },
+
+        constructPresets: function () {
+            var _this = this;
+            this.$presets = $(this.settings['presetsHTML']);
+            this.$menu.append(this.$presets);
+
+            $.each(this.settings['presets'], function (i, preset) {
+                var unique_id = _this.$element.attr('name') + '_preset_' + i;
+                var $item = $(_this.settings['menuItemHTML'])
+                    .attr({
+                        'for': unique_id,
+                        'role': 'menuitem'
+                    })
+                    .text(' ' + preset.name)
+                    .on('keydown.multiselect', _this.upDown.bind(_this, 'preset'))
+                    .appendTo(_this.$presets);
+
+                var $input = $('<input>')
+                    .attr({
+                        'type': 'checkbox',
+                        'name': _this.$element.attr('name') + '_presets',
+                        'id': unique_id
+                    })
+                    .prependTo($item);
+
+                $input.on('change.multiselect', function () {
+                    if ($(this).prop('checked')) {
+                        _this.$element.val(preset.options);
+                    } else {
+                        _this.$element.val([]);
+                    }
+                    _this.$element.trigger('change');
+                });
+            });
+            _this.$presets.append('<button>Valider</button>');
+            this.$presets.on('click','button',function(){
+                _this.menuHide();
+                return false;
+            });
+
+            this.$element.on('change.multiselect', function () {
+                _this.updatePresets();
+            });
+
+            this.updatePresets();
+        },
+
+        updatePresets: function () {
+            var _this = this;
+
+            $.each(this.settings['presets'], function (i, preset) {
+                var unique_id = _this.$element.attr('name') + '_preset_' + i;
+                var $input = _this.$presets.find('#' + unique_id);
+
+                if (arraysAreEqual(preset.options || [], _this.$element.val() || [])) {
+                    $input.prop('checked', true);
+                } else {
+                    $input.prop('checked', false);
+                }
+            });
+        },
+
+        constructMenuItemsGroup: function ($optgroup, optgroup_index) {
+            var _this = this;
+
+            $optgroup.children('option').each(function (option_index, option) {
+                var $item = _this.constructMenuItem($(option), optgroup_index + '_' + option_index);
+                var cls = _this.settings['menuItemTitleClass'];
+                if (option_index !== 0) {
+                    cls += 'sr';
+                }
+                $item.addClass(cls).attr('data-group-title', $optgroup.attr('label'));
+                _this.$menuItems.append($item);
+            });
+        },
+
+        constructMenuItem: function ($option, option_index) {
+            var unique_id = this.$element.attr('name') + '_' + option_index;
+            var $item = $(this.settings['menuItemHTML'])
+                .attr({
+                    'for': unique_id,
+                    'role': 'menuitem'
+                })
+                .on('keydown.multiselect', this.upDown.bind(this, 'menuitem'))
+                .text(' ' + $option.text());
+
+            var $input = $('<input>')
+                .attr({
+                    'type': 'checkbox',
+                    'id': unique_id,
+                    'value': $option.val()
+                })
+                .prependTo($item);
+
+            if ($option.is(':disabled')) {
+                $input.attr('disabled', 'disabled');
+            }
+            if ($option.is(':selected')) {
+                $input.prop('checked', 'checked');
+            }
+
+            $input.on('change.multiselect', function () {
+                if ($(this).prop('checked')) {
+                    $option.prop('selected', true);
+                } else {
+                    $option.prop('selected', false);
+                }
+
+                // .prop() on its own doesn't generate a change event.
+                // Other plugins might want to do stuff onChange.
+                $option.trigger('change', [true]);
+            });
+
+            return $item;
+        },
+
+        constructModal: function () {
+            var _this = this;
+
+            if (this.settings['modalHTML']) {
+                this.$modal = $(this.settings['modalHTML']);
+                this.$modal.on('click.multiselect', function () {
+                    _this.menuHide();
+                })
+                this.$modal.insertBefore(this.$menu);
+            }
+        },
+
+        setUpBodyClickListener: function () {
+            var _this = this;
+
+            // Hide the $menu when you click outside of it.
+            $('html').on('click.multiselect', function () {
+                _this.menuHide();
+            });
+
+            // Stop click events from inside the $button or $menu from
+            // bubbling up to the body and closing the menu!
+            this.$container.on('click.multiselect', function (e) {
+                e.stopPropagation();
+            });
+        },
+
+        setUpLabelsClickListener: function () {
+            var _this = this;
+            this.$labels.on('click.multiselect', function (e) {
+                e.preventDefault();
+                e.stopPropagation();
+                _this.menuToggle();
+            });
+        },
+
+        menuShow: function () {
+            $('html').trigger('click.multiselect'); // Close any other open menus
+            this.$container.addClass(this.settings['activeClass']);
+
+            if (this.settings['positionMenuWithin'] && this.settings['positionMenuWithin'] instanceof $) {
+                var menuLeftEdge = this.$menu.offset().left + this.$menu.outerWidth();
+                var withinLeftEdge = this.settings['positionMenuWithin'].offset().left +
+                    this.settings['positionMenuWithin'].outerWidth();
+
+                if (menuLeftEdge > withinLeftEdge) {
+                    this.$menu.css('width', (withinLeftEdge - this.$menu.offset().left));
+                    this.$container.addClass(this.settings['positionedMenuClass']);
+                }
+            }
+
+            var menuBottom = this.$menu.offset().top + this.$menu.outerHeight();
+            var viewportBottom = $(window).scrollTop() + $(window).height();
+            if (menuBottom > viewportBottom - this.settings['viewportBottomGutter']) {
+                this.$menu.css({
+                    'maxHeight': Math.max(
+                        viewportBottom - this.settings['viewportBottomGutter'] - this.$menu.offset().top,
+                        this.settings['menuMinHeight']
+                    ),
+                    'overflow': 'scroll'
+                });
+            } else {
+                this.$menu.css({
+                    'maxHeight': '',
+                    'overflow': ''
+                });
+            }
+        },
+
+        menuHide: function () {
+            this.$container.removeClass(this.settings['activeClass']);
+            this.$container.removeClass(this.settings['positionedMenuClass']);
+            this.$menu.css('width', 'auto');
+        },
+
+        menuToggle: function () {
+            if (this.$container.hasClass(this.settings['activeClass'])) {
+                this.menuHide();
+            } else {
+                this.menuShow();
+            }
+        }
+
+    });
+
+    $.fn[pluginName] = function (options) {
+        return this.each(function () {
+            if (!$.data(this, "plugin_" + pluginName)) {
+                $.data(this, "plugin_" + pluginName,
+                    new MultiSelect(this, options));
+            }
+        });
+    };
+
+})(jQuery);
index 4e8f8169c42202648530fbb7e87d9854478c7984..36e2089f7b083a7b37221fb28072c2b998684cfd 100644 (file)
@@ -1,3 +1,189 @@
+#grandvision-cart {
+  @bleu: #2459a9;
+  background-color: #f3f3f3;
+  color: #000;
+  padding: 0 40px 40px 40px;
+
+  .content {
+    text-align: left;
+    overflow: visible !important;
+
+    h2 {
+      text-transform: uppercase;
+      font-size: 20px;
+      margin-bottom: 10px;
+    }
+
+    #grandvision-selection {
+      display: inline-block;
+      width: 550px;
+      vertical-align: top;
+      margin-top: -20px;
+      height: 700px;
+
+      #emptyres {
+        display: none;
+      }
+
+      .filters {
+        margin-bottom: 10px;
+
+        .multi-select-container {
+          display: inline-block;
+          position: relative;
+          width: 125px;
+          margin-right: 5px;
+        }
+
+        .multi-select-menu {
+          position: absolute;
+          left: 0;
+          top: 0.8em;
+          float: left;
+          min-width: 100%;
+          background: #fff;
+          margin: 1em 0;
+          padding: 0.4em 0;
+          display: none;
+          z-index: 2;
+          width: 300px !important;
+        }
+
+
+        .multi-select-menu label {
+          color: #464646;
+          display: block;
+          padding: 3px 10px;
+          font-size: 16px;
+        }
+
+        .multi-select-menu input {
+          margin-right: 0.3em;
+          vertical-align: 0.1em;
+        }
+
+        .multi-select-button {
+          margin: 4px 0;
+          display: block;
+          color: #474747;
+          font-size: 0.875em;
+          padding: 0.2em 2em 0.2em 0.6em;
+          white-space: nowrap;
+          overflow: hidden;
+          text-overflow: ellipsis;
+          background-color: #fff;
+          border-radius: 2px;
+          cursor: default;
+          position: relative;
+        }
+
+        .multi-select-button.active {
+          background-color: @bleu;
+          color: #fff;
+        }
+
+        .multi-select-button.active:after {
+          border-color: #fff transparent transparent transparent;
+        }
+
+        .multi-select-container--open .multi-select-button.active::after {
+          border-color: transparent transparent #fff transparent
+        }
+
+        .multi-select-button:after {
+          content: "";
+          display: inline-block;
+          width: 0;
+          height: 0;
+          border-style: solid;
+          border-width: 0.4em 0.4em 0 0.4em;
+          border-color: #474747 transparent transparent transparent;
+          margin-left: 0.4em;
+          vertical-align: 0.1em;
+          position: absolute;
+          right: 10px;
+          top: 11px;
+        }
+
+        .multi-select-presets {
+          position: relative;
+          padding-top: 5px;
+          border-top: 1px solid #eee;
+        }
+
+        .multi-select-presets button {
+          cursor: pointer;
+          position: absolute;
+          top: 5px;
+          right: 5px;
+          background-color: @bleu;
+          color: #fff;
+          text-transform: uppercase;
+          border: 0;
+          border-radius: 4px;
+          padding: 2px 15px;
+          font-size: 17px;
+          font-family: "Open Sans", sans-serif;
+        }
+
+        .multi-select-container--open .multi-select-menu {
+          display: block;
+        }
+
+        .multi-select-container--open .multi-select-button:after {
+          border-width: 0 0.4em 0.4em 0.4em;
+          border-color: transparent transparent #999 transparent;
+        }
+      }
+
+      .items {
+        width: 550px;
+
+        .item {
+          width: 515px;
+          height: 57px;
+          padding: 10px;
+          background-color: #fff;
+          margin-bottom: 3px;
+          overflow: hidden;
+
+          .infos {
+            width: 370px;
+            float: left;
+            font-size: 10px;
+            margin-left: 10px;
+
+            h3 {
+              font-size: 14px;
+              text-transform: uppercase;
+            }
+          }
+
+          .img {
+            width: 115px;
+            height: 57px;
+            position: relative;
+            top: -13px;
+            float: left;
+
+            img {
+              width: 115px;
+            }
+          }
+        }
+      }
+    }
+
+    #grandvision-boxes {
+      display: inline-block;
+      width: 550px;
+      height: 700px;
+      vertical-align: top;
+      margin-top: -20px;
+    }
+  }
+}
+
 #grandvision-details {
   @bleu: #2459a9;
   background-color: #fff;
         left: 0px;
         width: 360px;
         max-width: 360px;
-        height:202px;
+        height: 202px;
         max-height: 202px;
         overflow: hidden;
 
 
       .dots {
         position: absolute;
-        top:210px;
-        left:40%;
+        top: 210px;
+        left: 40%;
 
         a {
           display: block;
           background-color: #ccc;
           margin: 0 10px;
           border-radius: 50%;
-          border:2px solid #fff;
+          border: 2px solid #fff;
 
           &:hover, &.active {
             border-color: @bleu;