From 4e3e9421272ac0c11507cf2d6aaa797bef88b61d Mon Sep 17 00:00:00 2001 From: Martin Raifer Date: Mon, 12 Dec 2022 19:03:03 +0100 Subject: [PATCH 1/2] show (route) relation `colour`s in lists and comboboxes --- CHANGELOG.md | 2 ++ css/80_app.css | 29 +++++++++++++++++- modules/ui/feature_list.js | 2 ++ modules/ui/sections/raw_member_editor.js | 2 ++ modules/ui/sections/raw_membership_editor.js | 32 +++++++++++++++++--- 5 files changed, 62 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52586f666..25612167a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ _Breaking developer changes, which may affect downstream projects or sites that #### :tada: New Features * Show a _remaining input length_ indicator and a warning if the maximum for OSM tags (typically, 255 characters) is exceeded ([#9390], [#9392] thanks [@alanb43], [#7943], [#9374]) #### :sparkles: Usability & Accessibility +* Show the color of (route) relations in the form of small colored circles in relation membership section and feature search results ([#9424]) #### :white_check_mark: Validation #### :bug: Bugfixes * Fix bug which made it impossible to change an object's preset from a sub-preset to the respective parents preset (e.g. from Driveway to Service Road) ([#9372]) @@ -65,6 +66,7 @@ _Breaking developer changes, which may affect downstream projects or sites that [#9397]: https://github.com/openstreetmap/iD/issues/9397 [#9413]: https://github.com/openstreetmap/iD/pull/9413 [#9423]: https://github.com/openstreetmap/iD/pull/9423 +[#9424]: https://github.com/openstreetmap/iD/pull/9424 [@alanb43]: https://github.com/alanb43 [@Rewinteer]: https://github.com/Rewinteer diff --git a/css/80_app.css b/css/80_app.css index de9ea0a8c..ff3e49b2d 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -1412,7 +1412,7 @@ a.hide-toggle { .ideditor[dir='rtl'] .field-label .label-text { padding: 5px 10px 4px 0; } -.field-label .label-text span { +.field-label .label-text { white-space: nowrap; } @@ -2748,12 +2748,39 @@ img.tag-reference-wiki-image { font-weight: normal; padding-left: 10px; } +.section-raw-member-editor .member-row .member-entity-name.has-colour::before, +.section-raw-membership-editor .member-row .member-entity-name.has-colour::before, +.feature-list .entity-name.has-colour::before, +.combobox-parent-relation .has-colour::before { + display: inline-block; + content: ''; + margin-left: -5px; + margin-right: 5px; + border-style: solid; + border-width: 4px; + border-radius: 4px; + border-color: inherit; +} +.combobox-parent-relation .has-colour::before { + margin-left: 2px; +} .ideditor[dir='rtl'] .section-raw-member-editor .member-row .member-entity-name, .ideditor[dir='rtl'] .section-raw-membership-editor .member-row .member-entity-name { padding-left:0; padding-right: 10px; } +.ideditor[dir='rtl'] .section-raw-member-editor .member-row .member-entity-name.has-colour::before, +.ideditor[dir='rtl'] .section-raw-membership-editor .member-row .member-entity-name.has-colour::before, +.ideditor[dir='rtl'] .feature-list .entity-name.has-colour::before { + margin-left: 5px; + margin-right: -5px; +} +.ideditor[dir='rtl'] .combobox-parent-relation .has-colour::before { + margin-left: 5px; + margin-right: 2px; +} + .form-field-input-member > input.member-role { border-radius: 0 0 4px 4px; diff --git a/modules/ui/feature_list.js b/modules/ui/feature_list.js index c0634f1eb..17586d01d 100644 --- a/modules/ui/feature_list.js +++ b/modules/ui/feature_list.js @@ -310,6 +310,8 @@ export function uiFeatureList(context) { label .append('span') .attr('class', 'entity-name') + .classed('has-colour', d => d.entity && d.entity.type === 'relation' && d.entity.tags.colour) + .style('border-color', d => d.entity && d.entity.type === 'relation' && d.entity.tags.colour) .text(function(d) { return d.name; }); enter diff --git a/modules/ui/sections/raw_member_editor.js b/modules/ui/sections/raw_member_editor.js index ab5942785..9ad0901a9 100644 --- a/modules/ui/sections/raw_member_editor.js +++ b/modules/ui/sections/raw_member_editor.js @@ -198,6 +198,8 @@ export function uiSectionRawMemberEditor(context) { labelLink .append('span') .attr('class', 'member-entity-name') + .classed('has-colour', d => d.member.type === 'relation' && d.member.tags.colour) + .style('border-color', d => d.member.type === 'relation' && d.member.tags.colour) .text(function(d) { return utilDisplayName(d.member); }); label diff --git a/modules/ui/sections/raw_membership_editor.js b/modules/ui/sections/raw_membership_editor.js index 093df5d4b..3e1e250e3 100644 --- a/modules/ui/sections/raw_membership_editor.js +++ b/modules/ui/sections/raw_membership_editor.js @@ -239,7 +239,7 @@ export function uiSectionRawMembershipEditor(context) { var graph = context.graph(); - function baseDisplayLabel(entity) { + function baseDisplayValue(entity) { var matched = presetManager.match(entity, graph); var presetName = (matched && matched.name()) || t('inspector.relation'); var entityName = utilDisplayName(entity) || ''; @@ -247,23 +247,45 @@ export function uiSectionRawMembershipEditor(context) { return presetName + ' ' + entityName; } + function baseDisplayLabel(entity) { + var matched = presetManager.match(entity, graph); + var presetName = (matched && matched.name()) || t('inspector.relation'); + var entityName = utilDisplayName(entity) || ''; + + return selection => { + selection + .append('b') + .text(presetName + ' '); + selection + .append('span') + .classed('has-colour', entity.tags.colour) + .style('border-color', entity.tags.colour) + .text(entityName); + }; + } + var explicitRelation = q && context.hasEntity(q.toLowerCase()); if (explicitRelation && explicitRelation.type === 'relation' && explicitRelation.id !== entityID) { // loaded relation is specified explicitly, only show that result.push({ relation: explicitRelation, - value: baseDisplayLabel(explicitRelation) + ' ' + explicitRelation.id + value: baseDisplayValue(explicitRelation) + ' ' + explicitRelation.id, + display: baseDisplayLabel(explicitRelation) }); } else { context.history().intersects(context.map().extent()).forEach(function(entity) { if (entity.type !== 'relation' || entity.id === entityID) return; - var value = baseDisplayLabel(entity); + var value = baseDisplayValue(entity); if (q && (value + ' ' + entity.id).toLowerCase().indexOf(q.toLowerCase()) === -1) return; - result.push({ relation: entity, value: value }); + result.push({ + relation: entity, + value, + display: baseDisplayLabel(entity) + }); }); result.sort(function(a, b) { @@ -349,6 +371,8 @@ export function uiSectionRawMembershipEditor(context) { labelLink .append('span') .attr('class', 'member-entity-name') + .classed('has-colour', d => d.relation.tags.colour) + .style('border-color', d => d.relation.tags.colour) .text(function(d) { return utilDisplayName(d.relation); }); labelEnter From 20b72a2b682d40ce268223167ed4cbcf688e8fe5 Mon Sep 17 00:00:00 2001 From: Martin Raifer Date: Fri, 17 Nov 2023 13:45:49 +0100 Subject: [PATCH 2/2] only render route colours if the value is a valid color addresses https://github.com/openstreetmap/iD/pull/9424#discussion_r1046495633 --- modules/osm/tags.js | 13 +++++++++++++ modules/ui/feature_list.js | 3 ++- modules/ui/fields/input.js | 11 +---------- modules/ui/sections/raw_member_editor.js | 3 ++- modules/ui/sections/raw_membership_editor.js | 5 +++-- 5 files changed, 21 insertions(+), 14 deletions(-) diff --git a/modules/osm/tags.js b/modules/osm/tags.js index 4962e969d..f3eb6cc1d 100644 --- a/modules/osm/tags.js +++ b/modules/osm/tags.js @@ -245,3 +245,16 @@ export var osmFlowingWaterwayTagValues = { // Tags which values should be considered case sensitive when offering tag suggestions export const allowUpperCaseTagValues = /network|taxon|genus|species|brand|grape_variety|royal_cypher|listed_status|booth|rating|stars|:output|_hours|_times|_ref|manufacturer|country|target|brewery|cai_scale|traffic_sign/; + +// Returns whether a `colour` tag value looks like a valid color we can display +export function isColourValid(value) { + if (!value.match(/^(#([0-9a-fA-F]{3}){1,2}|\w+)$/)) { + // OSM only supports hex or named colors + return false; + } + if (!CSS.supports('color', value) || ['unset', 'inherit', 'initial', 'revert'].includes(value)) { + // see https://stackoverflow.com/a/68217760/1627467 + return false; + } + return true; +} diff --git a/modules/ui/feature_list.js b/modules/ui/feature_list.js index f4062c73f..fe004bf68 100644 --- a/modules/ui/feature_list.js +++ b/modules/ui/feature_list.js @@ -11,6 +11,7 @@ import { geoSphericalDistance } from '../geo/geo'; import { geoExtent } from '../geo'; import { modeSelect } from '../modes/select'; import { osmEntity } from '../osm/entity'; +import { isColourValid } from '../osm/tags'; import { services } from '../services'; import { svgIcon } from '../svg/icon'; import { uiCmd } from './cmd'; @@ -310,7 +311,7 @@ export function uiFeatureList(context) { label .append('span') .attr('class', 'entity-name') - .classed('has-colour', d => d.entity && d.entity.type === 'relation' && d.entity.tags.colour) + .classed('has-colour', d => d.entity && d.entity.type === 'relation' && d.entity.tags.colour && isColourValid(d.entity.tags.colour)) .style('border-color', d => d.entity && d.entity.type === 'relation' && d.entity.tags.colour) .text(function(d) { return d.name; }); diff --git a/modules/ui/fields/input.js b/modules/ui/fields/input.js index 92a62d302..14f05cd26 100644 --- a/modules/ui/fields/input.js +++ b/modules/ui/fields/input.js @@ -9,6 +9,7 @@ import { t, localizer } from '../../core/localizer'; import { utilDetect, utilGetSetValue, utilNoAuto, utilRebind, utilTotalExtent } from '../../util'; import { svgIcon } from '../../svg/icon'; import { cardinal } from '../../osm/node'; +import { isColourValid } from '../../osm/tags'; import { uiLengthIndicator } from '..'; import { uiTooltip } from '../tooltip'; import { isEqual } from 'lodash-es'; @@ -228,16 +229,6 @@ export function uiFieldText(field, context) { function updateColourPreview() { - function isColourValid(colour) { - if (!colour.match(/^(#([0-9a-fA-F]{3}){1,2}|\w+)$/)) { - // OSM only supports hex or named colors - return false; - } else if (!CSS.supports('color', colour) || ['unset', 'inherit', 'initial', 'revert'].includes(colour)) { - // see https://stackoverflow.com/a/68217760/1627467 - return false; - } - return true; - } wrap.selectAll('.colour-preview') .remove(); diff --git a/modules/ui/sections/raw_member_editor.js b/modules/ui/sections/raw_member_editor.js index 9ad0901a9..e15712f6d 100644 --- a/modules/ui/sections/raw_member_editor.js +++ b/modules/ui/sections/raw_member_editor.js @@ -11,6 +11,7 @@ import { actionMoveMember } from '../../actions/move_member'; import { modeBrowse } from '../../modes/browse'; import { modeSelect } from '../../modes/select'; import { osmEntity } from '../../osm'; +import { isColourValid } from '../../osm/tags'; import { svgIcon } from '../../svg/icon'; import { services } from '../../services'; import { uiCombobox } from '../combobox'; @@ -198,7 +199,7 @@ export function uiSectionRawMemberEditor(context) { labelLink .append('span') .attr('class', 'member-entity-name') - .classed('has-colour', d => d.member.type === 'relation' && d.member.tags.colour) + .classed('has-colour', d => d.member.type === 'relation' && d.member.tags.colour && isColourValid(d.member.tags.colour)) .style('border-color', d => d.member.type === 'relation' && d.member.tags.colour) .text(function(d) { return utilDisplayName(d.member); }); diff --git a/modules/ui/sections/raw_membership_editor.js b/modules/ui/sections/raw_membership_editor.js index 3e1e250e3..3ef210d7e 100644 --- a/modules/ui/sections/raw_membership_editor.js +++ b/modules/ui/sections/raw_membership_editor.js @@ -12,6 +12,7 @@ import { actionDeleteMembers } from '../../actions/delete_members'; import { modeSelect } from '../../modes/select'; import { osmEntity, osmRelation } from '../../osm'; +import { isColourValid } from '../../osm/tags'; import { services } from '../../services'; import { svgIcon } from '../../svg/icon'; import { uiCombobox } from '../combobox'; @@ -258,7 +259,7 @@ export function uiSectionRawMembershipEditor(context) { .text(presetName + ' '); selection .append('span') - .classed('has-colour', entity.tags.colour) + .classed('has-colour', entity.tags.colour && isColourValid(entity.tags.colour)) .style('border-color', entity.tags.colour) .text(entityName); }; @@ -371,7 +372,7 @@ export function uiSectionRawMembershipEditor(context) { labelLink .append('span') .attr('class', 'member-entity-name') - .classed('has-colour', d => d.relation.tags.colour) + .classed('has-colour', d => d.relation.tags.colour && isColourValid(d.relation.tags.colour)) .style('border-color', d => d.relation.tags.colour) .text(function(d) { return utilDisplayName(d.relation); });