diff --git a/src/helpers/Cp.php b/src/helpers/Cp.php index cc986771fc2..3fea12f54f5 100644 --- a/src/helpers/Cp.php +++ b/src/helpers/Cp.php @@ -2325,6 +2325,7 @@ public static function fieldLayoutDesignerHtml(FieldLayout $fieldLayout, array $ Html::hiddenInput('fieldLayout', Json::encode($fieldLayoutConfig), [ 'data' => ['config-input' => true], ]) . + Html::beginTag('div', ['class' => 'fld-container']) . Html::beginTag('div', ['class' => 'fld-workspace']) . Html::beginTag('div', ['class' => 'fld-tabs']) . implode('', array_map(fn(FieldLayoutTab $tab) => self::_fldTabHtml($tab, $config['customizableTabs']), $tabs)) . @@ -2336,7 +2337,7 @@ public static function fieldLayoutDesignerHtml(FieldLayout $fieldLayout, array $ ]) : '') . Html::endTag('div') . // .fld-workspace - Html::beginTag('div', ['class' => 'fld-sidebar']) . + Html::beginTag('div', ['class' => 'fld-library']) . ($config['customizableUi'] ? Html::beginTag('section', [ 'class' => ['btngroup', 'btngroup--exclusive', 'small', 'fullwidth'], @@ -2377,7 +2378,8 @@ public static function fieldLayoutDesignerHtml(FieldLayout $fieldLayout, array $ implode('', array_map(fn(FieldLayoutElement $element) => self::layoutElementSelectorHtml($element, true), $availableUiElements)) . Html::endTag('div') // .fld-ui-library : '') . - Html::endTag('div') . // .fld-sidebar + Html::endTag('div') . // .fld-library + Html::endTag('div') . // .fld-container Html::endTag('div'); // .layoutdesigner } @@ -2399,6 +2401,7 @@ private static function _setLayoutOnElements(array $elements, FieldLayout $field */ private static function _fldTabHtml(FieldLayoutTab $tab, bool $customizable): string { + $menuId = sprintf('menu-%s', mt_rand()); return Html::beginTag('div', [ 'class' => 'fld-tab', @@ -2428,6 +2431,14 @@ private static function _fldTabHtml(FieldLayoutTab $tab, bool $customizable): st Html::endTag('div') . // .tabs Html::beginTag('div', ['class' => 'fld-tabcontent']) . implode('', array_map(fn(FieldLayoutElement $element) => self::layoutElementSelectorHtml($element, false), $tab->getElements())) . + Html::button(Craft::t('app', 'Add'), [ + 'class' => ['btn', 'add', 'icon', 'dashed', 'fullwidth', 'fld-add-btn'], + 'aria' => ['controls' => $menuId], + ]) . + Html::tag('div', options: [ + 'id' => $menuId, + 'class' => ['menu', 'menu--disclosure', 'fld-library-menu'], + ]) . Html::endTag('div') . // .fld-tabcontent Html::endTag('div'); // .fld-tab } diff --git a/src/translations/en/app.php b/src/translations/en/app.php index bb12764d6ee..895b944c59c 100644 --- a/src/translations/en/app.php +++ b/src/translations/en/app.php @@ -61,6 +61,7 @@ 'Add an option' => 'Add an option', 'Add to cart' => 'Add to cart', 'Add {type} above' => 'Add {type} above', + 'Add' => 'Add', 'Added to cart' => 'Added to cart', 'Address Fields' => 'Address Fields', 'Address Line 1' => 'Address Line 1', diff --git a/src/web/assets/cp/CpAsset.php b/src/web/assets/cp/CpAsset.php index 6c0d7f2b424..de0ab84ecb0 100644 --- a/src/web/assets/cp/CpAsset.php +++ b/src/web/assets/cp/CpAsset.php @@ -119,6 +119,7 @@ private function _registerTranslations(View $view): void 'Characters left: {chars, number}', 'A server error occurred.', 'Actions', + 'Add', 'Add…', 'All', 'Announcements', diff --git a/src/web/assets/cp/src/css/_fld.scss b/src/web/assets/cp/src/css/_fld.scss index f184d7be990..df8b488a034 100644 --- a/src/web/assets/cp/src/css/_fld.scss +++ b/src/web/assets/cp/src/css/_fld.scss @@ -4,7 +4,7 @@ $base: 24px; $tabPadding: 14px; $tabWidth: $base * 11; -$gridColor: hsl(212, 50%, 95%); +$gridColor: var(--gray-100); @mixin workspaceBg { background-color: var(--gray-050); @@ -14,6 +14,10 @@ $gridColor: hsl(212, 50%, 95%); } .layoutdesigner { + container-type: inline-size; +} + +.fld-container { display: flex; align-items: stretch; position: relative; @@ -30,10 +34,10 @@ $gridColor: hsl(212, 50%, 95%); .fld-workspace { flex: 1; @include border-radius( - var(--small-border-radius), + calc(var(--small-border-radius) - 1px), 0, 0, - var(--small-border-radius) + calc(var(--small-border-radius - 1px)) ); @include padding($base, 0, $base, $base); @include workspaceBg; @@ -47,47 +51,68 @@ $gridColor: hsl(212, 50%, 95%); } } - .fld-sidebar { + &:not(:has(.fld-library)) { + @include padding-right(0); + + .fld-workspace { + @include padding-right($base); + } + } + + .fld-library { position: absolute; top: 0; @include right(0); height: 100%; width: $tabWidth + 1; padding: $tabPadding; - display: flex; - flex-direction: column; + } +} - .btngroup { - margin-bottom: $tabPadding; - } +.fld-new-tab-btn:active { + background-color: var(--gray-050); +} - .fld-field-library { - margin: -3px; - padding: 3px; - flex: 1; - min-height: 0; - overflow: auto; +.fld-library-menu { + width: $tabWidth; + @include margin-left($tabPadding * -1); + padding: $tabPadding; +} - .fld-field-group { - margin-top: $tabPadding; +.fld-library { + display: flex; + flex-direction: column; + + .btngroup { + margin-bottom: $tabPadding; + } - h6 { - margin-bottom: 7px; - } + .fld-field-library { + margin: -3px; + padding: 3px; + flex: 1; + min-height: 0; + overflow: auto; + + .fld-field-group { + margin-top: $tabPadding; + + & > *:not(:first-child) { + margin-top: var(--s); } } + } - .filtered { - display: none; - } + .fld-ui-library > *:not(:first-child) { + margin-top: var(--s); } - .fld-new-tab-btn:active { - background-color: var(--gray-050); + .filtered { + display: none; } } -.fld-sidebar, +.layoutdesigner .fld-library, .fld-tab .tabs .tab, .fld-tab .fld-tabcontent, .fld-new-tab-btn, @@ -152,6 +177,17 @@ $gridColor: hsl(212, 50%, 95%); var(--medium-border-radius), var(--medium-border-radius) ); + + & > .fld-element, + & > .fld-add-btn { + &:not(:first-child) { + margin-top: var(--s); + } + } + + .fld-add-btn:not([aria-expanded='true']) { + display: none; + } } &.fld-insertion { @@ -190,10 +226,6 @@ $gridColor: hsl(212, 50%, 95%); cursor: grab; } - & + .fld-element { - margin-top: 7px; - } - &.fld-insertion { box-sizing: border-box; border: 2px dashed var(--hairline-color); @@ -337,3 +369,25 @@ $gridColor: hsl(212, 50%, 95%); @include margin(0, var(--neg-padding), 0, 0); } } + +@container (width < #{$base + $tabWidth + $base + $tabWidth + 1}) { + .fld-container { + @include padding-right(0); + + .fld-workspace { + @include padding-right($base); + } + + & > .fld-library { + display: none; + } + } + + .fld-tab { + .fld-tabcontent { + .fld-add-btn { + display: inline-flex !important; + } + } + } +} diff --git a/src/web/assets/cp/src/js/Craft.js b/src/web/assets/cp/src/js/Craft.js index b26b50bf8e7..985fb9e22cc 100644 --- a/src/web/assets/cp/src/js/Craft.js +++ b/src/web/assets/cp/src/js/Craft.js @@ -3011,12 +3011,12 @@ $.extend($.fn, { }); }, - disclosureMenu: function () { + disclosureMenu: function (settings) { return this.each(function () { const $trigger = $(this); // Only instantiate if it's not already a disclosure trigger, and it references a disclosure content if (!$trigger.data('trigger') && $trigger.attr('aria-controls')) { - new Garnish.DisclosureMenu($trigger); + new Garnish.DisclosureMenu($trigger, settings); } }); }, diff --git a/src/web/assets/cp/src/js/FieldLayoutDesigner.js b/src/web/assets/cp/src/js/FieldLayoutDesigner.js index 3790dc50655..a8b8d00c74f 100644 --- a/src/web/assets/cp/src/js/FieldLayoutDesigner.js +++ b/src/web/assets/cp/src/js/FieldLayoutDesigner.js @@ -1,14 +1,16 @@ /** global: Craft */ import $ from 'jquery'; +import disclosureMenu from '../../../garnish/src/DisclosureMenu'; /** global: Garnish */ Craft.FieldLayoutDesigner = Garnish.Base.extend( { $container: null, + $innerContainer: null, $configInput: null, $tabContainer: null, $newTabBtn: null, - $sidebar: null, + $libraryContainer: null, $selectedLibrary: null, $fieldLibrary: null, $uiLibrary: null, @@ -37,19 +39,20 @@ Craft.FieldLayoutDesigner = Garnish.Base.extend( this._fieldHandles = {}; - let $workspace = this.$container.children('.fld-workspace'); + this.$innerContainer = this.$container.children('.fld-container'); + const $workspace = this.$innerContainer.children('.fld-workspace'); this.$tabContainer = $workspace.children('.fld-tabs'); this.$newTabBtn = $workspace.children('.fld-new-tab-btn'); - this.$sidebar = this.$container.children('.fld-sidebar'); + this.$libraryContainer = this.$innerContainer.children('.fld-library'); this.$fieldLibrary = this.$selectedLibrary = - this.$sidebar.children('.fld-field-library'); + this.$libraryContainer.children('.fld-field-library'); let $fieldSearchContainer = this.$fieldLibrary.children('.search'); this.$fieldSearch = $fieldSearchContainer.children('input'); this.$clearFieldSearchBtn = $fieldSearchContainer.children('.clear'); - this.$fieldGroups = this.$sidebar.find('.fld-field-group'); + this.$fieldGroups = this.$libraryContainer.find('.fld-field-group'); this.$fields = this.$fieldGroups.children('.fld-element'); - this.$uiLibrary = this.$sidebar.children('.fld-ui-library'); + this.$uiLibrary = this.$libraryContainer.children('.fld-ui-library'); this.$uiLibraryElements = this.$uiLibrary.children(); // Set up the layout grids @@ -66,6 +69,7 @@ Craft.FieldLayoutDesigner = Garnish.Base.extend( } this.elementDrag = new Craft.FieldLayoutDesigner.ElementDrag(this); + this.initLibraryElements(this.$libraryContainer.find('.fld-element')); if (this.settings.customizableTabs) { this.tabDrag = new Craft.FieldLayoutDesigner.TabDrag(this); @@ -73,9 +77,9 @@ Craft.FieldLayoutDesigner = Garnish.Base.extend( this.addListener(this.$newTabBtn, 'activate', 'addTab'); } - // Set up the sidebar + // Set up the library if (this.settings.customizableUi) { - const $libraryPicker = this.$sidebar.children('.btngroup'); + const $libraryPicker = this.$libraryContainer.children('.btngroup'); new Craft.Listbox($libraryPicker, { onChange: ($selectedOption) => { const library = $selectedOption.data('library'); @@ -124,7 +128,7 @@ Craft.FieldLayoutDesigner = Garnish.Base.extend( label: Craft.t('app', 'New field'), class: 'mt-m fullwidth add icon dashed', }) - .appendTo(this.$sidebar); + .appendTo(this.$libraryContainer); this.addListener(this.$createFieldBtn, 'activate', async () => { this.createField(); @@ -190,6 +194,7 @@ Craft.FieldLayoutDesigner = Garnish.Base.extend( return; } + const menuId = `menu-${Math.floor(Math.random() * 1000000)}`; const $tab = $(`
@@ -197,7 +202,12 @@ Craft.FieldLayoutDesigner = Garnish.Base.extend( ${name}
-
+
+ + +
`); // keep it before the resize object @@ -271,7 +281,7 @@ Craft.FieldLayoutDesigner = Garnish.Base.extend( const $selector = $(response.data.selectorHtml); this.$fieldGroups.last().append($selector).removeClass('hidden'); this.refreshLibraryFields(); - this.elementDrag.addItems($selector); + this.initLibraryElements($selector); // refresh all instances of this field const $fields = designer.$tabContainer.find( @@ -282,6 +292,51 @@ Craft.FieldLayoutDesigner = Garnish.Base.extend( } }); }, + + initLibraryElements($elements) { + this.elementDrag.addItems($elements); + + this.addListener($elements, 'activate', (ev) => { + // if the library is in a disclosure menu, go ahead and add it to the tab + const $parent = this.$libraryContainer.parent(); + if ($parent.is('.fld-library-menu')) { + const disclosureMenu = $parent.data('disclosureMenu'); + const $libraryElement = $(ev.currentTarget); + const $element = + this.cloneLibraryElementForSelection($libraryElement); + const tab = disclosureMenu.$trigger + .closest('.fld-tab') + .data('fld-tab'); + $element.insertBefore(disclosureMenu.$trigger); + const element = tab.initElement($element); + element.updatePositionInConfig(); + this.tabGrid.refreshCols(true); + disclosureMenu.hide(); + } + }); + }, + + cloneLibraryElementForSelection($libraryElement) { + // Create a new element based on that one + const $element = $libraryElement.clone().removeClass('unused'); + + if (!Garnish.hasAttr($libraryElement, 'data-is-multi-instance')) { + // Hide the library element + $libraryElement + .css({visibility: 'inherit', display: 'field'}) + .addClass('hidden'); + + // Hide the group too? + if ($libraryElement.siblings('.fld-field:not(.hidden)').length === 0) { + $libraryElement.closest('.fld-field-group').addClass('hidden'); + } + } + + // Add it to the element dragger + this.elementDrag.addItems($element); + + return $element; + }, }, { defaults: { @@ -352,6 +407,7 @@ Craft.FieldLayoutDesigner.Tab = Garnish.Base.extend({ designer: null, uid: null, $container: null, + $addBtn: null, slideout: null, destroyed: false, @@ -396,7 +452,22 @@ Craft.FieldLayoutDesigner.Tab = Garnish.Base.extend({ } // initialize the elements - const $elements = this.$container.children('.fld-tabcontent').children(); + const $tabContent = this.$container.children('.fld-tabcontent'); + this.$addBtn = $tabContent.children('.fld-add-btn'); + + const disclosureMenu = this.$addBtn + .disclosureMenu({ + position: 'below', + }) + .data('disclosureMenu'); + disclosureMenu.on('beforeShow', () => { + this.designer.$libraryContainer.appendTo(disclosureMenu.$container); + }); + disclosureMenu.on('hide', () => { + this.designer.$libraryContainer.appendTo(this.designer.$innerContainer); + }); + + const $elements = $tabContent.children().not(this.$addBtn); for (let i = 0; i < $elements.length; i++) { this.initElement($($elements[i])); @@ -1632,10 +1703,8 @@ Craft.FieldLayoutDesigner.ElementDrag = }, findItems: function () { - // Return all of the used + unused fields - return this.designer.$tabContainer - .find('.fld-element') - .add(this.designer.$sidebar.find('.fld-element')); + // Return all of the in-use layout elements. (We'll add the library elements via initLibraryElement().) + return this.designer.$tabContainer.find('.fld-element'); }, /** @@ -1648,7 +1717,11 @@ Craft.FieldLayoutDesigner.ElementDrag = ); for (let i = 0; i < $fieldContainers.length; i++) { - $caboose = $caboose.add($('
').appendTo($fieldContainers[i])); + $caboose = $caboose.add( + $('
').insertBefore( + $fieldContainers.eq(i).children('.fld-add-btn') + ) + ); } return $caboose; @@ -1670,28 +1743,10 @@ Craft.FieldLayoutDesigner.ElementDrag = let showingInsertion = this.showingInsertion; if (showingInsertion) { if (this.draggingLibraryElement) { - // Create a new element based on that one - const $element = this.$draggee.clone().removeClass('unused'); - - if (!this.draggingMultiInstanceElement) { - // Hide the library field + // Clone the element nad set this.$draggee to the clone, as if we were dragging that all along + this.$draggee = this.designer.cloneLibraryElementForSelection( this.$draggee - .css({visibility: 'inherit', display: 'field'}) - .addClass('hidden'); - - // Hide the group too? - if ( - this.$draggee.siblings('.fld-field:not(.hidden)').length === 0 - ) { - this.$draggee.closest('.fld-field-group').addClass('hidden'); - } - } - - // Set this.$draggee to the clone, as if we were dragging that all along - this.$draggee = $element; - - // Remember it for later - this.addItems($element); + ); } } else if (!this.draggingLibraryElement) { let $libraryElement = this.draggingField diff --git a/src/web/assets/cp/src/js/Grid.js b/src/web/assets/cp/src/js/Grid.js index f9f8eaa7d2c..82da396429c 100644 --- a/src/web/assets/cp/src/js/Grid.js +++ b/src/web/assets/cp/src/js/Grid.js @@ -145,7 +145,11 @@ Craft.Grid = Garnish.Base.extend( } // Same number of columns as before? - if (force !== true && this.totalCols === this.refreshCols._.totalCols) { + if ( + force !== true && + this.totalCols === this.refreshCols._.totalCols && + !this.settings.snapToGrid + ) { this.completeRefreshCols(); return; } @@ -486,7 +490,7 @@ Craft.Grid = Garnish.Base.extend( // Now position the items this.positionItems(); - // Update the positions as the items' heigthts change + // Update the positions as the items' heights change this.addListener(this.$items, 'resize', 'onItemResize'); } } diff --git a/src/web/assets/garnish/dist/garnish.js b/src/web/assets/garnish/dist/garnish.js index 8a268d5c33c..a6fef3dda9f 100644 --- a/src/web/assets/garnish/dist/garnish.js +++ b/src/web/assets/garnish/dist/garnish.js @@ -1,3 +1,3 @@ /*! For license information please see garnish.js.LICENSE.txt */ -!function(){var t={55:function(t,e,i){var s=i(38),n=i(820),o=s.default;void 0===n.Garnish&&(n.Garnish=o),t.exports=s},820:function(t,e,i){"use strict";t.exports=function(){if("object"==typeof globalThis)return globalThis;var t;try{t=this||new Function("return this")()}catch(t){if("object"==typeof window)return window;if("object"==typeof self)return self;if(void 0!==i.g)return i.g}return t}()},38:function(t,e,i){"use strict";i.r(e),i.d(e,{default:function(){return N}});var s=jQuery,n=i.n(s);function o(t){return o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},o(t)}var r=function(){};function a(t){return a="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},a(t)}r.extend=function(t,e){var i=r.prototype.extend;r._prototyping=!0;var s=new this;i.call(s,t),s.base=function(){},delete r._prototyping;var n=s.constructor,o=s.constructor=function(){if(!r._prototyping)if(this._constructing||this.constructor==o)this._constructing=!0,n.apply(this,arguments),delete this._constructing;else if(null!=arguments[0])return(arguments[0].extend||i).call(arguments[0],s)};return o.ancestor=this,o.extend=this.extend,o.forEach=this.forEach,o.implement=this.implement,o.prototype=s,o.toString=this.toString,o.valueOf=function(t){return"object"==t?o:n.valueOf()},i.call(o,e),"function"==typeof o.init&&o.init(),o},r.prototype={extend:function(t,e){if(arguments.length>1){var i=this[t];if(i&&"function"==typeof e&&(!i.valueOf||i.valueOf()!=e.valueOf())&&/\bbase\b/.test(e)){var s=e.valueOf();e=function(){var t=this.base||r.prototype.base;this.base=i;var e=s.apply(this,arguments);return this.base=t,e},e.valueOf=function(t){return"object"==t?e:s},e.toString=r.toString}this[t]=e}else if(t){var n=r.prototype.extend;r._prototyping||"function"==typeof this||(n=this.extend||n);for(var a={toSource:null},h=["constructor","toString","valueOf"],l=r._prototyping?0:1;u=h[l++];)t[u]!=a[u]&&n.call(this,u,t[u]);for(var u in t)if(!a[u]){var c=Object.getOwnPropertyDescriptor(t,u);"undefined"!=o(c.value)?n.call(this,u,c.value):Object.defineProperty(this,u,c)}}return this}},r=r.extend({constructor:function(){this.extend(arguments[0])}},{ancestor:Object,version:"1.1",forEach:function(t,e,i){for(var s in t)void 0===this.prototype[s]&&e.call(i,t[s],s,t)},implement:function(){for(var t=0;t=0;n--){var o=this._eventHandlers[n];o.type!==s[0]||s[1]&&o.namespace!==s[1]||o.handler!==e||this._eventHandlers.splice(n,1)}},trigger:function(t,e){var i,s,o,r={type:t,target:this};for(i=0;it.length)&&(e=t.length);for(var i=0,s=new Array(e);ithis.drag._maxMouseScrollY&&(this.drag._scrollProperty="scrollTop",this.drag._scrollAxis="Y",this.drag._scrollDist=Math.round((this.mouseY-this.drag._maxMouseScrollY)/2)))),this.drag._scrollProperty||this.settings.axis===R.Y_AXIS||(this.drag._winScrollLeft=R.$win.scrollLeft(),this.drag._minMouseScrollX=this.drag._winScrollLeft+R.BaseDrag.windowScrollTargetSize,this.mouseXthis.drag._maxMouseScrollX&&(this.drag._scrollProperty="scrollLeft",this.drag._scrollAxis="X",this.drag._scrollDist=Math.round((this.mouseX-this.drag._maxMouseScrollX)/2)))),this.drag._scrollProperty?(this.scrollProperty||(this.scrollProxy||(this.scrollProxy=this._scrollWindow.bind(this)),this.scrollFrame&&(R.cancelAnimationFrame(this.scrollFrame),this.scrollFrame=null),this.scrollFrame=R.requestAnimationFrame(this.scrollProxy)),this.scrollProperty=this.drag._scrollProperty,this.scrollAxis=this.drag._scrollAxis,this.scrollDist=this.drag._scrollDist):this._cancelWindowScroll()),this.onDrag()},stopDragging:function(){this.dragging=!1,this.onDragStop(),this._cancelWindowScroll()},addItems:function(t){var e,i=this,s=function(t,e){var i="undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(!i){if(Array.isArray(t)||(i=function(t,e){if(t){if("string"==typeof t)return u(t,e);var i=Object.prototype.toString.call(t).slice(8,-1);return"Object"===i&&t.constructor&&(i=t.constructor.name),"Map"===i||"Set"===i?Array.from(t):"Arguments"===i||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(i)?u(t,e):void 0}}(t))||e&&t&&"number"==typeof t.length){i&&(t=i);var s=0,n=function(){};return{s:n,n:function(){return s>=t.length?{done:!0}:{done:!1,value:t[s++]}},e:function(t){throw t},f:n}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var o,r=!0,a=!1;return{s:function(){i=i.call(t)},n:function(){var t=i.next();return r=t.done,t},e:function(t){a=!0,o=t},f:function(){try{r||null==i.return||i.return()}finally{if(a)throw o}}}}(t=n().makeArray(t));try{var o=function(){var t=e.value;n().data(t,"drag")&&(console.warn("Element was added to more than one dragger"),n().data(t,"drag").removeItems(t)),n().data(t,"drag",i),i.addListener(i._getItemHandle(t),"mousedown",(function(e){i._handleMouseDown(e,t)}))};for(s.s();!(e=s.n()).done;)o()}catch(t){s.e(t)}finally{s.f()}this.$items=this.$items.add(t)},removeItems:function(t){t=n().makeArray(t);for(var e=0;e=(null!==(e=this.settings.minMouseDist)&&void 0!==e?e:R.BaseDrag.minMouseDist)&&this.startDragging()),this.dragging&&this.drag(!0)},_handleMouseUp:function(t){this.removeAllListeners(R.$doc),this.dragging&&this.stopDragging(),this.$targetItem=null},_scrollWindow:function(){this._.scrollPos=R.$scrollContainer[this.scrollProperty](),this._.scrollTargetPos=this._.scrollPos+this.scrollDist,this._.scrollTargetPos<0?this._.scrollTargetPos=0:(this._.$scrollContainer=R.$scrollContainer[0]===R.$win[0]?R.$bod:R.$scrollContainer,"Y"===this.scrollAxis?this._.scrollMax=this._.$scrollContainer[0].clientHeight-R.$scrollContainer.height():this._.scrollMax=this._.$scrollContainer[0].clientWidth-R.$scrollContainer.width(),this._.scrollTargetPos>this._.scrollMax&&(this._.scrollTargetPos=this._.scrollMax)),R.$scrollContainer[this.scrollProperty](this._.scrollTargetPos),this["mouse"+this.scrollAxis]-=this._.scrollPos-R.$scrollContainer[this.scrollProperty](),this["realMouse"+this.scrollAxis]=this["mouse"+this.scrollAxis],this.drag(),this.scrollFrame=R.requestAnimationFrame(this.scrollProxy)},_cancelWindowScroll:function(){this.scrollFrame&&(R.cancelAnimationFrame(this.scrollFrame),this.scrollFrame=null),this.scrollProperty=null,this.scrollAxis=null,this.scrollDist=null},_deinitItem:function(t){this.removeAllListeners(t),n().removeData(t,"drag")}},{minMouseDist:1,windowScrollTargetSize:25,defaults:{minMouseDist:null,handle:null,axis:null,ignoreHandleSelector:"input, textarea, button, select, .btn",onDragStart:n().noop,onDrag:n().noop,onDragStop:n().noop}}),d=h.extend({$container:null,$all:null,$options:null,init:function(t){this.$container=n()(t),this.$container.data("checkboxSelect")&&(console.warn("Double-instantiating a checkbox select on an element"),this.$container.data("checkboxSelect").destroy()),this.$container.data("checkboxSelect",this);var e=this.$container.find("input");this.$all=e.filter(".all:first"),this.$options=e.not(this.$all),this.addListener(this.$all,"change","onAllChange")},onAllChange:function(){var t=this.$all.prop("checked");this.$options.prop({checked:t,disabled:t})},destroy:function(){this.$container.removeData("checkboxSelect"),this.base()}}),g=h.extend({$target:null,options:null,$menu:null,showingMenu:!1,init:function(t,e,i){this.$target=n()(t),this.$target.data("contextmenu")&&(console.warn("Double-instantiating a context menu on an element"),this.$target.data("contextmenu").destroy()),this.$target.data("contextmenu",this),this.options=e,this.setSettings(i,R.ContextMenu.defaults),R.ContextMenu.counter++,this.enable()},buildMenu:function(){this.$menu=n()('