diff --git a/bin/Site/Area.css b/bin/Site/Area.css index 26ec9377a51a86e774ff237e15489cb498712ad9..82ec00839aeb94ea871a1a1f85e468fa6b38c8b4 100644 --- a/bin/Site/Area.css +++ b/bin/Site/Area.css @@ -2,6 +2,7 @@ .quiqqer-bricks-site-category-area { float: left; margin-bottom: 20px; + overflow: hidden; position: relative; width: 100%; } @@ -25,6 +26,18 @@ top: 4px; } +.quiqqer-bricks-site-category-area-list { + float: left; + padding: 0; + position: relative; + width: 100%; +} + +.quiqqer-bricks-site-category-area-brick { + list-style-type: none; + position: relative; +} + .quiqqer-bricks-site-category-area-brick select { background: #fff; margin: 2px; @@ -45,3 +58,25 @@ text-decoration: line-through; } +.quiqqer-bricks-site-category-area-placeholder { + background: #fff; + cursor: pointer; + height: 100%; + left: 0; + padding: 5px; + position: absolute; + top: 0; + width: 100%; +} + +.quiqqer-bricks-site-category-area-dd-active .quiqqer-bricks-site-category-area-placeholder { + background: #2f8fc6; + color: #fff; +} + +.quiqqer-bricks-site-category-area-extraButtons { + float: right; + opacity: 0; + position: relative; + width: 0 +} diff --git a/bin/Site/Area.js b/bin/Site/Area.js index a9c1158a848b7d75649b74db6e3d4f39328f7ab1..14edb1ec4395cb854d86996d38631353a692fc91 100644 --- a/bin/Site/Area.js +++ b/bin/Site/Area.js @@ -15,10 +15,11 @@ define('package/quiqqer/bricks/bin/Site/Area', [ 'qui/controls/elements/List', 'Locale', 'Ajax', + 'package/quiqqer/bricks/bin/Sortables', 'css!package/quiqqer/bricks/bin/Site/Area' -], function (QUI, QUIControl, QUIButton, QUIPopup, QUIConfirm, QUIList, QUILocale, QUIAjax) +], function (QUI, QUIControl, QUIButton, QUIPopup, QUIConfirm, QUIList, QUILocale, QUIAjax, Sortables) { "use strict"; @@ -50,13 +51,19 @@ define('package/quiqqer/bricks/bin/Site/Area', [ { this.parent( options ); - this.$AddButton = false; - this.$SettingsButton = false; this.$availableBricks = []; this.$loaded = false; this.$brickIds = []; this.$brickData = []; + this.$AddButton = false; + this.$SettingsButton = false; + this.$SortableButton = false; + this.$MoreButton = false; + + this.$List = false; + this.$FXExtraBtns = false; + this.addEvents({ onInject : this.$onInject }); @@ -64,25 +71,39 @@ define('package/quiqqer/bricks/bin/Site/Area', [ /** * Return the domnode element - * @return {Element} + * @return {HTMLElement} */ create: function () { - var title = this.getAttribute( 'title' ); + var self = this, + title = this.getAttribute( 'title' ); this.$Elm = new Element('div', { 'class' : 'quiqqer-bricks-site-category-area', html : '<div class="quiqqer-bricks-site-category-area-title">'+ QUILocale.get( title.group, title.var ) + ' <div class="quiqqer-bricks-site-category-area-buttons"></div>' + - '</div>', + '</div><ul class="quiqqer-bricks-site-category-area-list"></ul>', 'data-name' : this.getAttribute( 'name' ) }); + // Elements var Buttons = this.$Elm.getElement( '.quiqqer-bricks-site-category-area-buttons' ); + var ExtraButtons = new Element('div', { + 'class' : 'quiqqer-bricks-site-category-area-extraButtons' + }); + + + this.$FXExtraBtns = moofx( ExtraButtons ); + + this.$List = this.$Elm.getElement( + '.quiqqer-bricks-site-category-area-list' + ); + + // buttons this.$AddButton = new QUIButton({ text : QUILocale.get( lg, 'site.area.button.add' ), textimage : 'icon-plus', @@ -92,6 +113,30 @@ define('package/quiqqer/bricks/bin/Site/Area', [ } }).inject( Buttons ); + ExtraButtons.inject( Buttons ); + + this.$MoreButton = new QUIButton({ + title : QUILocale.get( lg, 'site.area.button.area.more.openIt' ), + icon : 'icon-caret-left', + events : + { + onClick : function(Btn) + { + if ( Btn.getAttribute( 'icon' ) == 'icon-caret-left' ) + { + self.openButtons(); + return; + } + + self.closeButtons(); + } + }, + styles : { + marginLeft : 5 + } + }).inject( Buttons ); + + // extra buttons this.$SettingsButton = new QUIButton({ title : QUILocale.get( lg, 'site.area.button.area.settings' ), icon : 'icon-gears', @@ -101,7 +146,31 @@ define('package/quiqqer/bricks/bin/Site/Area', [ styles : { marginLeft : 10 } - }).inject( Buttons ); + }).inject( ExtraButtons ); + + this.$SortableButton = new QUIButton({ + title : QUILocale.get( lg, 'site.area.button.area.sort' ), + icon : 'icon-sort', + events : + { + onClick : function(Btn) + { + if ( Btn.isActive() ) + { + Btn.setNormal(); + self.unsortable(); + return; + } + + Btn.setActive(); + self.sortable(); + } + }, + styles : { + marginLeft : 5 + } + }).inject( ExtraButtons ); + return this.$Elm; }, @@ -264,12 +333,12 @@ define('package/quiqqer/bricks/bin/Site/Area', [ var self = this; - var Elm = new Element('div', { + var Elm = new Element('li', { 'class' : 'quiqqer-bricks-site-category-area-brick', html : '<select></select>' }); - Elm.inject( this.$Elm ); + Elm.inject( this.$List ); Select = Elm.getElement( 'select' ); new QUIButton({ @@ -340,6 +409,201 @@ define('package/quiqqer/bricks/bin/Site/Area', [ return data; }, + /** + * sort methods + */ + + /** + * Switch the sortable on + */ + sortable : function() + { + var Elm = this.getElm(), + elements = Elm.getElements( + '.quiqqer-bricks-site-category-area-brick' + ); + + elements.each(function(Brick) + { + var i, len, buttons, Button; + + buttons = Brick.getElements( '.qui-button' ); + + for ( i = 0, len = buttons.length; i < len; i++ ) + { + Button = QUI.Controls.getById( buttons[ i ].get( 'data-quiid' ) ); + + if ( Button ) { + Button.setDisable(); + } + } + + var Select = Brick.getElement( 'select' ), + Option = Select.getElement( 'option[value="'+ Select.value +'"]' ); + + new Element('div', { + 'class' : 'quiqqer-bricks-site-category-area-placeholder', + html : Option.get( 'html' ) + }).inject( Brick ); + }); + + Elm.getElements( 'select' ).set( 'disabled', true ); + + + new Sortables( this.$List, { + revert: { + duration: 500, + transition: 'elastic:out' + }, + clone : function(event) + { + var Target = event.target; + + if ( Target.nodeName != 'LI' ) { + Target = Target.getParent( 'li' ); + } + + var size = Target.getSize(), + pos = Target.getPosition( Target.getParent('ul') ); + + return new Element('div', { + styles : { + background : 'rgba(0,0,0,0.5)', + height : size.y, + top : pos.y, + width : size.x, + zIndex : 1000 + } + }); + }, + + onStart : function(element) + { + var Ul = element.getParent( 'ul' ); + + element.addClass( 'quiqqer-bricks-site-category-area-dd-active' ); + + Ul.setStyles({ + height : Ul.getSize().y, + overflow : 'hidden', + width : Ul.getSize().x + }); + }, + + onComplete : function(element) + { + var Ul = element.getParent( 'ul' ); + + element.removeClass( 'quiqqer-bricks-site-category-area-dd-active' ); + + Ul.setStyles({ + height : null, + overflow : null, + width : null + }); + } + }); + }, + + /** + * Switch the sortable off + */ + unsortable : function() + { + var Elm = this.getElm(), + elements = Elm.getElements( + '.quiqqer-bricks-site-category-area-brick' + ); + + Elm.getElements( 'select' ).set( 'disabled', false ); + Elm.getElements( '.quiqqer-bricks-site-category-area-placeholder').destroy(); + + elements.each(function(Brick) + { + var i, len, buttons, Button; + + buttons = Brick.getElements( '.qui-button' ); + + for ( i = 0, len = buttons.length; i < len; i++ ) + { + Button = QUI.Controls.getById( buttons[ i ].get( 'data-quiid' ) ); + + if ( Button ) { + Button.setEnable(); + } + } + }); + }, + + /** + * Opens the extra settings buttons + * + * @param {Function} callback + */ + openButtons : function(callback) + { + var self = this; + + this.$AddButton.hide(); + + self.$FXExtraBtns.style({ + borderLeft : '2px solid #cccfd5', + height : 30, + overflow : 'hidden' + }); + + this.$FXExtraBtns.animate({ + opacity : 1, + width : 85, + marginLeft : 10 + }, { + callback : function() + { + self.$MoreButton.setAttribute( 'icon', 'icon-caret-right' ); + + self.$FXExtraBtns.style({ + overflow : null + }); + + if ( typeof callback === 'function' ) { + callback(); + } + } + }); + }, + + /** + * Close the extra settings buttons + * + * * @param {Function} callback + */ + closeButtons : function( callback ) + { + var self = this; + + this.$FXExtraBtns.style({ + overflow : 'hidden', + borderLeft : null, + marginLeft : 0 + }); + + this.$FXExtraBtns.animate({ + opacity : 0, + width : 0 + }, { + callback : function() + { + self.$MoreButton.setAttribute( 'icon', 'icon-caret-left' ); + self.$AddButton.show(); + + + if ( typeof callback === 'function' ) { + callback(); + } + } + }); + }, + /** * dialogs */ diff --git a/bin/Sortables.js b/bin/Sortables.js new file mode 100644 index 0000000000000000000000000000000000000000..2988a44b7b9b9cfcd70209816fb80a5e19f90e29 --- /dev/null +++ b/bin/Sortables.js @@ -0,0 +1,254 @@ + +/* + --- + + script: Sortables.js + + name: Sortables + + description: Class for creating a drag and drop sorting interface for lists of items. + + license: MIT-style license + + authors: + - Tom Occhino + + requires: + - Core/Fx.Morph + - Drag.Move + + provides: [Sortables] + + ... + */ + +define('package/quiqqer/bricks/bin/Sortables', function() +{ + "use strict"; + + return new Class({ + + Implements: [ Events, Options ], + + options: { + /* + onSort: function(element, clone){}, + onStart: function(element, clone){}, + onComplete: function(element){},*/ + opacity: 1, + clone: false, + revert: false, + handle: false, + dragOptions: {}, + unDraggableTags: ['button', 'input', 'a', 'textarea', 'select', 'option'] + }, + + initialize: function (lists, options) { + this.setOptions(options); + + this.elements = []; + this.lists = []; + this.idle = true; + + this.addLists($$(document.id(lists) || lists)); + + if (!this.options.clone) this.options.revert = false; + if (this.options.revert) this.effect = new Fx.Morph(null, Object.merge({ + duration: 250, + link: 'cancel' + }, this.options.revert)); + }, + + attach: function () { + this.addLists(this.lists); + return this; + }, + + detach: function () { + this.lists = this.removeLists(this.lists); + return this; + }, + + addItems: function () { + Array.flatten(arguments).each(function (element) { + this.elements.push(element); + var start = element.retrieve('sortables:start', function (event) { + this.start.call(this, event, element); + }.bind(this)); + (this.options.handle ? element.getElement(this.options.handle) || element : element).addEvent('mousedown', start); + }, this); + return this; + }, + + addLists: function () { + Array.flatten(arguments).each(function (list) { + this.lists.include(list); + this.addItems(list.getChildren()); + }, this); + return this; + }, + + removeItems: function () { + return $$(Array.flatten(arguments).map(function (element) { + this.elements.erase(element); + var start = element.retrieve('sortables:start'); + (this.options.handle ? element.getElement(this.options.handle) || element : element).removeEvent('mousedown', start); + + return element; + }, this)); + }, + + removeLists: function () { + return $$(Array.flatten(arguments).map(function (list) { + this.lists.erase(list); + this.removeItems(list.getChildren()); + + return list; + }, this)); + }, + + getDroppableCoordinates: function (element) { + var offsetParent = element.getOffsetParent(); + var position = element.getPosition(offsetParent); + var scroll = { + w: window.getScroll(), + offsetParent: offsetParent.getScroll() + }; + position.x += scroll.offsetParent.x; + position.y += scroll.offsetParent.y; + + if (offsetParent.getStyle('position') == 'fixed') { + position.x -= scroll.w.x; + position.y -= scroll.w.y; + } + + return position; + }, + + getClone: function (event, element) { + if (!this.options.clone) return new Element(element.tagName).inject(document.body); + if (typeOf(this.options.clone) == 'function') return this.options.clone.call(this, event, element, this.list); + var clone = element.clone(true).setStyles({ + margin: 0, + position: 'absolute', + visibility: 'hidden', + width: element.getStyle('width') + }).addEvent('mousedown', function (event) { + element.fireEvent('mousedown', event); + }); + //prevent the duplicated radio inputs from unchecking the real one + if (clone.get('html').test('radio')) { + clone.getElements('input[type=radio]').each(function (input, i) { + input.set('name', 'clone_' + i); + if (input.get('checked')) element.getElements('input[type=radio]')[i].set('checked', true); + }); + } + + return clone.inject(this.list).setPosition(this.getDroppableCoordinates(this.element)); + }, + + getDroppables: function () { + var droppables = this.list.getChildren().erase(this.clone).erase(this.element); + if (!this.options.constrain) droppables.append(this.lists).erase(this.list); + return droppables; + }, + + insert: function (dragging, element) { + var where = 'inside'; + if (this.lists.contains(element)) { + this.list = element; + this.drag.droppables = this.getDroppables(); + } else { + where = this.element.getAllPrevious().contains(element) ? 'before' : 'after'; + } + this.element.inject(element, where); + this.fireEvent('sort', [this.element, this.clone]); + }, + + start: function (event, element) { + if ( + !this.idle || + event.rightClick || + (!this.options.handle && this.options.unDraggableTags.contains(event.target.get('tag'))) + ) return; + + this.idle = false; + this.element = element; + this.opacity = element.getStyle('opacity'); + this.list = element.getParent(); + this.clone = this.getClone(event, element); + + this.drag = new Drag.Move(this.clone, Object.merge({ + + droppables: this.getDroppables() + }, this.options.dragOptions)).addEvents({ + onSnap: function () { + event.stop(); + this.clone.setStyle('visibility', 'visible'); + this.element.setStyle('opacity', this.options.opacity || 0); + this.fireEvent('start', [this.element, this.clone]); + }.bind(this), + onEnter: this.insert.bind(this), + onCancel: this.end.bind(this), + onComplete: this.end.bind(this) + }); + + this.clone.inject(this.element, 'before'); + this.drag.start(event); + }, + + end: function () { + this.drag.detach(); + this.element.setStyle('opacity', this.opacity); + var self = this; + if (this.effect) { + var dim = this.element.getStyles('width', 'height'), + clone = this.clone, + pos = clone.computePosition(this.getDroppableCoordinates(clone)); + + var destroy = function () { + this.removeEvent('cancel', destroy); + clone.destroy(); + self.reset(); + }; + + this.effect.element = clone; + this.effect.start({ + top: pos.top, + left: pos.left, + width: dim.width, + height: dim.height, + opacity: 0.25 + }).addEvent('cancel', destroy).chain(destroy); + } else { + this.clone.destroy(); + self.reset(); + } + + }, + + reset: function () { + this.idle = true; + this.fireEvent('complete', this.element); + }, + + serialize: function () { + var params = Array.link(arguments, { + modifier: Type.isFunction, + index: function (obj) { + return obj !== null; + } + }); + var serial = this.lists.map(function (list) { + return list.getChildren().map(params.modifier || function (element) { + return element.get('id'); + }, this); + }, this); + + var index = params.index; + if (this.lists.length == 1) index = 0; + return (index || index === 0) && index >= 0 && index < this.lists.length ? serial[index] : serial; + } + + }); +});