Files
iD/modules/ui/fields/access.js
Tobias ce51b33cf2 Improve combobox dropdown styling to give labels more room and readability (#10127)
* Fields: Break long lines in combo dropdown instead of ellipsis

Longer translations keys are not readable with ellipsis and the tooltip only shows the description (or fallback), not the title.

This change makes long use line breaks with small line height to make the whole text visible.

* Fields: Use table styles for rows to maximize available space

The flex box solution gave a lot of space to the label. This solution minimizes that space to give more space to the values dropdown.
2024-02-26 14:02:22 +01:00

334 lines
9.7 KiB
JavaScript

import { dispatch as d3_dispatch } from 'd3-dispatch';
import { select as d3_select } from 'd3-selection';
import { uiCombobox } from '../combobox';
import { utilGetSetValue, utilNoAuto, utilRebind } from '../../util';
import { t } from '../../core/localizer';
export function uiFieldAccess(field, context) {
var dispatch = d3_dispatch('change');
var items = d3_select(null);
var _tags;
function access(selection) {
var wrap = selection.selectAll('.form-field-input-wrap')
.data([0]);
wrap = wrap.enter()
.append('div')
.attr('class', 'form-field-input-wrap form-field-input-' + field.type)
.merge(wrap);
var list = wrap.selectAll('ul')
.data([0]);
list = list.enter()
.append('ul')
.attr('class', 'rows')
.merge(list);
items = list.selectAll('li')
.data(field.keys);
// Enter
var enter = items.enter()
.append('li')
.attr('class', function(d) { return 'labeled-input preset-access-' + d; });
enter
.append('div')
.attr('class', 'label preset-label-access')
.attr('for', function(d) { return 'preset-input-access-' + d; })
.html(function(d) { return field.t.html('types.' + d); });
enter
.append('div')
.attr('class', 'preset-input-access-wrap')
.append('input')
.attr('type', 'text')
.attr('class', function(d) { return 'preset-input-access preset-input-access-' + d; })
.call(utilNoAuto)
.each(function(d) {
d3_select(this)
.call(uiCombobox(context, 'access-' + d)
.data(access.options(d))
);
});
// Update
items = items.merge(enter);
wrap.selectAll('.preset-input-access')
.on('change', change)
.on('blur', change);
}
function change(d3_event, d) {
var tag = {};
var value = context.cleanTagValue(utilGetSetValue(d3_select(this)));
// don't override multiple values with blank string
if (!value && typeof _tags[d] !== 'string') return;
tag[d] = value || undefined;
dispatch.call('change', this, tag);
}
access.options = function(type) {
var options = [
'yes',
'no',
'designated',
'permissive',
'destination',
'customers',
'private',
'permit',
'unknown'
];
if (type === 'access') {
options = options.filter(v => v !== 'yes' && v !== 'designated');
}
if (type === 'bicycle') {
options.splice(options.length - 4, 0, 'dismount');
}
var stringsField = field.resolveReference('stringsCrossReference');
return options.map(function(option) {
return {
title: stringsField.t('options.' + option + '.description'),
value: option
};
});
};
const placeholdersByTag = {
highway: {
footway: {
foot: 'designated',
motor_vehicle: 'no'
},
steps: {
foot: 'yes',
motor_vehicle: 'no',
bicycle: 'no',
horse: 'no'
},
pedestrian: {
foot: 'yes',
motor_vehicle: 'no'
},
cycleway: {
motor_vehicle: 'no',
bicycle: 'designated'
},
bridleway: {
motor_vehicle: 'no',
horse: 'designated'
},
path: {
foot: 'yes',
motor_vehicle: 'no',
bicycle: 'yes',
horse: 'yes'
},
motorway: {
foot: 'no',
motor_vehicle: 'yes',
bicycle: 'no',
horse: 'no'
},
trunk: {
motor_vehicle: 'yes'
},
primary: {
foot: 'yes',
motor_vehicle: 'yes',
bicycle: 'yes',
horse: 'yes'
},
secondary: {
foot: 'yes',
motor_vehicle: 'yes',
bicycle: 'yes',
horse: 'yes'
},
tertiary: {
foot: 'yes',
motor_vehicle: 'yes',
bicycle: 'yes',
horse: 'yes'
},
residential: {
foot: 'yes',
motor_vehicle: 'yes',
bicycle: 'yes',
horse: 'yes'
},
unclassified: {
foot: 'yes',
motor_vehicle: 'yes',
bicycle: 'yes',
horse: 'yes'
},
service: {
foot: 'yes',
motor_vehicle: 'yes',
bicycle: 'yes',
horse: 'yes'
},
motorway_link: {
foot: 'no',
motor_vehicle: 'yes',
bicycle: 'no',
horse: 'no'
},
trunk_link: {
motor_vehicle: 'yes'
},
primary_link: {
foot: 'yes',
motor_vehicle: 'yes',
bicycle: 'yes',
horse: 'yes'
},
secondary_link: {
foot: 'yes',
motor_vehicle: 'yes',
bicycle: 'yes',
horse: 'yes'
},
tertiary_link: {
foot: 'yes',
motor_vehicle: 'yes',
bicycle: 'yes',
horse: 'yes'
},
construction: {
access: 'no'
},
busway: {
access: 'no',
bus: 'designated',
emergency: 'yes',
}
},
barrier: {
bollard: {
access: 'no',
bicycle: 'yes',
foot: 'yes'
},
bus_trap: {
motor_vehicle: 'no',
psv: 'yes',
foot: 'yes',
bicycle: 'yes'
},
city_wall: {
access: 'no'
},
coupure: {
access: 'yes'
},
cycle_barrier: {
motor_vehicle: 'no'
},
ditch: {
access: 'no'
},
entrance: {
access: 'yes'
},
fence: {
access: 'no'
},
hedge: {
access: 'no'
},
jersey_barrier: {
access: 'no'
},
motorcycle_barrier: {
motor_vehicle: 'no'
},
rail_guard: {
access: 'no'
}
}
};
access.tags = function(tags) {
_tags = tags;
utilGetSetValue(items.selectAll('.preset-input-access'), function(d) {
return typeof tags[d] === 'string' ? tags[d] : '';
})
.classed('mixed', function(d) {
return tags[d] && Array.isArray(tags[d]);
})
.attr('title', function(d) {
return tags[d] && Array.isArray(tags[d]) && tags[d].filter(Boolean).join('\n');
})
.attr('placeholder', function(d) {
if (tags[d] && Array.isArray(tags[d])) {
return t('inspector.multiple_values');
}
if (d === 'bicycle' || d === 'motor_vehicle') {
if (tags.vehicle && typeof tags.vehicle === 'string') {
return tags.vehicle;
}
}
if (tags.access && typeof tags.access === 'string') {
return tags.access;
}
function getPlaceholdersByTag(key, placeholdersByKey) {
if (typeof tags[key] === 'string') {
if (placeholdersByKey[tags[key]] &&
placeholdersByKey[tags[key]][d]) {
return placeholdersByKey[tags[key]][d];
}
} else {
var impliedAccesses = tags[key].filter(Boolean).map(function(val) {
return placeholdersByKey[val] && placeholdersByKey[val][d];
}).filter(Boolean);
if (impliedAccesses.length === tags[key].length &&
new Set(impliedAccesses).size === 1) {
// if all the barrier values have the same implied access for this type then use that
return impliedAccesses[0];
}
}
}
for (const key in placeholdersByTag) {
if (tags[key]) {
const impliedAccess = getPlaceholdersByTag(key, placeholdersByTag[key]);
if (impliedAccess) {
return impliedAccess;
}
}
}
if (d === 'access' && !tags.barrier) {
return 'yes';
}
return field.placeholder();
});
};
access.focus = function() {
items.selectAll('.preset-input-access')
.node().focus();
};
return utilRebind(access, dispatch, 'on');
}