diff --git a/ext/afform/admin/ang/afGuiEditor.css b/ext/afform/admin/ang/afGuiEditor.css index 8537bc578b8a..4b22cace9c6f 100644 --- a/ext/afform/admin/ang/afGuiEditor.css +++ b/ext/afform/admin/ang/afGuiEditor.css @@ -1,14 +1,11 @@ #afGuiEditor #afGuiEditor-palette { margin-right: 5px; + height: 100%; } #afGuiEditor #afGuiEditor-canvas { margin-left: 5px; -} - -#afGuiEditor .panel-body { - padding: 5px 12px; - position: relative; + height: 100%; } #afGuiEditor fieldset legend { @@ -26,13 +23,39 @@ margin-bottom: 10px; } -#afGuiEditor #afGuiEditor-palette-tabs li { +#afGuiEditor .panel { + height: 100%; +} +#afGuiEditor .panel-heading { + height: 44px; + padding: 10px; +} +#afGuiEditor .panel-heading ul.nav-tabs { + border-bottom: 0 none; +} +#afGuiEditor .panel-heading ul.nav-tabs li { top: 1px; } - -#afGuiEditor #afGuiEditor-palette-tabs li > a { - padding: 10px 15px; +#afGuiEditor .panel-heading ul.nav-tabs li.fluid-width-tab { + white-space: nowrap; + overflow: hidden; +} +#afGuiEditor .panel-heading ul.nav-tabs li.active { + max-width: 50%; +} +#afGuiEditor .panel-heading ul.nav-tabs li > a { + padding: 5px 3px 5px 8px; + height: 33px; font-size: 12px; + margin: 0; +} + +#afGuiEditor .panel-body { + padding: 5px 12px; + position: relative; + height: calc(100% - 44px); + overflow-y: scroll; + overflow-x: hidden; } #afGuiEditor .af-gui-columns { @@ -49,7 +72,7 @@ } #afGuiEditor .crm-editable-enabled, -#afGuiEditor-palette-tabs > li > a > span { +#afGuiEditor .panel-heading ul.nav-tabs li > a > span { display: inline-block; padding: 0 4px !important; border: 2px solid transparent !important; @@ -112,7 +135,7 @@ left: 0; padding-left: 15px; } -#afGuiEditor:not(.af-gui-dragging) #afGuiEditor-canvas:hover .af-gui-bar { +#afGuiEditor:not(.af-gui-dragging *) #afGuiEditor-canvas:hover .af-gui-bar { opacity: 1; transition: opacity .2s; } @@ -122,6 +145,16 @@ transition: opacity .1s; } +/* Disable menu while dragging */ +body.af-gui-dragging #civicrm-menu { + pointer-events: none; +} +/* Disable scrollbars while dragging */ +body.af-gui-dragging { + overflow-x: hidden; + overflow-y: hidden; +} + #afGuiEditor .af-gui-bar .btn.active { background-color: #b3b3b3; } @@ -142,9 +175,11 @@ position: relative; padding: 22px 3px 3px; min-height: 40px; - display: block; + margin-bottom: 10px; + margin-top: 10px; } +#afGuiEditor af-gui-container, #afGuiEditor af-gui-markup, #afGuiEditor af-gui-field, #afGuiEditor af-gui-edit-options { @@ -160,10 +195,12 @@ #afGuiEditor .af-gui-container-type-fieldset { box-shadow: 0 0 5px #bbbbbb; + margin-top: 20px; + margin-bottom: 20px; } #afGuiEditor .af-gui-container:hover, -#afGuiEditor.af-gui-dragging .af-gui-container { +.af-gui-dragging #afGuiEditor .af-gui-container { border: 2px dashed #757575; } #afGuiEditor .af-gui-container.af-gui-dragtarget { @@ -217,6 +254,20 @@ margin-top: 10px; } +#afGuiEditor .ui-sortable-helper { + height: 20px !important; + opacity: .5; + overflow: visible; +} +#afGuiEditor .ui-sortable-helper > * { + background-color: #d5d5d5; +} +#afGuiEditor .ui-sortable-helper .af-gui-palette-item { + height: 30px; + width: 300px; + border: 2px dashed #0071bd; +} + #afGuiEditor .af-gui-entity-palette-select-list { max-height: 400px; overflow-y: auto; diff --git a/ext/afform/admin/ang/afGuiEditor.js b/ext/afform/admin/ang/afGuiEditor.js index 406906a5c1a0..6fde62204abb 100644 --- a/ext/afform/admin/ang/afGuiEditor.js +++ b/ext/afform/admin/ang/afGuiEditor.js @@ -190,10 +190,10 @@ $(this).removeClass('af-gui-dragtarget'); }) .on('sortstart', '#afGuiEditor', function() { - $('#afGuiEditor').addClass('af-gui-dragging'); + $('body').addClass('af-gui-dragging'); }) .on('sortstop', function() { - $('.af-gui-dragging').removeClass('af-gui-dragging'); + $('body').removeClass('af-gui-dragging'); $('.af-gui-dragtarget').removeClass('af-gui-dragtarget'); }); }); diff --git a/ext/afform/admin/ang/afGuiEditor/afGuiEditor.component.js b/ext/afform/admin/ang/afGuiEditor/afGuiEditor.component.js index 8adebf6a865b..ba46a74fb7c6 100644 --- a/ext/afform/admin/ang/afGuiEditor/afGuiEditor.component.js +++ b/ext/afform/admin/ang/afGuiEditor/afGuiEditor.component.js @@ -26,13 +26,24 @@ $scope.saving = false; $scope.selectedEntityName = null; this.meta = afGui.meta; - var editor = this; + var editor = this, + sortableOptions = {}; this.$onInit = function() { // Load the current form plus blocks & fields afGui.resetMeta(); afGui.addMeta(this.data); initializeForm(); + + $timeout(fixEditorHeight); + $timeout(editor.adjustTabWidths); + $(window) + .on('resize.afGuiEditor', fixEditorHeight) + .on('resize.afGuiEditor', editor.adjustTabWidths); + }; + + this.$onDestroy = function() { + $(window).off('.afGuiEditor'); }; // Initialize the current form @@ -140,7 +151,11 @@ delete $scope.entities[type + num].loading; if (selectTab) { editor.selectEntity(type + num); + $timeout(function() { + editor.scrollToEntity(type + num); + }); } + $timeout(editor.adjustTabWidths); } if (meta.fields) { @@ -165,6 +180,7 @@ this.selectEntity = function(entityName) { $scope.selectedEntityName = entityName; + $timeout(editor.adjustTabWidths); }; this.getEntity = function(entityName) { @@ -175,6 +191,18 @@ return $scope.selectedEntityName; }; + // Scroll an entity's first fieldset into view of the canvas + this.scrollToEntity = function(entityName) { + var $canvas = $('#afGuiEditor-canvas-body'), + $entity = $('.af-gui-container-type-fieldset[data-entity="' + entityName + '"]').first(), + // Scrolltop value needed to place entity's fieldset at top of canvas + scrollValue = $canvas.scrollTop() + ($entity.offset().top - $canvas.offset().top), + // Maximum possible scrollTop (height minus contents height, adjusting for padding) + maxScroll = $('#afGuiEditor-canvas-body > *').height() - $canvas.height() + 20; + // Exceeding the maximum scrollTop breaks the animation so keep it under the limit + $canvas.animate({scrollTop: scrollValue > maxScroll ? maxScroll : scrollValue}, 500); + }; + this.getAfform = function() { return $scope.afform; }; @@ -232,6 +260,24 @@ return options; } + // Options for ui-sortable in field palette + this.getSortableOptions = function(entityName) { + if (!sortableOptions[entityName + '']) { + sortableOptions[entityName + ''] = { + helper: 'clone', + appendTo: '#afGuiEditor-canvas-body > af-gui-container', + containment: '#afGuiEditor-canvas-body', + update: editor.onDrop, + items: '> div:not(.disabled)', + connectWith: '#afGuiEditor-canvas ' + (entityName ? '[data-entity="' + entityName + '"] > ' : '') + '[ui-sortable]', + placeholder: 'af-gui-dropzone', + tolerance: 'pointer', + scrollSpeed: 8 + }; + } + return sortableOptions[entityName + '']; + }; + // Validates that a drag-n-drop action is allowed this.onDrop = function(event, ui) { var sort = ui.item.sortable; @@ -278,6 +324,27 @@ }); } }); + + // Force editor panels to a fixed height, to avoid palette scrolling offscreen + function fixEditorHeight() { + var height = $(window).height() - $('#afGuiEditor').offset().top; + $('#afGuiEditor').height(Math.floor(height)); + } + + // Compress tabs on small screens + this.adjustTabWidths = function() { + $('#afGuiEditor .panel-heading ul.nav-tabs li.active').css('max-width', ''); + $('#afGuiEditor .panel-heading ul.nav-tabs').each(function() { + var remainingSpace = Math.floor($(this).width()) - 1, + inactiveTabs = $(this).children('li.fluid-width-tab').not('.active'); + $(this).children('.active,:not(.fluid-width-tab)').each(function() { + remainingSpace -= $(this).width(); + }); + if (inactiveTabs.length) { + inactiveTabs.css('max-width', Math.floor(remainingSpace / inactiveTabs.length) + 'px'); + } + }); + }; } }); diff --git a/ext/afform/admin/ang/afGuiEditor/afGuiEditorCanvas.html b/ext/afform/admin/ang/afGuiEditor/afGuiEditorCanvas.html index df975f105891..0b767376da26 100644 --- a/ext/afform/admin/ang/afGuiEditor/afGuiEditorCanvas.html +++ b/ext/afform/admin/ang/afGuiEditor/afGuiEditorCanvas.html @@ -21,11 +21,13 @@