Merge pull request #1087 from bjarnef/dev-v7-U4-6425
U4-6425 - Maximum property for Related Links
This commit is contained in:
@@ -243,7 +243,8 @@
|
||||
background-color: @grayLighter
|
||||
}
|
||||
|
||||
.table-striped tbody i:hover {
|
||||
/* don't hide all icons, e.g. for a sortable handle */
|
||||
.umb-listview .table-striped tbody i:not(.handle):hover {
|
||||
display: none !important
|
||||
}
|
||||
|
||||
|
||||
@@ -714,7 +714,45 @@ ul.color-picker li a {
|
||||
}
|
||||
|
||||
//
|
||||
// tags
|
||||
// Related links
|
||||
// --------------------------------------------------
|
||||
.umb-relatedlinks table > tr > td { word-wrap:break-word; word-break: break-all; border-bottom: 1px solid transparent; }
|
||||
.umb-relatedlinks .handle { cursor:move; }
|
||||
.umb-relatedlinks table > tbody > tr.unsortable .handle { cursor:default; }
|
||||
|
||||
.umb-relatedlinks table td.col-sort { width: 20px; }
|
||||
.umb-relatedlinks table td.col-caption { min-width: 200px; }
|
||||
.umb-relatedlinks table td.col-link { min-width: 200px; }
|
||||
.umb-relatedlinks table td.col-actions { min-width: 120px; }
|
||||
|
||||
.umb-relatedlinks table td.col-caption .control-wrapper,
|
||||
.umb-relatedlinks table td.col-link .control-wrapper { display: flex; }
|
||||
|
||||
.umb-relatedlinks table td.col-caption .control-wrapper input[type="text"],
|
||||
.umb-relatedlinks table td.col-link .control-wrapper input[type="text"] { width: auto; flex: 1; }
|
||||
|
||||
/* sortable placeholder */
|
||||
.umb-relatedlinks .sortable-placeholder {
|
||||
background-color: @tableBackgroundAccent;
|
||||
display: table-row;
|
||||
}
|
||||
.umb-relatedlinks .sortable-placeholder > td {
|
||||
display: table-cell;
|
||||
padding: 8px;
|
||||
}
|
||||
.umb-relatedlinks .ui-sortable-helper {
|
||||
display: table-row;
|
||||
background-color: @white;
|
||||
opacity: 0.7;
|
||||
}
|
||||
.umb-relatedlinks .ui-sortable-helper > td {
|
||||
display: table-cell;
|
||||
border-bottom: 1px solid @tableBorder;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Tags
|
||||
// --------------------------------------------------
|
||||
.umb-tags{border: @grayLighter solid 1px; padding: 10px; font-size: 13px; text-shadow: none;}
|
||||
.umb-tags .tag{cursor: pointer; margin: 7px; padding: 7px; background: @blue}
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
angular.module("umbraco")
|
||||
.controller("Umbraco.PropertyEditors.RelatedLinksController",
|
||||
function ($rootScope, $scope, dialogService) {
|
||||
function ($rootScope, $scope, dialogService, iconHelper) {
|
||||
|
||||
if (!$scope.model.value) {
|
||||
$scope.model.value = [];
|
||||
}
|
||||
|
||||
$scope.model.config.max = isNumeric($scope.model.config.max) && $scope.model.config.max !== 0 ? $scope.model.config.max : Number.MAX_VALUE;
|
||||
|
||||
$scope.newCaption = '';
|
||||
$scope.newLink = 'http://';
|
||||
$scope.newNewWindow = false;
|
||||
$scope.newInternal = null;
|
||||
$scope.newInternalName = '';
|
||||
$scope.newInternalIcon = null;
|
||||
$scope.addExternal = true;
|
||||
$scope.currentEditLink = null;
|
||||
$scope.hasError = false;
|
||||
@@ -72,7 +75,6 @@
|
||||
}
|
||||
$scope.model.value[idx].edit = true;
|
||||
};
|
||||
|
||||
|
||||
$scope.saveEdit = function (idx) {
|
||||
$scope.model.value[idx].title = $scope.model.value[idx].caption;
|
||||
@@ -107,6 +109,7 @@
|
||||
this.edit = false;
|
||||
this.isInternal = true;
|
||||
this.internalName = $scope.newInternalName;
|
||||
this.internalIcon = $scope.newInternalIcon;
|
||||
this.type = "internal";
|
||||
this.title = $scope.newCaption;
|
||||
};
|
||||
@@ -117,7 +120,7 @@
|
||||
$scope.newNewWindow = false;
|
||||
$scope.newInternal = null;
|
||||
$scope.newInternalName = '';
|
||||
|
||||
$scope.newInternalIcon = null;
|
||||
}
|
||||
$event.preventDefault();
|
||||
};
|
||||
@@ -141,23 +144,73 @@
|
||||
$scope.model.value[index + direction] = temp;
|
||||
};
|
||||
|
||||
//helper for determining if a user can add items
|
||||
$scope.canAdd = function () {
|
||||
return $scope.model.config.max <= 0 || $scope.model.config.max > countVisible();
|
||||
}
|
||||
|
||||
//helper that returns if an item can be sorted
|
||||
$scope.canSort = function () {
|
||||
return countVisible() > 1;
|
||||
}
|
||||
|
||||
$scope.sortableOptions = {
|
||||
containment: 'parent',
|
||||
axis: 'y',
|
||||
handle: '.handle',
|
||||
cursor: 'move',
|
||||
cancel: '.no-drag',
|
||||
containment: 'parent',
|
||||
placeholder: 'sortable-placeholder',
|
||||
forcePlaceholderSize: true,
|
||||
helper: function (e, ui) {
|
||||
// When sorting <trs>, the cells collapse. This helper fixes that: http://www.foliotek.com/devblog/make-table-rows-sortable-using-jquery-ui-sortable/
|
||||
// When sorting table rows, the cells collapse. This helper fixes that: http://www.foliotek.com/devblog/make-table-rows-sortable-using-jquery-ui-sortable/
|
||||
ui.children().each(function () {
|
||||
$(this).width($(this).width());
|
||||
});
|
||||
return ui;
|
||||
},
|
||||
items: '> tr',
|
||||
items: '> tr:not(.unsortable)',
|
||||
tolerance: 'pointer',
|
||||
update: function (e, ui) {
|
||||
|
||||
// Get the new and old index for the moved element (using the URL as the identifier)
|
||||
var newIndex = ui.item.index();
|
||||
var movedLinkUrl = ui.item.attr('data-link');
|
||||
var originalIndex = getElementIndexByUrl(movedLinkUrl);
|
||||
|
||||
// Move the element in the model
|
||||
var movedElement = $scope.model.value[originalIndex];
|
||||
$scope.model.value.splice(originalIndex, 1);
|
||||
$scope.model.value.splice(newIndex, 0, movedElement);
|
||||
},
|
||||
start: function (e, ui) {
|
||||
//ui.placeholder.html("<td colspan='5'></td>");
|
||||
|
||||
// Build a placeholder cell that spans all the cells in the row: http://stackoverflow.com/questions/25845310/jquery-ui-sortable-and-table-cell-size
|
||||
var cellCount = 0;
|
||||
$('td, th', ui.helper).each(function () {
|
||||
// For each td or th try and get it's colspan attribute, and add that or 1 to the total
|
||||
var colspan = 1;
|
||||
var colspanAttr = $(this).attr('colspan');
|
||||
if (colspanAttr > 1) {
|
||||
colspan = colspanAttr;
|
||||
}
|
||||
cellCount += colspan;
|
||||
});
|
||||
|
||||
// Add the placeholder UI - note that this is the item's content, so td rather than tr - and set height of tr
|
||||
ui.placeholder.html('<td colspan="' + cellCount + '"></td>').height(ui.item.height());
|
||||
}
|
||||
};
|
||||
|
||||
//helper to count what is visible
|
||||
function countVisible() {
|
||||
return $scope.model.value.length;
|
||||
}
|
||||
|
||||
function isNumeric(n) {
|
||||
return !isNaN(parseFloat(n)) && isFinite(n);
|
||||
}
|
||||
|
||||
function getElementIndexByUrl(url) {
|
||||
for (var i = 0; i < $scope.model.value.length; i++) {
|
||||
if ($scope.model.value[i].link == url) {
|
||||
@@ -172,10 +225,12 @@
|
||||
if ($scope.currentEditLink != null) {
|
||||
$scope.currentEditLink.internal = data.id;
|
||||
$scope.currentEditLink.internalName = data.name;
|
||||
$scope.currentEditLink.internalIcon = iconHelper.convertFromLegacyIcon(data.icon);
|
||||
$scope.currentEditLink.link = data.id;
|
||||
} else {
|
||||
$scope.newInternal = data.id;
|
||||
$scope.newInternalName = data.name;
|
||||
$scope.newInternalIcon = iconHelper.convertFromLegacyIcon(data.icon);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -11,68 +11,79 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody ui-sortable="sortableOptions" ng-model="model.value">
|
||||
<tr ng-repeat="link in model.value" data-link="{{link.link}}">
|
||||
<td style="width: 20px"><i class="icon icon-navigation handle"></i></td>
|
||||
<td style="word-wrap:break-word; word-break: break-all">
|
||||
<tr ng-repeat="link in model.value" data-link="{{link.link}}" ng-class="{ 'unsortable': model.value.length <= 1 }">
|
||||
<td class="col-sort"><i class="icon icon-navigation handle" ng-show="canSort()"></i></td>
|
||||
<td class="col-caption">
|
||||
<span ng-show="!link.edit">{{link.caption}}</span>
|
||||
<input type="text" ng-model="link.caption" ng-show="link.edit" />
|
||||
<div class="control-wrapper">
|
||||
<input type="text" ng-model="link.caption" ng-show="link.edit" />
|
||||
</div>
|
||||
</td>
|
||||
<td style="word-wrap:break-word; word-break: break-all">
|
||||
<td class="col-link">
|
||||
<div ng-show="!link.edit">
|
||||
<i class="icon {{link.internalIcon}}" ng-show="link.internalIcon"></i>
|
||||
<a href="{{link.link}}" target="_blank" ng-show="!link.isInternal">{{link.link}}</a>
|
||||
<a href="#/content/content/edit/{{link.internal}}" target="_blank" ng-show="link.isInternal" ng-bind="link.internalName"></a>
|
||||
</div>
|
||||
<div ng-show="link.edit">
|
||||
<div ng-show="!link.isInternal">
|
||||
<input type="text" ng-model="link.link" /><br />
|
||||
<localize key="or">or</localize> <a href="#" ng-click="switchLinkType($event,link)"><localize key="relatedlinks_chooseInternal">choose internal page</localize></a>
|
||||
</div>
|
||||
<div class="control-wrapper">
|
||||
<input type="text" ng-model="link.link" />
|
||||
</div>
|
||||
<localize key="or">or</localize> <a href="#" ng-click="switchLinkType($event,link)"><localize key="relatedlinks_chooseInternal">choose internal page</localize></a>
|
||||
</div>
|
||||
|
||||
<div ng-show="link.isInternal">
|
||||
<i class="icon {{link.internalIcon}}" ng-show="link.internalIcon"></i>
|
||||
<span ng-bind="link.internalName"></span> <a href="#" ng-click="selectInternal($event,link)"><localize key="choose">Choose</localize></a><br />
|
||||
<localize key="or">or</localize> <a href="#" ng-click="switchLinkType($event,link)"><localize key="relatedlinks_enterExternal">enter external link</localize></a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<td class="col-newwindow">
|
||||
<span ng-show="!link.edit">{{link.newWindow}}</span>
|
||||
<input type="checkbox" ng-model="link.newWindow" ng-show="link.edit" />
|
||||
</td>
|
||||
<td>
|
||||
|
||||
<td class="col-actions">
|
||||
<div class="btn-group" ng-show="!link.edit">
|
||||
<button type="button" class="btn btn-default" ng-click="edit($index)"><localize key="edit">Edit</localize></button>
|
||||
<button type="button" class="btn btn-default" ng-click="delete($index)"><localize key="delete">Delete</localize></button>
|
||||
</div>
|
||||
<div class="btn-group" ng-show="link.edit">
|
||||
<div class="btn-group" ng-show="link.edit" style="margin-left: 0;">
|
||||
<button type="button" class="btn btn-default" ng-click="saveEdit($index)"><localize key="buttons_save">Save</localize></button>
|
||||
</div>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tfoot ng-show="canAdd()">
|
||||
<tr>
|
||||
<td></td>
|
||||
<td><input type="text" ng-model="newCaption" localize="placeholder" placeholder="@relatedlinks_captionPlaceholder" val-highlight="{{hasError}}" /></td>
|
||||
<td>
|
||||
<div ng-show="addExternal">
|
||||
<td class="col-sort"></td>
|
||||
<td class="col-caption">
|
||||
<div class="control-wrapper">
|
||||
<input type="text" ng-model="newCaption" localize="placeholder" placeholder="@relatedlinks_captionPlaceholder" val-highlight="hasError" />
|
||||
</div>
|
||||
</td>
|
||||
<td class="col-link">
|
||||
<div ng-show="addExternal">
|
||||
<div class="control-wrapper">
|
||||
<input type="text" ng-model="newLink" localize="placeholder" placeholder="@relatedlinks_externalLinkPlaceholder" />
|
||||
<br /> <localize key="or">or</localize>
|
||||
<a href="#" ng-click="switch($event)"><localize key="relatedlinks_chooseInternal">choose internal page</localize></a>
|
||||
</div>
|
||||
<localize key="or">or</localize>
|
||||
<a href="#" ng-click="switch($event)"><localize key="relatedlinks_chooseInternal">choose internal page</localize></a>
|
||||
</div>
|
||||
|
||||
<div ng-show="!addExternal">
|
||||
<span ng-bind="newInternalName"></span> <a href="#" ng-click="internal($event)"><localize key="choose">Choose</localize></a><br />
|
||||
<localize key="or">or</localize> <a href="#" ng-click="switch($event)"><localize key="relatedlinks_enterExternal">enter external link</localize></a>
|
||||
</div>
|
||||
</td>
|
||||
<td><input type="checkbox" ng-model="newNewWindow" /> </td>
|
||||
<td>
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-default" ng-click="add($event)"><localize key="general_add">Add</localize></button>
|
||||
</div>
|
||||
</td>
|
||||
<div ng-show="!addExternal">
|
||||
<i class="icon {{newInternalIcon}}" ng-show="newInternalIcon"></i>
|
||||
<span ng-bind="newInternalName"></span> <a href="#" ng-click="internal($event)"><localize key="choose">Choose</localize></a><br />
|
||||
<localize key="or">or</localize> <a href="#" ng-click="switch($event)"><localize key="relatedlinks_enterExternal">enter external link</localize></a>
|
||||
</div>
|
||||
</td>
|
||||
<td class="col-newwindow"><input type="checkbox" ng-model="newNewWindow" /></td>
|
||||
<td class="col-actions">
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-default" ng-click="add($event)" ng-disabled="newCaption == '' || !canAdd()" ng-show="canAdd()"><localize key="general_add">Add</localize></button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
@@ -84,4 +95,4 @@
|
||||
position="right">
|
||||
</umb-overlay>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@@ -11,5 +11,15 @@ namespace Umbraco.Web.PropertyEditors
|
||||
[PropertyEditor(Constants.PropertyEditors.RelatedLinksAlias, "Related links", "relatedlinks", ValueType ="JSON", Icon="icon-thumbnail-list", Group="pickers")]
|
||||
public class RelatedLinksPropertyEditor : PropertyEditor
|
||||
{
|
||||
protected override PreValueEditor CreatePreValueEditor()
|
||||
{
|
||||
return new RelatedLinksPreValueEditor();
|
||||
}
|
||||
|
||||
internal class RelatedLinksPreValueEditor : PreValueEditor
|
||||
{
|
||||
[PreValueField("max", "Maximum number of links", "number", Description = "Enter the maximum amount of links to be added, enter 0 for unlimited")]
|
||||
public int Maximum { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user