diff --git a/css/30_highways.css b/css/30_highways.css index 2122135889..05f18df067 100644 --- a/css/30_highways.css +++ b/css/30_highways.css @@ -1,13 +1,15 @@ -preset-icon-container/* highways */ +/* preset-icon-container highways */ /* defaults */ .preset-icon .icon.tag-highway.other-line { color: #fff; fill: #777; } + path.line.casing.tag-highway { stroke: #444; } + path.line.stroke.tag-highway { stroke: #ccc; } diff --git a/modules/ui/fields/textarea.js b/modules/ui/fields/textarea.js index 223121daeb..019480363f 100644 --- a/modules/ui/fields/textarea.js +++ b/modules/ui/fields/textarea.js @@ -8,18 +8,18 @@ import { utilRebind } from '../../util'; import { uiLengthIndicator } from '..'; - +import { svgIcon } from '../../svg/icon'; // ← ADDED export function uiFieldTextarea(field, context) { var dispatch = d3_dispatch('change'); var input = d3_select(null); + var wrap = d3_select(null); // ← ADDED var _lengthIndicator = uiLengthIndicator(context.maxCharsForTagValue()) .silent(field.usage === 'changeset' && field.key === 'comment'); var _tags; - function textarea(selection) { - var wrap = selection.selectAll('.form-field-input-wrap') + wrap = selection.selectAll('.form-field-input-wrap') .data([0]); wrap = wrap.enter() @@ -36,15 +36,24 @@ export function uiFieldTextarea(field, context) { .attr('dir', 'auto') .attr('id', field.domId) .call(utilNoAuto) - .on('input', change(true)) - .on('blur', change()) - .on('change', change()) + .on('input', function () { + change(true)(); + updatePatternValidation(); // ← ADDED + }) + .on('blur', function () { + change()(); + updatePatternValidation(); // ← ADDED + }) + .on('change', function () { + change()(); + updatePatternValidation(); // ← ADDED + }) .merge(input); wrap.call(_lengthIndicator); function change(onInput) { - return function() { + return function () { var val = utilGetSetValue(input); if (!onInput) val = context.cleanTagValue(val); @@ -58,9 +67,47 @@ export function uiFieldTextarea(field, context) { }; } } + + function updatePatternValidation() { + if (!field.pattern || !wrap || wrap.empty()) return; + + const value = utilGetSetValue(input).trim(); + + if (!value) { + wrap.selectAll('.form-field-pattern-info').remove(); + return; + } + + let isInvalid = false; + try { + const regex = new RegExp(field.pattern); + isInvalid = !regex.test(value); + } catch { + return; + } + const info = wrap.selectAll('.form-field-pattern-info') + .data(isInvalid ? [0] : []); - textarea.tags = function(tags) { + const enter = info.enter() + .append('div') + .attr('class', 'form-field-pattern-info'); + + enter + .append('span') + .call(svgIcon('#iD-icon-info')); + + enter + .append('span') + .attr('class', 'form-field-pattern-info-text') + .text(t('inspector.invalid_format')); + + info.exit().remove(); + } + // ============================ + + + textarea.tags = function (tags) { _tags = tags; var isMixed = Array.isArray(tags[field.key]); @@ -73,10 +120,12 @@ export function uiFieldTextarea(field, context) { if (!isMixed) { _lengthIndicator.update(tags[field.key]); } + + updatePatternValidation(); }; - textarea.focus = function() { + textarea.focus = function () { input.node().focus(); }; diff --git a/modules/validations/crossing_ways.js b/modules/validations/crossing_ways.js index 3f2e27123f..5ca3aa149c 100644 --- a/modules/validations/crossing_ways.js +++ b/modules/validations/crossing_ways.js @@ -841,4 +841,4 @@ export function validationCrossingWays(context) { validation.type = type; return validation; -} +} \ No newline at end of file