Fix issue with pressing enter on color picker labels

Prevent form submission when pressing enter in color picker labels, instead execute "add" code.
Improve validation highlights.
This commit is contained in:
Matthew Care
2023-09-09 23:10:48 +02:00
committed by Michael Latouche
parent 0c82b9b700
commit f8b66aaf50
2 changed files with 255 additions and 210 deletions

View File

@@ -1,38 +1,46 @@
<div class="umb-property-editor umb-prevalues-multivalues umb-colors" ng-controller="Umbraco.PrevalueEditors.MultiColorPickerController as vm">
<div class="control-group umb-prevalues-multivalues__add color-picker-preval">
<div class="umb-prevalues-multivalues__left">
<div class="control-group umb-prevalues-multivalues__add color-picker-preval">
<div class="umb-prevalues-multivalues__left">
<umb-color-picker
ng-model="newColor"
options="options"
on-hide="vm.hide(color)"
on-show="vm.show(color)"
on-change="vm.change(color)">
</umb-color-picker>
<umb-color-picker ng-model="newColor"
options="options"
on-hide="vm.hide(color)"
on-show="vm.show(color)"
on-change="vm.change(color)">
</umb-color-picker>
<label val-highlight="{{hasError}}">#{{newColor}}</label>
<input type="text" name="newLabel" ng-model="newLabel" focus-when="{{focusOnNew}}" class="umb-property-editor color-label" localize="placeholder" placeholder="@general_label" ng-show="vm.labelEnabled" />
</div>
<div class="umb-prevalues-multivalues__right">
<button type="button" class="btn btn-info add" ng-if="vm.editItem == null" ng-click="vm.add($event)"><localize key="general_add">Add</localize></button>
<button type="button" class="btn btn-info add" ng-if="vm.editItem != null" ng-click="vm.add($event)"><localize key="general_update">Update</localize></button>
<button type="button" class="btn btn-link" ng-if="vm.editItem != null" ng-click="vm.cancel($event)"><localize key="general_cancel">Cancel</localize></button>
</div>
<label val-highlight="{{colorHasError}}">#{{newColor}}</label>
<input type="text" name="newLabel"
ng-model="newLabel"
focus-when="{{focusOnNew}}"
class="umb-property-editor color-label"
localize="placeholder"
placeholder="@general_label"
ng-show="vm.labelEnabled"
ng-keydown="vm.addOnEnter($event)"
ng-keyup="vm.validateLabel()"
val-highlight="{{labelHasError}}" />
</div>
<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 track by $id(item)">
<umb-icon icon="icon-navigation" class="icon handle"></umb-icon>
<div class="umb-prevalues-multivalues__left">
<div class="thumbnail span1" hex-bg-color="{{item.value}}" hex-bg-orig="transparent"></div>
<div class="color-picker-prediv">
<pre>#{{item.value}}</pre>
<span ng-bind="item.label" ng-if="vm.labelEnabled"></span>
</div>
</div>
<div class="umb-prevalues-multivalues__right">
<button type="button" class="umb-node-preview__action umb-node-preview__action--red" ng-click="vm.edit(item, $event)"><localize key="general_edit">Edit</localize></button>
<button type="button" class="umb-node-preview__action umb-node-preview__action--red" ng-click="vm.remove(item, $event)"><localize key="general_remove">Remove</localize></button>
</div>
</div>
<div class="umb-prevalues-multivalues__right">
<button type="button" class="btn btn-info add" ng-if="vm.editItem == null" ng-click="vm.add($event)"><localize key="general_add">Add</localize></button>
<button type="button" class="btn btn-info add" ng-if="vm.editItem != null" ng-click="vm.add($event)"><localize key="general_update">Update</localize></button>
<button type="button" class="btn btn-link" ng-if="vm.editItem != null" ng-click="vm.cancel($event)"><localize key="general_cancel">Cancel</localize></button>
</div>
</div>
<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 track by $id(item)">
<umb-icon icon="icon-navigation" class="icon handle"></umb-icon>
<div class="umb-prevalues-multivalues__left">
<div class="thumbnail span1" hex-bg-color="{{item.value}}" hex-bg-orig="transparent"></div>
<div class="color-picker-prediv">
<pre>#{{item.value}}</pre>
<span ng-bind="item.label" ng-if="vm.labelEnabled"></span>
</div>
</div>
<div class="umb-prevalues-multivalues__right">
<button type="button" class="umb-node-preview__action umb-node-preview__action--red" ng-click="vm.edit(item, $event)"><localize key="general_edit">Edit</localize></button>
<button type="button" class="umb-node-preview__action umb-node-preview__action--red" ng-click="vm.remove(item, $event)"><localize key="general_remove">Remove</localize></button>
</div>
</div>
</div>
</div>

