Change autofix to accept arguments to perform, implement "fix all"

This commit is contained in:
Bryan Housel
2019-04-17 17:51:29 -04:00
parent d6327aec56
commit 9c4881cade
7 changed files with 153 additions and 74 deletions

View File

@@ -3126,9 +3126,8 @@ div.full-screen > button:hover {
padding: 5px 0;
}
.issue-label .issue-autofix {
width: 36px;
flex: 0 0 auto;
padding: 5px 7px;
padding: 5px 8px;
}
.issue-label .issue-info-button {
height: unset;
@@ -3149,19 +3148,39 @@ div.full-screen > button:hover {
border-radius: 4px 0 0 4px;
}
.issue-autofix button.autofix.action {
button.autofix.action {
flex: 0 0 20px;
height: 20px;
width: 20px;
background: #7092ff;
color: #fff;
}
.issue-label button.autofix.action:focus,
.issue-label button.autofix.action:hover,
.issue-label button.autofix.action.active {
button.autofix.action:focus,
button.autofix.action:hover,
button.autofix.action.active {
background: #597be7;
}
/* fix all */
.autofix-all {
display: flex;
flex-flow: row nowrap;
flex-direction: row-reverse;
height: 30px;
padding-top: 5px;
}
.autofix-all-link-text {
padding: 0 5px;
}
.autofix-all-link-icon svg {
margin: 0 9px;
background: currentColor;
border-radius: 4px;
}
.autofix-all-link-icon svg use {
color: #fff;
}
/* warning styles */
.warnings-list,
.warnings-list *,
@@ -3245,6 +3264,10 @@ div.full-screen > button:hover {
color: #ff0c05;
}
.layer-list.issues-list {
margin-bottom: 0;
}
/* Issues Pane */
.issues-options-container {

View File

@@ -1270,6 +1270,11 @@ en:
visible: "In View"
all: "Everywhere"
suggested: "Suggested updates:"
fix_one:
title: fix
fix_all:
title: Fix All
annotation: Fixed several validation issues.
almost_junction:
title: Almost Junctions
message: "{feature} is very close but not connected to {feature2}"

View File

@@ -1549,6 +1549,13 @@
}
},
"suggested": "Suggested updates:",
"fix_one": {
"title": "fix"
},
"fix_all": {
"title": "Fix All",
"annotation": "Fixed several validation issues."
},
"almost_junction": {
"title": "Almost Junctions",
"message": "{feature} is very close but not connected to {feature2}",

View File

@@ -318,11 +318,13 @@ export function coreValidator(context) {
// if (!difference) return;
// validator.validate();
// });
context.history()
.on('undone.validator', validator.validate)
.on('redone.validator', validator.validate);
// re-run validation when the user switches editing modes (less frequent)
context.on('exit.validator', function() {
validator.validate();
});
context
.on('exit.validator', validator.validate);
return validator;
@@ -342,7 +344,7 @@ export function validationIssue(attrs) {
this.hash = attrs.hash; // optional - string to further differentiate the issue
this.id = generateID.apply(this); // generated - see below
this.auto = null; // generated - if autofix exists, will be set below
this.autoFix = null; // generated - if autofix exists, will be set below
// A unique, deterministic string hash.
// Issues with identical id values are considered identical.
@@ -389,8 +391,8 @@ export function validationIssue(attrs) {
for (var i = 0; i < this.fixes.length; i++) {
var fix = this.fixes[i];
fix.issue = this;
if (fix.auto) {
this.auto = fix;
if (fix.autoArgs) {
this.autoFix = fix;
}
}
}
@@ -398,11 +400,11 @@ export function validationIssue(attrs) {
export function validationIssueFix(attrs) {
this.title = attrs.title; // Required
this.onClick = attrs.onClick; // Required
this.icon = attrs.icon; // Optional - shows 'iD-icon-wrench' if not set
this.entityIds = attrs.entityIds || []; // Optional - Used for hover-higlighting.
this.auto = attrs.auto; // Optional - pass true if this fix can be an auto fix
this.title = attrs.title; // Required
this.onClick = attrs.onClick; // Required
this.icon = attrs.icon; // Optional - shows 'iD-icon-wrench' if not set
this.entityIds = attrs.entityIds || []; // Optional - Used for hover-higlighting.
this.autoArgs = attrs.autoArgs; // Optional - pass [actions, annotation] arglist if this fix can automatically run
this.issue = null; // Generated link - added by ValidationIssue constructor
}

View File

@@ -4,6 +4,8 @@ import { event as d3_event, select as d3_select } from 'd3-selection';
import { t, textDirection } from '../util/locale';
import { tooltip } from '../util/tooltip';
import { actionNoop } from '../actions';
import { geoSphericalDistance } from '../geo';
import { modeSelect } from '../modes';
import { svgIcon } from '../svg';
@@ -17,8 +19,8 @@ import { utilCallWhenIdle, utilHighlightEntities } from '../util';
export function uiIssues(context) {
var key = t('issues.key');
var _errorsList = d3_select(null);
var _warningsList = d3_select(null);
var _errorsSelection = d3_select(null);
var _warningsSelection = d3_select(null);
var _rulesList = d3_select(null);
var _pane = d3_select(null);
var _toggleButton = d3_select(null);
@@ -51,35 +53,30 @@ export function uiIssues(context) {
.attr('fill', 'currentColor');
}
function renderErrorsList(selection) {
_errorsList = selection.selectAll('.errors-list')
.data([0]);
_errorsList = _errorsList.enter()
.append('ul')
.attr('class', 'layer-list errors-list issues-list')
.merge(_errorsList);
_errorsList
.call(drawIssuesList, _errors);
_errorsSelection = selection
.call(drawIssuesList, 'errors', _errors);
}
function renderWarningsList(selection) {
_warningsList = selection.selectAll('.warnings-list')
.data([0]);
_warningsList = _warningsList.enter()
.append('ul')
.attr('class', 'layer-list warnings-list issues-list')
.merge(_warningsList);
_warningsList
.call(drawIssuesList, _warnings);
_warningsSelection = selection
.call(drawIssuesList, 'warnings', _warnings);
}
function drawIssuesList(selection, issues) {
var items = selection.selectAll('li')
function drawIssuesList(selection, which, issues) {
var list = selection.selectAll('.issues-list')
.data([0]);
list = list.enter()
.append('ul')
.attr('class', 'layer-list issues-list ' + which + '-list')
.merge(list);
var items = list.selectAll('li')
.data(issues, function(d) { return d.id; });
// Exit
@@ -143,15 +140,16 @@ export function uiIssues(context) {
.append('span')
.attr('class', 'issue-autofix')
.each(function(d) {
if (!d.auto) return;
if (!d.autoFix) return;
d3_select(this)
.append('button')
.datum(d.auto) // set button datum to the autofix
.attr('title', t('issues.fix_one.title'))
.datum(d.autoFix) // set button datum to the autofix
.attr('class', 'autofix action')
.on('click', function(d) {
utilHighlightEntities(d.entityIds, false, context);
d.onClick();
context.perform.apply(context, d.autoArgs);
context.validator().validate();
})
.call(svgIcon('#iD-icon-wrench'));
@@ -162,6 +160,55 @@ export function uiIssues(context) {
items = items
.merge(itemsEnter)
.order();
// autofix
var canAutoFix = issues.filter(function(issue) { return issue.autoFix; });
var autoFixAll = selection.selectAll('.autofix-all')
.data(canAutoFix.length ? [0] : []);
// exit
autoFixAll.exit()
.remove();
// enter
var autoFixAllEnter = autoFixAll.enter()
.append('div')
.attr('class', 'autofix-all');
var linkEnter = autoFixAllEnter
.append('a')
.attr('class', 'autofix-all-link')
.attr('href', '#');
linkEnter
.append('span')
.attr('class', 'autofix-all-link-text')
.text(t('issues.fix_all.title'));
linkEnter
.append('span')
.attr('class', 'autofix-all-link-icon')
.call(svgIcon('#iD-icon-wrench'));
// update
autoFixAll = autoFixAll
.merge(autoFixAllEnter);
autoFixAll.selectAll('.autofix-all-link')
.on('click', function() {
context.perform(actionNoop());
canAutoFix.forEach(function(issue) {
var args = issue.autoFix.autoArgs.slice(); // copy
if (typeof args[args.length - 1] !== 'function') {
args.pop();
}
args.push(t('issues.fix_all.annotation'));
context.replace.apply(context, args);
});
context.validator().validate();
});
}
@@ -292,27 +339,28 @@ export function uiIssues(context) {
.classed('warning', (_errors.length === 0 && _warnings.length > 0))
.classed('hide', (_errors.length === 0 && _warnings.length === 0));
_pane.select('.issues-errors')
_pane.selectAll('.issues-errors')
.classed('hide', _errors.length === 0);
if (_errors.length > 0) {
_pane.select('.hide-toggle-issues_errors .hide-toggle-text')
_pane.selectAll('.hide-toggle-issues_errors .hide-toggle-text')
.text(t('issues.errors.list_title', { count: errorCount }));
if (!_pane.select('.disclosure-wrap-issues_errors').classed('hide')) {
_errorsList
.call(drawIssuesList, _errors);
_errorsSelection
.call(drawIssuesList, 'errors', _errors);
}
}
_pane.select('.issues-warnings')
_pane.selectAll('.issues-warnings')
.classed('hide', _warnings.length === 0);
if (_warnings.length > 0) {
_pane.select('.hide-toggle-issues_warnings .hide-toggle-text')
_pane.selectAll('.hide-toggle-issues_warnings .hide-toggle-text')
.text(t('issues.warnings.list_title', { count: warningCount }));
if (!_pane.select('.disclosure-wrap-issues_warnings').classed('hide')) {
_warningsList
.call(drawIssuesList, _warnings);
_warningsSelection
.call(drawIssuesList, 'warnings', _warnings);
}
}

View File

@@ -34,26 +34,23 @@ export function validationOldMultipolygon() {
entities: [outerWay, multipolygon],
fixes: [
new validationIssueFix({
auto: true,
autoArgs: [doUpgrade, t('issues.fix.move_tags.annotation')],
title: t('issues.fix.move_tags.title'),
onClick: function() {
var outerWay = this.issue.entities[0];
var multipolygon = this.issue.entities[1];
context.perform(
function(graph) {
multipolygon = multipolygon.mergeTags(outerWay.tags);
graph = graph.replace(multipolygon);
graph = actionChangeTags(outerWay.id, {})(graph);
return graph;
},
t('issues.fix.move_tags.annotation')
);
context.perform(doUpgrade, t('issues.fix.move_tags.annotation'));
}
})
]
})];
function doUpgrade(graph) {
multipolygon = multipolygon.mergeTags(outerWay.tags);
graph = graph.replace(multipolygon);
return actionChangeTags(outerWay.id, {})(graph);
}
function showReference(selection) {
selection.selectAll('.issue-reference')
.data([0])

View File

@@ -63,26 +63,23 @@ export function validationOutdatedTags() {
message: t('issues.outdated_tags.message', { feature: utilDisplayLabel(entity, context) }),
reference: showReference,
entities: [entity],
data: {
newTags: newTags
},
fixes: [
new validationIssueFix({
auto: true,
autoArgs: [doUpgrade, t('issues.fix.upgrade_tags.annotation')],
title: t('issues.fix.upgrade_tags.title'),
onClick: function() {
var entityID = this.issue.entities[0].id;
var newTags = this.issue.data.newTags;
context.perform(
actionChangeTags(entityID, newTags),
t('issues.fix.upgrade_tags.annotation')
);
context.perform(doUpgrade, t('issues.fix.upgrade_tags.annotation'));
}
})
]
})];
function doUpgrade(graph) {
return actionChangeTags(entity.id, newTags)(graph);
}
function showReference(selection) {
var enter = selection.selectAll('.issue-reference')
.data([0])