diff --git a/css/80_app.css b/css/80_app.css index ece0e1834..fb49f255c 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -1266,6 +1266,7 @@ a.hide-toggle { border-top: 0; border-radius: 0 0 4px 4px; overflow: hidden; + position: relative; } .form-field textarea { @@ -1988,7 +1989,7 @@ div.combobox { } .combobox-caret::after { - content:""; + content: ""; height: 0; width: 0; position: absolute; left: 0; right: 0; bottom: 0; top: 0; @@ -1998,6 +1999,20 @@ div.combobox { border-right: 5px solid transparent; } +/* Field Help */ + +.field-help-body { + display: block; + position: absolute; + overflow: hidden; + top: 0; + width: 100%; + height: 100%; + z-index: 2; + padding: 10px; + background: rgba(255,255,255,0.95); +} + /* Raw Tag Editor */ .tag-list { diff --git a/modules/ui/field.js b/modules/ui/field.js index bffaf4a9d..eee682068 100644 --- a/modules/ui/field.js +++ b/modules/ui/field.js @@ -11,6 +11,7 @@ import { import { textDirection } from '../util/locale'; import { svgIcon } from '../svg'; +import { uiFieldHelp } from './field_help'; import { uiFields } from './fields'; import { uiTagReference } from './tag_reference'; import { utilRebind } from '../util'; @@ -25,11 +26,11 @@ export function uiField(context, presetField, entity, options) { info: true }, options); - var dispatch = d3_dispatch('change'), - field = _clone(presetField), - show = options.show, - state = '', - tags = {}; + var dispatch = d3_dispatch('change'); + var field = _clone(presetField); + var _show = options.show; + var _state = ''; + var _tags = {}; field.impl = uiFields[field.type](field, context) @@ -48,14 +49,14 @@ export function uiField(context, presetField, entity, options) { if (!entity) return false; var original = context.graph().base().entities[entity.id]; return _some(field.keys, function(key) { - return original ? tags[key] !== original.tags[key] : tags[key]; + return original ? _tags[key] !== original.tags[key] : _tags[key]; }); } function isPresent() { return _some(field.keys, function(key) { - return tags[key]; + return _tags[key]; }); } @@ -65,8 +66,8 @@ export function uiField(context, presetField, entity, options) { d3_event.preventDefault(); if (!entity) return false; - var original = context.graph().base().entities[entity.id], - t = {}; + var original = context.graph().base().entities[entity.id]; + var t = {}; d.keys.forEach(function(key) { t[key] = original ? original.tags[key] : undefined; }); @@ -143,14 +144,25 @@ export function uiField(context, presetField, entity, options) { .classed('modified', isModified()) .classed('present', isPresent()) .each(function(d) { + var reference, help; + + // instantiate field help + if (options.wrap && field.type === 'restrictions') { + help = uiFieldHelp('restrictions'); + if (_state === 'hover') { + help.showing(false); + } + } + + // instantiate tag reference if (options.wrap && options.info) { var referenceKey = d.key; if (d.type === 'multiCombo') { // lookup key without the trailing ':' referenceKey = referenceKey.replace(/:$/, ''); } - var reference = uiTagReference(d.reference || { key: referenceKey }, context); - if (state === 'hover') { + reference = uiTagReference(d.reference || { key: referenceKey }, context); + if (_state === 'hover') { reference.showing(false); } } @@ -158,35 +170,44 @@ export function uiField(context, presetField, entity, options) { d3_select(this) .call(d.impl); - if (options.wrap && options.info) { + // add field help components + if (help) { + d3_select(this) + .call(help.body) + .select('.form-label-button-wrap') + .call(help.button); + } + + // add tag reference components + if (reference) { d3_select(this) .call(reference.body) .select('.form-label-button-wrap') .call(reference.button); } - d.impl.tags(tags); + d.impl.tags(_tags); }); }; field.state = function(_) { - if (!arguments.length) return state; - state = _; + if (!arguments.length) return _state; + _state = _; return field; }; field.tags = function(_) { - if (!arguments.length) return tags; - tags = _; + if (!arguments.length) return _tags; + _tags = _; return field; }; field.show = function() { - show = true; - if (field.default && field.key && tags[field.key] !== field.default) { + _show = true; + if (field.default && field.key && _tags[field.key] !== field.default) { var t = {}; t[field.key] = field.default; dispatch.call('change', this, t); @@ -195,7 +216,7 @@ export function uiField(context, presetField, entity, options) { field.isShown = function() { - return show || _some(field.keys, function(key) { return !!tags[key]; }); + return _show || _some(field.keys, function(key) { return !!_tags[key]; }); }; diff --git a/modules/ui/field_help.js b/modules/ui/field_help.js new file mode 100644 index 000000000..71aebf076 --- /dev/null +++ b/modules/ui/field_help.js @@ -0,0 +1,101 @@ +import { + event as d3_event, + select as d3_select +} from 'd3-selection'; + +import { t } from '../util/locale'; +import { svgIcon } from '../svg'; + + +export function uiFieldHelp(fieldName) { + var fieldHelp = {}; + var _body = d3_select(null); + var _showing; + + + function show() { + _body + .classed('hide', false) + .transition() + .duration(200) + .style('height', '100%'); + + _showing = true; + } + + + function hide() { + _body + .transition() + .duration(200) + .style('height', '0px') + .on('end', function () { + _body.classed('hide', true); + }); + + _showing = false; + } + + + fieldHelp.button = function(selection) { + var button = selection.selectAll('.field-help-button') + .data([0]); + + // enter/update + button.enter() + .append('button') + .attr('class', 'field-help-button') + .attr('tabindex', -1) + .call(svgIcon('#icon-help')) + .merge(button) + .on('click', function () { + d3_event.stopPropagation(); + d3_event.preventDefault(); + if (_showing) { + hide(); + } else { + show(); + } + }); + }; + + + fieldHelp.body = function(selection) { + // this control expects the field to have a preset-input-wrap div + var wrap = selection.selectAll('.preset-input-wrap'); + if (wrap.empty()) return; + + _body = wrap.selectAll('.field-help-body') + .data([0]); + + var enter = _body.enter() + .append('div') + .attr('class', 'field-help-body cf hide') + .style('height', '0px'); + +//debug +for (var i = 0; i < 15; i++) { + enter + .append('p') + .attr('class', 'field-help-description') + .text('lorem ipsum'); +} + + _body = _body + .merge(enter); + + if (_showing === false) { + hide(); + } + }; + + + fieldHelp.showing = function(_) { + if (!arguments.length) return _showing; + _showing = _; + return fieldHelp; + }; + + + return fieldHelp; +} diff --git a/modules/ui/index.js b/modules/ui/index.js index 8a3dd80c6..d908b7078 100644 --- a/modules/ui/index.js +++ b/modules/ui/index.js @@ -19,6 +19,7 @@ export { uiEntityEditor } from './entity_editor'; export { uiFeatureInfo } from './feature_info'; export { uiFeatureList } from './feature_list'; export { uiField } from './field'; +export { uiFieldHelp } from './field_help'; export { uiFlash } from './flash'; export { uiFormFields } from './form_fields'; export { uiFullScreen } from './full_screen'; diff --git a/modules/ui/tag_reference.js b/modules/ui/tag_reference.js index 4c5b39dcd..81cf4659b 100644 --- a/modules/ui/tag_reference.js +++ b/modules/ui/tag_reference.js @@ -13,17 +13,18 @@ import { svgIcon } from '../svg'; export function uiTagReference(tag) { - var taginfo = services.taginfo, - tagReference = {}, - button = d3_select(null), - body = d3_select(null), - loaded, - showing; + var taginfo = services.taginfo; + var tagReference = {}; + + var _button = d3_select(null); + var _body = d3_select(null); + var _loaded; + var _showing; function findLocal(data) { - var locale = utilDetect().locale.toLowerCase(), - localized; + var locale = utilDetect().locale.toLowerCase(); + var localized; if (locale !== 'pt-br') { // see #3776, prefer 'pt' over 'pt-br' localized = _find(data, function(d) { @@ -52,7 +53,7 @@ export function uiTagReference(tag) { function load(param) { if (!taginfo) return; - button + _button .classed('tag-reference-loading', true); taginfo.docs(param, function show(err, data) { @@ -61,13 +62,13 @@ export function uiTagReference(tag) { docs = findLocal(data); } - body.html(''); + _body.html(''); if (!docs || !docs.title) { if (param.hasOwnProperty('value')) { load(_omit(param, 'value')); // retry with key only } else { - body + _body .append('p') .attr('class', 'tag-reference-description') .text(t('inspector.no_documentation_key')); @@ -77,7 +78,7 @@ export function uiTagReference(tag) { } if (docs.image && docs.image.thumb_url_prefix) { - body + _body .append('img') .attr('class', 'tag-reference-wiki-image') .attr('src', docs.image.thumb_url_prefix + '100' + docs.image.thumb_url_suffix) @@ -87,12 +88,12 @@ export function uiTagReference(tag) { done(); } - body + _body .append('p') .attr('class', 'tag-reference-description') .text(docs.description || t('inspector.documentation_redirect')); - body + _body .append('a') .attr('class', 'tag-reference-link') .attr('target', '_blank') @@ -104,7 +105,7 @@ export function uiTagReference(tag) { // Add link to info about "good changeset comments" - #2923 if (param.key === 'comment') { - body + _body .append('a') .attr('class', 'tag-reference-comment-link') .attr('target', '_blank') @@ -119,54 +120,54 @@ export function uiTagReference(tag) { function done() { - loaded = true; + _loaded = true; - button + _button .classed('tag-reference-loading', false); - body + _body .classed('expanded', true) .transition() .duration(200) .style('max-height', '200px') .style('opacity', '1'); - showing = true; + _showing = true; } function hide() { - body + _body .transition() .duration(200) .style('max-height', '0px') .style('opacity', '0') .on('end', function () { - body.classed('expanded', false); + _body.classed('expanded', false); }); - showing = false; + _showing = false; } tagReference.button = function(selection) { - button = selection.selectAll('.tag-reference-button') + _button = selection.selectAll('.tag-reference-button') .data([0]); - button = button.enter() + _button = _button.enter() .append('button') .attr('class', 'tag-reference-button') .attr('tabindex', -1) .call(svgIcon('#icon-inspect')) - .merge(button); + .merge(_button); - button + _button .on('click', function () { d3_event.stopPropagation(); d3_event.preventDefault(); - if (showing) { + if (_showing) { hide(); - } else if (loaded) { + } else if (_loaded) { done(); } else { load(tag); @@ -176,31 +177,29 @@ export function uiTagReference(tag) { tagReference.body = function(selection) { - var tagid = tag.rtype || (tag.key + '-' + tag.value); - - body = selection.selectAll('.tag-reference-body') + _body = selection.selectAll('.tag-reference-body') .data([tagid], function(d) { return d; }); - body.exit() + _body.exit() .remove(); - body = body.enter() + _body = _body.enter() .append('div') .attr('class', 'tag-reference-body cf') .style('max-height', '0') .style('opacity', '0') - .merge(body); + .merge(_body); - if (showing === false) { + if (_showing === false) { hide(); } }; tagReference.showing = function(_) { - if (!arguments.length) return showing; - showing = _; + if (!arguments.length) return _showing; + _showing = _; return tagReference; };