diff --git a/css/80_app.css b/css/80_app.css index 6a47129e9..5a4d52be1 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -3009,50 +3009,60 @@ div.full-screen > button:hover { width: 100%; font-weight: inherit; border-radius: 0; + text-align: inherit; + display: flex; + color: inherit; } [dir='rtl'] .issue button { padding: 5px 5px 5px 10px; } - -.issue button.label { - text-align: inherit; - display: flex; -} .warnings-list, .issue.severity-warning, -.issue.severity-warning li, li.issue.severity-warning { border-color: #FFDF5C; } .errors-list, .issue.severity-error, -.issue.severity-error li, li.issue.severity-error { border-color: #f5b0ab; } +.issue.severity-warning, .issue.severity-warning button, .mode-save .warning-section { background: #ffb; } -.issue.severity-warning button:hover, -.issue.severity-warning button:focus { +.issue.severity-warning:not(.expanded) button:hover, +.issue.severity-warning:not(.expanded) button:focus { background: #FFFF99; } -.issue.severity-warning .icon { - color: #FFB300 +.issue.severity-warning .issue-icon, +.issue.severity-warning .issue-fix-item.actionable { + color: #ff9205; + fill: #ff9205; +} +.issue.severity-warning .issue-fix-item.actionable:hover { + color: #f07504; + fill: #f07504; } +.issue.severity-error, .issue.severity-error button, .mode-save .error-section { background: #FFD5D4; } -.issue.severity-error button:hover, -.issue.severity-error button:focus { +.issue.severity-error:not(.expanded) button:hover, +.issue.severity-error:not(.expanded) button:focus { background: #ffc9c7; } -.issue.severity-error .icon { - color: #DD1400 +.issue.severity-error .issue-icon, +.issue.severity-error .issue-fix-item.actionable { + color: #DD1400; + fill: #DD1400; +} +.issue.severity-error .issue-fix-item.actionable:hover { + color: #ab0f00; + fill: #ab0f00; } /* Issues Pane */ @@ -3091,13 +3101,27 @@ div.full-screen > button:hover { .entity-issues .issue:not(:last-of-type) { margin-bottom: 10px; } -ul.issue-fix-list li:first-child { - border-top-style: solid; - border-top-width: 2px; +.issue.expanded button.label { + cursor: pointer; + pointer-events: none; + padding-bottom: 0px; } -ul.issue-fix-list li:not(:last-of-type) { - border-bottom-style: solid; - border-bottom-width: 1px; +ul.issue-fix-list button { + padding: 2px 10px 2px 26px; +} +.issue-fix-item:first-of-type button { + padding-top:4px; +} +.issue-fix-item:last-of-type button { + padding-bottom:7px; +} +.issue-fix-item:not(.actionable) button { + cursor: pointer; + pointer-events: none; +} +.issue-fix-item:not(.actionable) .fix-icon { + color: #555; + fill: #555; } .issue:not(.expanded) ul.issue-fix-list { height: 0px; diff --git a/data/core.yaml b/data/core.yaml index be513a2ed..7d8314cb1 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -1266,6 +1266,8 @@ en: remove_tag: title: Remove the tag annotation: Removed tag. + reposition_features: + title: Reposition the features select_preset: title: Select a feature type tag_as_disconnected: diff --git a/dist/locales/en.json b/dist/locales/en.json index 6ec4c597c..8400cde39 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -1556,6 +1556,9 @@ "title": "Remove the tag", "annotation": "Removed tag." }, + "reposition_features": { + "title": "Reposition the features" + }, "select_preset": { "title": "Select a feature type" }, diff --git a/modules/core/validator.js b/modules/core/validator.js index cf917f3bb..09726117d 100644 --- a/modules/core/validator.js +++ b/modules/core/validator.js @@ -222,6 +222,7 @@ export function validationIssue(attrs) { export function validationIssueFix(attrs) { + this.icon = attrs.icon; this.title = attrs.title; this.onClick = attrs.onClick; this.entityIds = attrs.entityIds || []; // Used for hover-higlighting. diff --git a/modules/ui/entity_issues.js b/modules/ui/entity_issues.js index ee962faf0..86b044299 100644 --- a/modules/ui/entity_issues.js +++ b/modules/ui/entity_issues.js @@ -14,11 +14,13 @@ export function uiEntityIssues(context) { var _entityID; - // context.validator().on('reload.entity_issues', function() { - // _selection.selectAll('.entity-issues') - // .call(render); - // update(); - // }); + context.validator().on('reload.entity_issues', function() { + + update(); + + _selection.selectAll('.entity-issues') + .call(render); + }); function clamp(num, min, max) { @@ -64,6 +66,11 @@ export function uiEntityIssues(context) { var itemsEnter = items.enter() .append('div') .attr('class', function(d) { return 'issue severity-' + d.severity; }) + .call(tooltip() + .html(true) + .title(function(d) { return uiTooltipHtml(d.tooltip); }) + .placement('top') + ) .on('mouseover.highlight', function(d) { var ids = d.entities.map(function(e) { return e.id; }); utilHighlightEntities(ids, true, context); @@ -91,13 +98,6 @@ export function uiEntityIssues(context) { .append('button') .attr('class', 'label'); - labelsEnter - .call(tooltip() - .html(true) - .title(function(d) { return uiTooltipHtml(d.tooltip); }) - .placement('top') - ); - labelsEnter .append('span') .attr('class', 'issue-icon') @@ -129,14 +129,39 @@ export function uiEntityIssues(context) { // fixes var fixLists = items.selectAll('.issue-fix-list'); - fixLists.selectAll('.issue-fix-item') + var fixes = fixLists.selectAll('.issue-fix-item') .data(function(d) { return d.fixes; }) .enter() .append('li') - .attr('class', 'issue-fix-item') + .attr('class', function(d) { + return 'issue-fix-item ' + (!!d.onClick ? 'actionable' : ''); + }) .append('button') - .text(function(d) { return d.title; }) - .on('click', function(d) { d.onClick(); }); + .on('click', function(d) { + if (d.onClick) { + utilHighlightEntities(d.entityIds, false, context); + d.onClick(); + } + }) + .on('mouseover.highlight', function(d) { + utilHighlightEntities(d.entityIds, true, context); + }) + .on('mouseout.highlight', function(d) { + utilHighlightEntities(d.entityIds, false, context); + }); + + fixes.append('span') + .attr('class', 'fix-icon') + .each(function(d) { + var iconName = d.icon || 'iD-icon-wrench'; + if (iconName.startsWith('maki')) { + iconName += '-15'; + } + d3_select(this).call(svgIcon('#' + iconName, 'pre-text')); + }); + + fixes.append('span') + .text(function(d) { return d.title; }); } diff --git a/modules/validations/almost_junction.js b/modules/validations/almost_junction.js index 31aeb9e88..00cc79b06 100644 --- a/modules/validations/almost_junction.js +++ b/modules/validations/almost_junction.js @@ -162,6 +162,7 @@ export function validationAlmostJunction() { if (Object.keys(node.tags).length === 0) { // node has no tags, suggest noexit fix fixes.push(new validationIssueFix({ + icon: 'maki-barrier', title: t('issues.fix.tag_as_disconnected.title'), onClick: function() { var nodeID = this.issue.entities[1].id; diff --git a/modules/validations/crossing_ways.js b/modules/validations/crossing_ways.js index 5b3007b7d..27d0a7458 100644 --- a/modules/validations/crossing_ways.js +++ b/modules/validations/crossing_ways.js @@ -398,7 +398,9 @@ export function validationCrossingWays() { } })); } - + fixes.push(new validationIssueFix({ + title: t('issues.fix.reposition_features.title') + })); return new validationIssue({ type: type, severity: 'warning', diff --git a/modules/validations/deprecated_tag.js b/modules/validations/deprecated_tag.js index a3da623c1..2247ad14a 100644 --- a/modules/validations/deprecated_tag.js +++ b/modules/validations/deprecated_tag.js @@ -30,6 +30,7 @@ export function validationDeprecatedTag() { }, fixes: [ new validationIssueFix({ + icon: 'iD-icon-up', title: t('issues.fix.upgrade_tags.title'), onClick: function() { var entity = this.issue.entities[0]; diff --git a/modules/validations/disconnected_way.js b/modules/validations/disconnected_way.js index 85c514b5c..55645960f 100644 --- a/modules/validations/disconnected_way.js +++ b/modules/validations/disconnected_way.js @@ -38,6 +38,7 @@ export function validationDisconnectedWay() { if (!entity.isClosed()) { fixes.push(new validationIssueFix({ + icon: 'iD-operation-continue-left', title: t('issues.fix.continue_from_start.title'), entityIds: [entity.first()], onClick: function() { @@ -46,6 +47,7 @@ export function validationDisconnectedWay() { } })); fixes.push(new validationIssueFix({ + icon: 'iD-operation-continue', title: t('issues.fix.continue_from_end.title'), entityIds: [entity.last()], onClick: function() { @@ -56,6 +58,7 @@ export function validationDisconnectedWay() { } fixes.push(new validationIssueFix({ + icon: 'iD-operation-delete', title: t('issues.fix.delete_feature.title'), entityIds: [entity.id], onClick: function() { @@ -80,7 +83,7 @@ export function validationDisconnectedWay() { function continueDrawing(way, vertex) { // make sure the vertex is actually visible and editable var map = context.map(); - if (!map.editable() || !map.contains(vertex.loc)) { + if (!map.editable() || !map.trimmedExtent().contains(vertex.loc)) { map.zoomToEase(vertex); } diff --git a/modules/validations/generic_name.js b/modules/validations/generic_name.js index 9c5293706..9ab314901 100644 --- a/modules/validations/generic_name.js +++ b/modules/validations/generic_name.js @@ -6,7 +6,7 @@ import { actionChangeTags } from '../actions'; import { discardNames } from '../../node_modules/name-suggestion-index/config/filters.json'; -export function validationGenericName(context) { +export function validationGenericName() { var type = 'generic_name'; @@ -37,7 +37,7 @@ export function validationGenericName(context) { } - var validation = function(entity) { + var validation = function(entity, context) { var issues = []; var generic = isGenericName(entity); if (generic) { @@ -50,6 +50,7 @@ export function validationGenericName(context) { entities: [entity], fixes: [ new validationIssueFix({ + icon: 'iD-operation-delete', title: t('issues.fix.remove_generic_name.title'), onClick: function() { var entity = this.issue.entities[0]; diff --git a/modules/validations/missing_tag.js b/modules/validations/missing_tag.js index 3ed4313a7..6d472c4ae 100644 --- a/modules/validations/missing_tag.js +++ b/modules/validations/missing_tag.js @@ -58,12 +58,14 @@ export function validationMissingTag() { entities: [entity], fixes: [ new validationIssueFix({ + icon: 'iD-icon-search', title: t('issues.fix.select_preset.title'), onClick: function() { context.ui().sidebar.showPresetList(); } }), new validationIssueFix({ + icon: 'iD-operation-delete', title: t('issues.fix.delete_feature.title'), onClick: function() { var id = this.issue.entities[0].id; diff --git a/svg/iD-sprite/icons/icon-wrench.svg b/svg/iD-sprite/icons/icon-wrench.svg new file mode 100644 index 000000000..0eaef0b6b --- /dev/null +++ b/svg/iD-sprite/icons/icon-wrench.svg @@ -0,0 +1,4 @@ + + + + diff --git a/svg/iD-sprite/operations/operation-continue-left.svg b/svg/iD-sprite/operations/operation-continue-left.svg new file mode 100644 index 000000000..28e35f6b9 --- /dev/null +++ b/svg/iD-sprite/operations/operation-continue-left.svg @@ -0,0 +1,4 @@ + + + +