View File

@@ -1,191 +1,228 @@
angular.module("umbraco").controller("Umbraco.PrevalueEditors.MultiColorPickerController",
function ($scope, angularHelper, $element, eventsService) {
function ($scope, angularHelper, $element, eventsService) {
const vm = this;
const vm = this;
vm.add = add;
vm.remove = remove;
vm.edit = edit;
vm.cancel = cancel;
vm.add = add;
vm.addOnEnter = addOnEnter;
vm.validateLabel = validateLabel;
vm.remove = remove;
vm.edit = edit;
vm.cancel = cancel;
vm.show = show;
vm.hide = hide;
vm.change = change;
vm.show = show;
vm.hide = hide;
vm.change = change;
vm.labelEnabled = false;
vm.editItem = null;
vm.labelEnabled = false;
vm.editItem = null;
// NOTE: We need to make each color an object, not just a string because you cannot 2-way bind to a primitive.
const defaultColor = "000000";
const defaultLabel = null;
// NOTE: We need to make each color an object, not just a string because you cannot 2-way bind to a primitive.
const defaultColor = "000000";
const defaultLabel = null;
$scope.newColor = defaultColor;
$scope.newLabel = defaultLabel;
$scope.hasError = false;
$scope.focusOnNew = false;
$scope.newColor = defaultColor;
$scope.newLabel = defaultLabel;
$scope.colorHasError = false;
$scope.labelHasError = false;
$scope.focusOnNew = false;
$scope.options = {
type: "color",
color: defaultColor,
allowEmpty: false,
showAlpha: false
};
$scope.options = {
type: "color",
color: defaultColor,
allowEmpty: false,
showAlpha: false
};
function hide() {
// show the add button
$element.find(".btn.add").show();
function hide() {
// show the add button
$element.find(".btn.add").show();
}
function show() {
// hide the add button
$element.find(".btn.add").hide();
}
function change(color) {
angularHelper.safeApply($scope, function () {
if (color) {
$scope.newColor = color.toHexString().trimStart("#");
$scope.colorHasError = !colorIsValid();
}
});
}
function show() {
// hide the add button
$element.find(".btn.add").hide();
}
function change(color) {
angularHelper.safeApply($scope, function () {
if (color) {
$scope.newColor = color.toHexString().trimStart("#");
}
});
}
var evts = [];
evts.push(eventsService.on("toggleValue", function (e, args) {
if (args.inputId === "useLabel") {
vm.labelEnabled = args.value;
}
}));
$scope.$on('$destroy', function () {
for (var e in evts) {
eventsService.unsubscribe(evts[e]);
}
});
if (!Utilities.isArray($scope.model.value)) {
//make an array from the dictionary
var items = [];
for (var i in $scope.model.value) {
var oldValue = $scope.model.value[i];
if (Object.prototype.hasOwnProperty.call(oldValue, "value")) {
items.push({
value: oldValue.value,
label: oldValue.label,
sortOrder: oldValue.sortOrder,
id: i
});
} else {
items.push({
value: oldValue,
label: oldValue,
sortOrder: oldValue.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;
}
// ensure labels
for (var ii = 0; ii < $scope.model.value.length; ii++) {
var item = $scope.model.value[ii];
item.label = Object.prototype.hasOwnProperty.call(item, "label") ? item.label : item.value;
}
function validLabel(label) {
return label !== null && typeof label !== "undefined" && label !== "" && label.length && label.length > 0;
}
function remove(item, evt) {
evt.preventDefault();
$scope.model.value = _.reject($scope.model.value, function (x) {
return x.value === item.value && x.label === item.label;
});
setDirty();
}
function add(evt) {
evt.preventDefault();
if ($scope.newColor) {
var newLabel = validLabel($scope.newLabel) ? $scope.newLabel : $scope.newColor;
var exists = _.find($scope.model.value, function (item) {
return item != vm.editItem && (item.value.toUpperCase() === $scope.newColor.toUpperCase() || item.label.toUpperCase() === newLabel.toUpperCase());
});
if (!exists) {
if (vm.editItem == null) {
$scope.model.value.push({
value: $scope.newColor,
label: newLabel
});
} else {
if(vm.editItem.value === vm.editItem.label && vm.editItem.value === newLabel) {
vm.editItem.label = $scope.newColor;
}
else {
vm.editItem.label = newLabel;
}
vm.editItem.value = $scope.newColor;
vm.editItem = null;
}
$scope.newLabel = "";
$scope.hasError = false;
$scope.focusOnNew = true;
setDirty();
return;
}
// there was an error, do the highlight (will be set back by the directive)
$scope.hasError = true;
}
}
function edit(item, evt) {
evt.preventDefault();
vm.editItem = item;
$scope.newColor = item.value;
$scope.newLabel = item.label;
}
function cancel(evt) {
evt.preventDefault();
vm.editItem = null;
$scope.newColor = defaultColor;
$scope.newLabel = defaultLabel;
}
function setDirty() {
if (vm.modelValueForm) {
vm.modelValueForm.selectedColor.$setDirty();
}
}
$scope.sortableOptions = {
axis: 'y',
containment: 'parent',
cursor: 'move',
//handle: ".handle, .thumbnail",
items: '> div.control-group',
tolerance: 'pointer',
update: function () {
setDirty();
}
};
var evts = [];
evts.push(eventsService.on("toggleValue", function (e, args) {
if (args.inputId === "useLabel") {
vm.labelEnabled = args.value;
}
}));
$scope.$on('$destroy', function () {
for (var e in evts) {
eventsService.unsubscribe(evts[e]);
}
});
if (!Utilities.isArray($scope.model.value)) {
//make an array from the dictionary
var items = [];
for (var i in $scope.model.value) {
var oldValue = $scope.model.value[i];
if (Object.prototype.hasOwnProperty.call(oldValue, "value")) {
items.push({
value: oldValue.value,
label: oldValue.label,
sortOrder: oldValue.sortOrder,
id: i
});
} else {
items.push({
value: oldValue,
label: oldValue,
sortOrder: oldValue.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;
}
// ensure labels
for (var ii = 0; ii < $scope.model.value.length; ii++) {
var item = $scope.model.value[ii];
item.label = Object.prototype.hasOwnProperty.call(item, "label") ? item.label : item.value;
}
function remove(item, evt) {
evt.preventDefault();
$scope.model.value = _.reject($scope.model.value, function (x) {
return x.value === item.value && x.label === item.label;
});
setDirty();
}
function colorIsValid() {
var colorExists = _.find($scope.model.value, function (item) {
return item != vm.editItem && item.value.toUpperCase() === $scope.newColor.toUpperCase();
});
return colorExists ? false : true;
}
function getLabel() {
var validLabel = $scope.newLabel !== null && typeof $scope.newLabel !== "undefined" && $scope.newLabel !== "" && $scope.newLabel.length && $scope.newLabel.length > 0;
return validLabel ? $scope.newLabel : $scope.newColor;
}
function labelIsValid() {
var label = getLabel();
label = label.toUpperCase();
var labelExists = _.find($scope.model.value, function (item) {
return item != vm.editItem && item.label.toUpperCase() === label;
});
return labelExists ? false : true;
}
function validateLabel() {
$scope.labelHasError = !labelIsValid();
}
function addOnEnter(evt) {
if (evt.keyCode === 13) {
add(evt);
}
}
function add(evt) {
evt.preventDefault();
if ($scope.newColor) {
$scope.colorHasError = !colorIsValid();
$scope.labelHasError = !labelIsValid();
if ($scope.labelHasError || $scope.colorHasError) {
return;
}
var newLabel = getLabel();
if (vm.editItem == null) {
$scope.model.value.push({
value: $scope.newColor,
label: newLabel
});
} else {
if (vm.editItem.value === vm.editItem.label && vm.editItem.value === newLabel) {
vm.editItem.label = $scope.newColor;
}
else {
vm.editItem.label = newLabel;
}
vm.editItem.value = $scope.newColor;
vm.editItem = null;
}
$scope.newLabel = "";
$scope.colorHasError = false;
$scope.labelHasError = false;
$scope.focusOnNew = true;
setDirty();
return;
}
}
function edit(item, evt) {
evt.preventDefault();
vm.editItem = item;
$scope.newColor = item.value;
$scope.newLabel = item.label;
}
function cancel(evt) {
evt.preventDefault();
vm.editItem = null;
$scope.newColor = defaultColor;
$scope.newLabel = defaultLabel;
}
function setDirty() {
if (vm.modelValueForm) {
vm.modelValueForm.selectedColor.$setDirty();
}
}
$scope.sortableOptions = {
axis: 'y',
containment: 'parent',
cursor: 'move',
//handle: ".handle, .thumbnail",
items: '> div.control-group',
tolerance: 'pointer',
update: function () {
setDirty();
}
};
});