modules/ui/sections/validation_issues.js (170 lines of code) (raw):
import _debounce from 'lodash-es/debounce';
import { select as d3_select } from 'd3-selection';
import { actionNoop } from '../../actions/noop';
import { geoSphericalDistance } from '@id-sdk/geo';
import { svgIcon } from '../../svg/icon';
import { prefs } from '../../core/preferences';
import { t } from '../../core/localizer';
import { utilHighlightEntities } from '../../util';
import { uiSection } from '../section';
export function uiSectionValidationIssues(id, severity, context) {
var _issues = [];
var section = uiSection(id, context)
.label(function() {
if (!_issues) return '';
var issueCountText = _issues.length > 1000 ? '1000+' : String(_issues.length);
return t('inspector.title_count', { title: t.html('issues.' + severity + 's.list_title'), count: issueCountText });
})
.disclosureContent(renderDisclosureContent)
.shouldDisplay(function() {
return _issues && _issues.length;
});
function getOptions() {
return {
what: prefs('validate-what') || 'edited',
where: prefs('validate-where') || 'all'
};
}
// get and cache the issues to display, unordered
function reloadIssues() {
_issues = context.validator().getIssuesBySeverity(getOptions())[severity];
}
function renderDisclosureContent(selection) {
var center = context.map().center();
var graph = context.graph();
// sort issues by distance away from the center of the map
var issues = _issues.map(function withDistance(issue) {
var extent = issue.extent(graph);
var dist = extent ? geoSphericalDistance(center, extent.center()) : 0;
return Object.assign(issue, { dist: dist });
})
.sort(function byDistance(a, b) {
return a.dist - b.dist;
});
// cut off at 1000
issues = issues.slice(0, 1000);
//renderIgnoredIssuesReset(_warningsSelection);
selection
.call(drawIssuesList, issues);
}
function drawIssuesList(selection, issues) {
const showAutoFix = prefs('rapid-internal-feature.showAutoFix') === 'true';
var list = selection.selectAll('.issues-list')
.data([0]);
list = list.enter()
.append('ul')
.attr('class', 'layer-list issues-list ' + severity + 's-list')
.merge(list);
var items = list.selectAll('li')
.data(issues, function(d) { return d.key; });
// Exit
items.exit()
.remove();
// Enter
var itemsEnter = items.enter()
.append('li')
.attr('class', function (d) { return 'issue severity-' + d.severity; });
var labelsEnter = itemsEnter
.append('button')
.attr('class', 'issue-label')
.on('click', function(d3_event, d) {
context.validator().focusIssue(d);
})
.on('mouseover', function(d3_event, d) {
utilHighlightEntities(d.entityIds, true, context);
})
.on('mouseout', function(d3_event, d) {
utilHighlightEntities(d.entityIds, false, context);
});
var textEnter = labelsEnter
.append('span')
.attr('class', 'issue-text');
textEnter
.append('span')
.attr('class', 'issue-icon')
.each(function(d) {
var iconName = '#iD-icon-' + (d.severity === 'warning' ? 'alert' : 'error');
d3_select(this)
.call(svgIcon(iconName));
});
textEnter
.append('span')
.attr('class', 'issue-message');
if (showAutoFix) {
labelsEnter
.append('span')
.attr('class', 'issue-autofix')
.each(function(d) {
if (!d.autoArgs) return;
d3_select(this)
.append('button')
.attr('title', t('issues.fix_one.title'))
.datum(d) // set button datum to the issue
.attr('class', 'autofix action')
.on('click', function(d3_event, d) {
d3_event.preventDefault();
d3_event.stopPropagation();
utilHighlightEntities(d.entityIds, false, context); // unhighlight
context.perform.apply(context, d.autoArgs);
context.validator().validate();
})
.call(svgIcon('#iD-icon-wrench'));
});
} /* show autoFix */
// Update
items = items
.merge(itemsEnter)
.order();
items.selectAll('.issue-message')
.html(function(d) {
return d.message(context);
});
var canAutoFix = issues.filter(function(issue) { return issue.autoArgs; });
var autoFixAll = selection.selectAll('.autofix-all')
.data(showAutoFix && canAutoFix.length ? [0] : []);
// exit
autoFixAll.exit()
.remove();
// enter
var autoFixAllEnter = autoFixAll.enter()
.insert('div', '.issues-list')
.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')
.html(t.html('issues.fix_all.title'));
linkEnter
.append('span')
.attr('class', 'autofix-all-link-icon')
.call(svgIcon('#iD-icon-wrench'));
// if (severity === 'warning') {
// renderIgnoredIssuesReset(selection);
// }
// update
autoFixAll = autoFixAll
.merge(autoFixAllEnter);
autoFixAll.selectAll('.autofix-all-link')
.on('click', function() {
context.pauseChangeDispatch();
context.perform(actionNoop());
canAutoFix.forEach(function(issue) {
var args = issue.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.resumeChangeDispatch();
context.validator().validate();
});
}
context.validator().on('validated.uiSectionValidationIssues' + id, function() {
window.requestIdleCallback(function() {
reloadIssues();
section.reRender();
});
});
context.map().on('move.uiSectionValidationIssues' + id,
_debounce(function() {
window.requestIdleCallback(function() {
if (getOptions().where === 'visible') {
// must refetch issues if they are viewport-dependent
reloadIssues();
}
// always reload list to re-sort-by-distance
section.reRender();
});
}, 1000)
);
return section;
}