Merge pull request #2775 from bjarnef/dev-v7-U4-9025-2
U4-9025 - Add sortable to color picker prevalues
This commit is contained in:
@@ -64,7 +64,7 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
function ToggleDirective(localizationService) {
|
||||
function ToggleDirective(localizationService, eventsService) {
|
||||
|
||||
function link(scope, el, attr, ctrl) {
|
||||
|
||||
@@ -73,6 +73,7 @@
|
||||
|
||||
function onInit() {
|
||||
setLabelText();
|
||||
eventsService.emit("toggleValue", { value: scope.checked });
|
||||
}
|
||||
|
||||
function setLabelText() {
|
||||
@@ -98,7 +99,8 @@
|
||||
}
|
||||
|
||||
scope.click = function() {
|
||||
if(scope.onClick) {
|
||||
if (scope.onClick) {
|
||||
eventsService.emit("toggleValue", { value: !scope.checked });
|
||||
scope.onClick();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -148,50 +148,67 @@ ul.color-picker li a {
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
.control-group.color-picker-preval .thumbnail {
|
||||
width:30px;
|
||||
border:none;
|
||||
}
|
||||
|
||||
|
||||
/* pre-value editor */
|
||||
/*.control-group.color-picker-preval:before {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
height: 100%;
|
||||
}*/
|
||||
.control-group.color-picker-preval {
|
||||
.thumbnail {
|
||||
width: 36px;
|
||||
border: none;
|
||||
cursor: move;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
/*.control-group.color-picker-preval div.thumbnail {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}*/
|
||||
.control-group.color-picker-preval div.color-picker-prediv {
|
||||
display: inline-block;
|
||||
width: 60%;
|
||||
}
|
||||
.handle {
|
||||
float: left;
|
||||
display: inline-flex;
|
||||
margin: 5px 3px 5px 0;
|
||||
}
|
||||
|
||||
.control-group.color-picker-preval pre {
|
||||
display: inline;
|
||||
margin-right: 20px;
|
||||
margin-left: 10px;
|
||||
width: 50%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
margin-bottom: 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
div.color-picker-prediv {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
||||
.control-group.color-picker-preval input[type="text"] {
|
||||
min-width: 40%;
|
||||
width: 40%;
|
||||
display: inline-block;
|
||||
margin-right: 20px;
|
||||
margin-top: 1px;
|
||||
}
|
||||
pre {
|
||||
display: inline;
|
||||
font-family: monospace;
|
||||
margin-right: 10px;
|
||||
margin-left: 10px;
|
||||
width: 50%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
margin-bottom: 0;
|
||||
vertical-align: middle;
|
||||
padding-top: 7px;
|
||||
padding-bottom: 7px;
|
||||
background: #f7f7f7;
|
||||
}
|
||||
|
||||
.control-group.color-picker-preval label {
|
||||
border: solid @white 1px;
|
||||
padding: 6px;
|
||||
span {
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
min-width: 40%;
|
||||
width: 320px;
|
||||
display: inline-block;
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.sp-replacer {
|
||||
margin-right: 18px;
|
||||
}
|
||||
|
||||
label {
|
||||
border: 1px solid #fff;
|
||||
padding: 7px 10px;
|
||||
font-family: monospace;
|
||||
border: 1px solid #dfdfe1;
|
||||
background: #f7f7f7;
|
||||
margin: 0 15px 0 0;
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -238,15 +255,6 @@ ul.color-picker li a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.umb-mediapicker .label.trashed {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 5px;
|
||||
color: #fff;
|
||||
background-color: #fe6561;
|
||||
background-image: linear-gradient(180deg,#fe6561,#fe6561);
|
||||
}
|
||||
|
||||
.umb-mediapicker .add-link-square {
|
||||
height: 120px;
|
||||
}
|
||||
@@ -518,7 +526,6 @@ ul.color-picker li a {
|
||||
|
||||
.gravity-container .viewport {
|
||||
max-width: 600px;
|
||||
min-width: 250px;
|
||||
}
|
||||
|
||||
.gravity-container .viewport:hover {
|
||||
@@ -810,38 +817,28 @@ ul.color-picker li a {
|
||||
padding-top: 27px;
|
||||
}
|
||||
|
||||
.umb-fileupload .file-icon-wrap {
|
||||
.umb-fileupload .file-icon {
|
||||
text-align: center;
|
||||
display: block;
|
||||
position: relative;
|
||||
padding: 5px 0;
|
||||
|
||||
/* fit text within container */
|
||||
& + span {
|
||||
word-wrap: break-word;
|
||||
> .icon {
|
||||
font-size: 70px;
|
||||
line-height: 110%;
|
||||
color: @gray-4;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.file-icon {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
|
||||
> .icon {
|
||||
font-size: 70px;
|
||||
line-height: 110%;
|
||||
color: @gray-4;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
> span {
|
||||
> span {
|
||||
color: @white;
|
||||
background: @gray-4;
|
||||
padding: 1px 3px;
|
||||
font-size: 12px;
|
||||
line-height: 130%;
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
left: 35px;
|
||||
}
|
||||
padding: 1px 3px;
|
||||
font-size: 12px;
|
||||
line-height: 130%;
|
||||
position: absolute;
|
||||
top: 45px;
|
||||
left: 110px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -901,19 +898,6 @@ ul.color-picker li a {
|
||||
border-bottom: 1px solid @tableBorder;
|
||||
}
|
||||
|
||||
.umb-multiple-textbox .textbox-wrapper { align-items: center; margin-bottom: 15px; }
|
||||
.umb-multiple-textbox .textbox-wrapper .umb-editor { margin-bottom: 0; }
|
||||
.umb-multiple-textbox .textbox-wrapper i { margin-right: 5px; }
|
||||
.umb-multiple-textbox .textbox-wrapper i.handle { margin-left: 5px; }
|
||||
.umb-multiple-textbox .textbox-wrapper a.remove { margin-left: 5px; text-decoration: none; }
|
||||
.umb-multiple-textbox .add-link {
|
||||
&:extend(.umb-node-preview-add);
|
||||
}
|
||||
.umb-editor-wrapper .umb-multiple-textbox .add-link {
|
||||
&:extend(.umb-editor-wrapper .umb-node-preview);
|
||||
}
|
||||
.umb-modal .umb-multiple-textbox .textbox-wrapper .umb-editor { flex: 1 1 auto; width: auto; }
|
||||
|
||||
|
||||
//
|
||||
// Tags
|
||||
@@ -943,21 +927,7 @@ ul.color-picker li a {
|
||||
|
||||
//
|
||||
// Nested boolean (e.g. list view bulk action permissions)
|
||||
// -------------------------------------------------------
|
||||
// ---------------------=====-----------------------------
|
||||
.umb-nested-boolean label {margin-bottom: 8px; float: left; width: 320px;}
|
||||
.umb-nested-boolean label span {float: left; width: 80%;}
|
||||
.umb-nested-boolean label input[type='checkbox'] {margin-right: 10px; float: left;}
|
||||
|
||||
|
||||
//
|
||||
// Custom styles of property editors in property preview in document type editor
|
||||
// -----------------------------------------------------------------------------
|
||||
.umb-group-builder__property-preview {
|
||||
.umb-property-editor {
|
||||
.slider {
|
||||
.tooltip {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<a href="" ng-click="click()" class="umb-toggle" ng-class="{'umb-toggle--checked': checked}">
|
||||
<a href="" ng-click="click()" class="umb-toggle dib" ng-class="{'umb-toggle--checked': checked}">
|
||||
|
||||
<span ng-if="!labelPosition && showLabels === 'true' || labelPosition === 'left' && showLabels === 'true'">
|
||||
<span ng-if="!checked" class="umb-toggle__label umb-toggle__label--left">{{ displayLabelOff }}</span>
|
||||
@@ -16,4 +16,4 @@
|
||||
<span ng-if="checked" class="umb-toggle__label umb-toggle__label--right">{{ displayLabelOn }}</span>
|
||||
</span>
|
||||
|
||||
</a>
|
||||
</a>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<input overlay-submit-on-enter="false" name="newItem" focus-when="{{focusOnNew}}" ng-keydown="createNew($event)" type="text" ng-model="newItem" val-highlight="{{hasError}}" />
|
||||
</div>
|
||||
<div class="umb-prevalues-multivalues__right">
|
||||
<button class="btn btn-info" ng-click="add($event)">Add</button>
|
||||
<button class="btn btn-info" ng-click="add($event)"><localize key="general_add">Add</localize></button>
|
||||
</div>
|
||||
</div>
|
||||
<div ui-sortable="sortableOptions">
|
||||
@@ -14,7 +14,7 @@
|
||||
<input type="text" ng-model="item.value" val-server="item_{{$index}}" required />
|
||||
</div>
|
||||
<div class="umb-prevalues-multivalues__right">
|
||||
<a class="umb-node-preview__action" ng-click="remove(item, $event)">Remove</a>
|
||||
<a class="umb-node-preview__action umb-node-preview__action--red" ng-click="remove(item, $event)"><localize key="general_remove">Remove</localize></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,46 +1,111 @@
|
||||
function ColorPickerController($scope) {
|
||||
|
||||
$scope.isConfigured = $scope.model.config && $scope.model.config.items && _.keys($scope.model.config.items).length > 0;
|
||||
|
||||
if ($scope.isConfigured) {
|
||||
|
||||
for (var key in $scope.model.config.items) {
|
||||
if (!$scope.model.config.items[key].hasOwnProperty("value"))
|
||||
$scope.model.config.items[key] = { value: $scope.model.config.items[key], label: $scope.model.config.items[key] };
|
||||
}
|
||||
|
||||
$scope.model.useLabel = isTrue($scope.model.config.useLabel);
|
||||
initActiveColor();
|
||||
}
|
||||
function ColorPickerController($scope) {
|
||||
|
||||
//setup the default config
|
||||
var config = {
|
||||
items: [],
|
||||
multiple: false
|
||||
};
|
||||
|
||||
//map the user config
|
||||
angular.extend(config, $scope.model.config);
|
||||
|
||||
//map back to the model
|
||||
$scope.model.config = config;
|
||||
|
||||
function convertArrayToDictionaryArray(model) {
|
||||
//now we need to format the items in the dictionary because we always want to have an array
|
||||
var newItems = [];
|
||||
for (var i = 0; i < model.length; i++) {
|
||||
newItems.push({ id: model[i], sortOrder: 0, value: model[i] });
|
||||
}
|
||||
|
||||
return newItems;
|
||||
}
|
||||
|
||||
|
||||
function convertObjectToDictionaryArray(model) {
|
||||
//now we need to format the items in the dictionary because we always want to have an array
|
||||
var newItems = [];
|
||||
var vals = _.values($scope.model.config.items);
|
||||
var keys = _.keys($scope.model.config.items);
|
||||
|
||||
for (var i = 0; i < vals.length; i++) {
|
||||
var label = vals[i].value ? vals[i].value : vals[i];
|
||||
newItems.push({ id: keys[i], sortOrder: vals[i].sortOrder, value: label });
|
||||
}
|
||||
|
||||
return newItems;
|
||||
}
|
||||
$scope.isConfigured = $scope.model.config && $scope.model.config.items && _.keys($scope.model.config.items).length > 0;
|
||||
|
||||
if ($scope.isConfigured) {
|
||||
|
||||
for (var key in $scope.model.config.items) {
|
||||
if (!$scope.model.config.items[key].hasOwnProperty("value"))
|
||||
$scope.model.config.items[key] = { value: $scope.model.config.items[key], label: $scope.model.config.items[key] };
|
||||
}
|
||||
|
||||
$scope.model.useLabel = isTrue($scope.model.config.useLabel);
|
||||
initActiveColor();
|
||||
}
|
||||
|
||||
if (!angular.isArray($scope.model.config.items)) {
|
||||
//make an array from the dictionary
|
||||
var items = [];
|
||||
for (var i in $scope.model.config.items) {
|
||||
var oldValue = $scope.model.config.items[i];
|
||||
if (oldValue.hasOwnProperty("value")) {
|
||||
items.push({
|
||||
value: oldValue.value,
|
||||
label: oldValue.label,
|
||||
sortOrder: oldValue.sortOrder,
|
||||
id: i
|
||||
});
|
||||
} else {
|
||||
items.push({
|
||||
value: oldValue,
|
||||
label: oldValue,
|
||||
sortOrder: sortOrder,
|
||||
id: i
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//ensure the items are sorted by the provided sort order
|
||||
items.sort(function (a, b) { return (a.sortOrder > b.sortOrder) ? 1 : ((b.sortOrder > a.sortOrder) ? -1 : 0); });
|
||||
|
||||
//now make the editor model the array
|
||||
$scope.model.config.items = items;
|
||||
}
|
||||
|
||||
$scope.toggleItem = function (color) {
|
||||
|
||||
var currentColor = ($scope.model.value && $scope.model.value.hasOwnProperty("value"))
|
||||
? $scope.model.value.value
|
||||
: $scope.model.value;
|
||||
|
||||
$scope.toggleItem = function (color) {
|
||||
|
||||
var currentColor = ($scope.model.value && $scope.model.value.hasOwnProperty("value"))
|
||||
? $scope.model.value.value
|
||||
: $scope.model.value;
|
||||
|
||||
var newColor;
|
||||
if (currentColor === color.value) {
|
||||
// deselect
|
||||
$scope.model.value = $scope.model.useLabel ? { value: "", label: "" } : "";
|
||||
newColor = "";
|
||||
}
|
||||
else {
|
||||
if (currentColor === color.value) {
|
||||
// deselect
|
||||
$scope.model.value = $scope.model.useLabel ? { value: "", label: "" } : "";
|
||||
newColor = "";
|
||||
}
|
||||
else {
|
||||
// select
|
||||
$scope.model.value = $scope.model.useLabel ? { value: color.value, label: color.label } : color.value;
|
||||
$scope.model.value = $scope.model.useLabel ? { value: color.value, label: color.label } : color.value;
|
||||
newColor = color.value;
|
||||
}
|
||||
|
||||
// this is required to re-validate
|
||||
$scope.propertyForm.modelValue.$setViewValue(newColor);
|
||||
};
|
||||
}
|
||||
|
||||
// this is required to re-validate
|
||||
$scope.propertyForm.modelValue.$setViewValue(newColor);
|
||||
};
|
||||
|
||||
// Method required by the valPropertyValidator directive (returns true if the property editor has at least one color selected)
|
||||
$scope.validateMandatory = function () {
|
||||
var isValid = !$scope.model.validation.mandatory || (
|
||||
$scope.model.value != null
|
||||
&& $scope.model.value != ""
|
||||
&& (!$scope.model.value.hasOwnProperty("value") || $scope.model.value.value !== "")
|
||||
$scope.validateMandatory = function () {
|
||||
var isValid = !$scope.model.validation.mandatory || (
|
||||
$scope.model.value != null
|
||||
&& $scope.model.value != ""
|
||||
&& (!$scope.model.value.hasOwnProperty("value") || $scope.model.value.value !== "")
|
||||
);
|
||||
return {
|
||||
isValid: isValid,
|
||||
@@ -48,83 +113,83 @@ function ColorPickerController($scope) {
|
||||
errorKey: "required"
|
||||
};
|
||||
}
|
||||
$scope.isConfigured = $scope.model.config && $scope.model.config.items && _.keys($scope.model.config.items).length > 0;
|
||||
|
||||
// A color is active if it matches the value and label of the model.
|
||||
// If the model doesn't store the label, ignore the label during the comparison.
|
||||
$scope.isActiveColor = function (color) {
|
||||
|
||||
// no value
|
||||
if (!$scope.model.value)
|
||||
return false;
|
||||
|
||||
// Complex color (value and label)?
|
||||
if (!$scope.model.value.hasOwnProperty("value"))
|
||||
return $scope.model.value === color.value;
|
||||
|
||||
return $scope.model.value.value === color.value && $scope.model.value.label === color.label;
|
||||
};
|
||||
|
||||
// Finds the color best matching the model's color,
|
||||
// and sets the model color to that one. This is useful when
|
||||
// either the value or label was changed on the data type.
|
||||
function initActiveColor() {
|
||||
|
||||
// no value
|
||||
if (!$scope.model.value)
|
||||
return;
|
||||
|
||||
// Complex color (value and label)?
|
||||
if (!$scope.model.value.hasOwnProperty("value"))
|
||||
return;
|
||||
|
||||
var modelColor = $scope.model.value.value;
|
||||
var modelLabel = $scope.model.value.label;
|
||||
|
||||
// Check for a full match or partial match.
|
||||
var foundItem = null;
|
||||
|
||||
// Look for a fully matching color.
|
||||
for (var key in $scope.model.config.items) {
|
||||
var item = $scope.model.config.items[key];
|
||||
if (item.value == modelColor && item.label == modelLabel) {
|
||||
foundItem = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Look for a color with a matching value.
|
||||
if (!foundItem) {
|
||||
for (var key in $scope.model.config.items) {
|
||||
var item = $scope.model.config.items[key];
|
||||
if (item.value == modelColor) {
|
||||
foundItem = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Look for a color with a matching label.
|
||||
if (!foundItem) {
|
||||
for (var key in $scope.model.config.items) {
|
||||
var item = $scope.model.config.items[key];
|
||||
if (item.label == modelLabel) {
|
||||
foundItem = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If a match was found, set it as the active color.
|
||||
if (foundItem) {
|
||||
$scope.model.value.value = foundItem.value;
|
||||
$scope.model.value.label = foundItem.label;
|
||||
}
|
||||
}
|
||||
|
||||
// figures out if a value is trueish enough
|
||||
function isTrue(bool) {
|
||||
return !!bool && bool !== "0" && angular.lowercase(bool) !== "false";
|
||||
$scope.isConfigured = $scope.model.config && $scope.model.config.items && _.keys($scope.model.config.items).length > 0;
|
||||
|
||||
// A color is active if it matches the value and label of the model.
|
||||
// If the model doesn't store the label, ignore the label during the comparison.
|
||||
$scope.isActiveColor = function (color) {
|
||||
|
||||
// no value
|
||||
if (!$scope.model.value)
|
||||
return false;
|
||||
|
||||
// Complex color (value and label)?
|
||||
if (!$scope.model.value.hasOwnProperty("value"))
|
||||
return $scope.model.value === color.value;
|
||||
|
||||
return $scope.model.value.value === color.value && $scope.model.value.label === color.label;
|
||||
};
|
||||
|
||||
// Finds the color best matching the model's color,
|
||||
// and sets the model color to that one. This is useful when
|
||||
// either the value or label was changed on the data type.
|
||||
function initActiveColor() {
|
||||
|
||||
// no value
|
||||
if (!$scope.model.value)
|
||||
return;
|
||||
|
||||
// Complex color (value and label)?
|
||||
if (!$scope.model.value.hasOwnProperty("value"))
|
||||
return;
|
||||
|
||||
var modelColor = $scope.model.value.value;
|
||||
var modelLabel = $scope.model.value.label;
|
||||
|
||||
// Check for a full match or partial match.
|
||||
var foundItem = null;
|
||||
|
||||
// Look for a fully matching color.
|
||||
for (var key in $scope.model.config.items) {
|
||||
var item = $scope.model.config.items[key];
|
||||
if (item.value == modelColor && item.label == modelLabel) {
|
||||
foundItem = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Look for a color with a matching value.
|
||||
if (!foundItem) {
|
||||
for (var key in $scope.model.config.items) {
|
||||
var item = $scope.model.config.items[key];
|
||||
if (item.value == modelColor) {
|
||||
foundItem = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Look for a color with a matching label.
|
||||
if (!foundItem) {
|
||||
for (var key in $scope.model.config.items) {
|
||||
var item = $scope.model.config.items[key];
|
||||
if (item.label == modelLabel) {
|
||||
foundItem = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If a match was found, set it as the active color.
|
||||
if (foundItem) {
|
||||
$scope.model.value.value = foundItem.value;
|
||||
$scope.model.value.label = foundItem.label;
|
||||
}
|
||||
}
|
||||
|
||||
// figures out if a value is trueish enough
|
||||
function isTrue(bool) {
|
||||
return !!bool && bool !== "0" && angular.lowercase(bool) !== "false";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,24 @@
|
||||
<div ng-controller="Umbraco.PrevalueEditors.MultiColorPickerController">
|
||||
<div class="control-group color-picker-preval">
|
||||
<input name="newColor" type="hidden" />
|
||||
<label for="newColor" val-highlight="hasError">#{{newColor}}</label>
|
||||
<input name="newLabel" type="text" ng-model="newLabel" class="umb-editor color-label" placeholder="Label" />
|
||||
<button class="btn add" ng-click="add($event)"><localize key="general_add">Add</localize></button>
|
||||
<div class="umb-editor umb-prevalues-multivalues" ng-controller="Umbraco.PrevalueEditors.MultiColorPickerController">
|
||||
<div class="control-group umb-prevalues-multivalues__add color-picker-preval">
|
||||
<div class="umb-prevalues-multivalues__left">
|
||||
<input name="newColor" type="hidden" />
|
||||
<label for="newColor" val-highlight="{{hasError}}">#{{newColor}}</label>
|
||||
<input name="newLabel" type="text" ng-model="newLabel" class="umb-editor color-label" placeholder="Label" ng-show="labelEnabled" />
|
||||
</div>
|
||||
<div class="umb-prevalues-multivalues__right">
|
||||
<button class="btn btn-info add" ng-click="add($event)"><localize key="general_add">Add</localize></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group color-picker-preval" ng-repeat="item in model.value">
|
||||
<div class="thumbnail span1" hex-bg-color="{{item.value}}" bg-orig="transparent"></div>
|
||||
<div class="color-picker-prediv"><pre>#{{item.value}} - {{item.label}}</pre></div>
|
||||
<button class="btn btn-danger" ng-click="remove(item, $event)"><localize key="general_remove">Remove</localize></button>
|
||||
<div ui-sortable="sortableOptions" ng-model="model.value">
|
||||
<div class="control-group umb-prevalues-multivalues__listitem color-picker-preval" ng-repeat="item in model.value">
|
||||
<i class="icon icon-navigation handle"></i>
|
||||
<div class="umb-prevalues-multivalues__left">
|
||||
<div class="thumbnail span1" hex-bg-color="{{item.value}}" bg-orig="transparent"></div>
|
||||
<div class="color-picker-prediv"><pre>#{{item.value}}</pre><span>{{item.label}}</span></div>
|
||||
</div>
|
||||
<div class="umb-prevalues-multivalues__right">
|
||||
<a class="umb-node-preview__action umb-node-preview__action--red" ng-click="remove(item, $event)"><localize key="general_remove">Remove</localize></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
angular.module("umbraco").controller("Umbraco.PrevalueEditors.MultiColorPickerController",
|
||||
function ($scope, $timeout, assetsService, angularHelper, $element) {
|
||||
function ($scope, $timeout, assetsService, angularHelper, $element, localizationService, eventsService) {
|
||||
//NOTE: We need to make each color an object, not just a string because you cannot 2-way bind to a primitive.
|
||||
var defaultColor = "000000";
|
||||
var defaultLabel = null;
|
||||
@@ -8,6 +8,23 @@
|
||||
$scope.newLavel = defaultLabel;
|
||||
$scope.hasError = false;
|
||||
|
||||
$scope.labels = {};
|
||||
|
||||
var labelKeys = [
|
||||
"general_cancel",
|
||||
"general_choose"
|
||||
];
|
||||
|
||||
$scope.labelEnabled = false;
|
||||
eventsService.on("toggleValue", function (e, args) {
|
||||
$scope.labelEnabled = args.value;
|
||||
});
|
||||
|
||||
localizationService.localizeMany(labelKeys).then(function (values) {
|
||||
$scope.labels.cancel = values[0];
|
||||
$scope.labels.choose = values[1];
|
||||
});
|
||||
|
||||
assetsService.load([
|
||||
//"lib/spectrum/tinycolor.js",
|
||||
"lib/spectrum/spectrum.js"
|
||||
@@ -16,8 +33,8 @@
|
||||
elem.spectrum({
|
||||
color: null,
|
||||
showInitial: false,
|
||||
chooseText: "choose", // TODO: These can be localised
|
||||
cancelText: "cancel", // TODO: These can be localised
|
||||
chooseText: $scope.labels.choose,
|
||||
cancelText: $scope.labels.cancel,
|
||||
preferredFormat: "hex",
|
||||
showInput: true,
|
||||
clickoutFiresChange: true,
|
||||
@@ -46,16 +63,22 @@
|
||||
items.push({
|
||||
value: oldValue.value,
|
||||
label: oldValue.label,
|
||||
sortOrder: oldValue.sortOrder,
|
||||
id: i
|
||||
});
|
||||
} else {
|
||||
items.push({
|
||||
value: oldValue,
|
||||
label: oldValue,
|
||||
sortOrder: sortOrder,
|
||||
id: i
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//ensure the items are sorted by the provided sort order
|
||||
items.sort(function (a, b) { return (a.sortOrder > b.sortOrder) ? 1 : ((b.sortOrder > a.sortOrder) ? -1 : 0); });
|
||||
|
||||
//now make the editor model the array
|
||||
$scope.model.value = items;
|
||||
}
|
||||
@@ -104,6 +127,39 @@
|
||||
|
||||
};
|
||||
|
||||
$scope.sortableOptions = {
|
||||
axis: 'y',
|
||||
containment: 'parent',
|
||||
cursor: 'move',
|
||||
//handle: ".handle, .thumbnail",
|
||||
items: '> div.control-group',
|
||||
tolerance: 'pointer',
|
||||
update: function (e, ui) {
|
||||
// Get the new and old index for the moved element (using the text as the identifier, so
|
||||
// we'd have a problem if two prevalues were the same, but that would be unlikely)
|
||||
var newIndex = ui.item.index();
|
||||
var movedPrevalueText = $('pre', ui.item).text();
|
||||
var originalIndex = getElementIndexByPrevalueText(movedPrevalueText);
|
||||
|
||||
//// Move the element in the model
|
||||
if (originalIndex > -1) {
|
||||
var movedElement = $scope.model.value[originalIndex];
|
||||
$scope.model.value.splice(originalIndex, 1);
|
||||
$scope.model.value.splice(newIndex, 0, movedElement);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function getElementIndexByPrevalueText(value) {
|
||||
for (var i = 0; i < $scope.model.value.length; i++) {
|
||||
if ($scope.model.value[i].value === value) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
//load the separate css for the editor to avoid it blocking our js loading
|
||||
assetsService.loadCss("lib/spectrum/spectrum.css", $scope);
|
||||
});
|
||||
|
||||
@@ -22,17 +22,17 @@ namespace Umbraco.Web.PropertyEditors
|
||||
//use a custom editor too
|
||||
field.View = "views/propertyeditors/colorpicker/colorpicker.prevalues.html";
|
||||
//change the description
|
||||
field.Description = "Add and remove colors";
|
||||
field.Description = "Add, remove or sort colors.";
|
||||
//change the label
|
||||
field.Name = "Add color";
|
||||
field.Name = "Colors";
|
||||
//need to have some custom validation happening here
|
||||
field.Validators.Add(new ColorListValidator());
|
||||
|
||||
field.Validators.Add(new ColorListValidator());
|
||||
|
||||
Fields.Insert(0, new PreValueField
|
||||
{
|
||||
Name = "Include labels?",
|
||||
View = "boolean",
|
||||
Key = "useLabel",
|
||||
Name = "Include labels?",
|
||||
View = "boolean",
|
||||
Key = "useLabel",
|
||||
Description = "Stores colors as a Json object containing both the color hex string and label, rather than just the hex string."
|
||||
});
|
||||
}
|
||||
@@ -40,28 +40,44 @@ namespace Umbraco.Web.PropertyEditors
|
||||
public override IDictionary<string, object> ConvertDbToEditor(IDictionary<string, object> defaultPreVals, PreValueCollection persistedPreVals)
|
||||
{
|
||||
var dictionary = persistedPreVals.FormatAsDictionary();
|
||||
var items = dictionary
|
||||
.Where(x => x.Key != "useLabel")
|
||||
.ToDictionary(x => x.Value.Id, x => x.Value.Value);
|
||||
var items = dictionary.Where(x => x.Key != "useLabel")
|
||||
.OrderBy(x => x.Value.SortOrder);
|
||||
|
||||
var items2 = new Dictionary<int, object>();
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (item.Value.DetectIsJson() == false)
|
||||
var valueItem = new ColorPickerColor
|
||||
{
|
||||
items2[item.Key] = item.Value;
|
||||
continue;
|
||||
}
|
||||
Color = item.Value.Value,
|
||||
Label = item.Value.Value,
|
||||
SortOrder = item.Value.SortOrder
|
||||
};
|
||||
|
||||
try
|
||||
if (item.Value.Value.DetectIsJson())
|
||||
{
|
||||
items2[item.Key] = JsonConvert.DeserializeObject(item.Value);
|
||||
try
|
||||
{
|
||||
var valueObject = JsonConvert.DeserializeObject<ColorPickerColor>(item.Value.Value);
|
||||
valueItem = new ColorPickerColor
|
||||
{
|
||||
Color = valueObject.Color,
|
||||
Label = valueObject.Label,
|
||||
SortOrder = valueObject.SortOrder
|
||||
};
|
||||
}
|
||||
catch
|
||||
{
|
||||
// let's say parsing Json failed, we'll not do anything,
|
||||
// we'll just use the valueItem we built in the first place
|
||||
}
|
||||
}
|
||||
catch
|
||||
|
||||
items2[item.Value.Id] = new JObject
|
||||
{
|
||||
// let's say parsing Json failed, so what we have is the string - build json
|
||||
items2[item.Key] = new JObject { { "color", item.Value }, { "label", item.Value } };
|
||||
}
|
||||
{ "value", valueItem.Color },
|
||||
{ "label", valueItem.Label },
|
||||
{ "sortOrder", valueItem.SortOrder }
|
||||
};
|
||||
}
|
||||
|
||||
var result = new Dictionary<string, object> { { "items", items2 } };
|
||||
@@ -80,9 +96,8 @@ namespace Umbraco.Web.PropertyEditors
|
||||
|
||||
try
|
||||
{
|
||||
object useLabelObj;
|
||||
var useLabel = false;
|
||||
if (editorValue.TryGetValue("useLabel", out useLabelObj))
|
||||
if (editorValue.TryGetValue("useLabel", out var useLabelObj))
|
||||
{
|
||||
useLabel = useLabelObj is string && (string) useLabelObj == "1";
|
||||
result["useLabel"] = new PreValue(useLabel ? "1" : "0");
|
||||
@@ -90,21 +105,26 @@ namespace Umbraco.Web.PropertyEditors
|
||||
|
||||
// get all non-empty values
|
||||
var index = 0;
|
||||
// items get submitted in the sorted order, so just count them up
|
||||
var sortOrder = -1;
|
||||
foreach (var preValue in val.OfType<JObject>()
|
||||
.Where(x => x["value"] != null)
|
||||
.Select(x =>
|
||||
{
|
||||
var idString = x["id"] == null ? "0" : x["id"].ToString();
|
||||
int id;
|
||||
if (int.TryParse(idString, out id) == false) id = 0;
|
||||
int.TryParse(idString, out var id);
|
||||
|
||||
var color = x["value"].ToString();
|
||||
if (string.IsNullOrWhiteSpace(color)) return null;
|
||||
|
||||
var label = x["label"].ToString();
|
||||
return new PreValue(id, useLabel
|
||||
? JsonConvert.SerializeObject(new { value = color, label = label })
|
||||
: color);
|
||||
|
||||
sortOrder++;
|
||||
var value = useLabel
|
||||
? JsonConvert.SerializeObject(new { value = color, label = label, sortOrder = sortOrder })
|
||||
: color;
|
||||
|
||||
return new PreValue(id, value, sortOrder);
|
||||
})
|
||||
.WhereNotNull())
|
||||
{
|
||||
@@ -150,4 +170,14 @@ namespace Umbraco.Web.PropertyEditors
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class ColorPickerColor
|
||||
{
|
||||
[JsonProperty("value")]
|
||||
public string Color { get; set; }
|
||||
[JsonProperty("label")]
|
||||
public string Label { get; set; }
|
||||
[JsonProperty("sortOrder")]
|
||||
public int SortOrder { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,14 +2,12 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using umbraco;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Web.PropertyEditors
|
||||
{
|
||||
@@ -37,7 +35,7 @@ namespace Umbraco.Web.PropertyEditors
|
||||
{
|
||||
new PreValueField(new EnsureUniqueValuesValidator())
|
||||
{
|
||||
Description = "Add and remove values for the list",
|
||||
Description = "Add, remove or sort values for the list.",
|
||||
//we're going to call this 'items' because we are going to override the
|
||||
//serialization of the pre-values to ensure that each one gets saved with it's own key
|
||||
//(new db row per pre-value, thus to maintain backwards compatibility)
|
||||
@@ -45,7 +43,7 @@ namespace Umbraco.Web.PropertyEditors
|
||||
//It's also important to note that by default the dropdown angular controller is expecting the
|
||||
// config options to come in with a property called 'items'
|
||||
Key = "items",
|
||||
Name = ui.Text("editdatatype", "addPrevalue"),
|
||||
Name = ApplicationContext.Current.Services.TextService.Localize("editdatatype/addPrevalue"),
|
||||
View = "multivalues"
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user