diff --git a/css/80_app.css b/css/80_app.css index 0894a06fd..958b0e24d 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -717,6 +717,7 @@ button.add-note svg.icon { } .field-help-title button.close, +.sidebar-component .header button.data-editor-close, .sidebar-component .header button.note-editor-close, .entity-editor-pane .header button.preset-close, .preset-list-pane .header button.preset-choose { @@ -725,6 +726,7 @@ button.add-note svg.icon { top: 0; } [dir='rtl'] .field-help-title button.close, +[dir='rtl'] .sidebar-component .header button.data-editor-close, [dir='rtl'] .sidebar-component .header button.note-editor-close, [dir='rtl'] .entity-editor-pane .header button.preset-close, [dir='rtl'] .preset-list-pane .header button.preset-choose { @@ -1392,6 +1394,10 @@ a.hide-toggle { .inspector-hover .tag-row .form-field.input-wrap-position { width: 50%; } +.inspector-hover .tag-row .key-wrap, +.inspector-hover .tag-row .input-wrap-position { + height: 31px; +} .inspector-hover .tag-row:first-child input.value { border-top-right-radius: 4px; @@ -2541,6 +2547,57 @@ input.key-trap { } +/* Map Data Inspector */ +.data-header { + background-color: #f6f6f6; + border-radius: 5px; + border: 1px solid #ccc; + display: flex; + flex-flow: row nowrap; + align-items: center; +} + +.data-header-icon { + background-color: #fff; + padding: 10px; + flex: 0 0 62px; + position: relative; + width: 60px; + height: 60px; + border-right: 1px solid #ccc; + border-radius: 5px 0 0 5px; +} +[dir='rtl'] .data-header-icon { + border-right: unset; + border-left: 1px solid #ccc; + border-radius: 0 5px 5px 0; +} + +.data-header-icon .icon-wrap { + position: absolute; + top: 0px; +} + +.data-header-label { + background-color: #f6f6f6; + padding: 0 15px; + flex: 1 1 100%; + font-size: 14px; + font-weight: bold; + border-radius: 0 5px 5px 0; +} +[dir='rtl'] .data-header-label { + border-radius: 5px 0 0 5px; +} + +/* tag editor - no buttons */ +.data-editor.raw-tag-editor button { + display: none; +} +.data-editor.raw-tag-editor .tag-row .input-wrap-position { + width: 50%; +} + /* Fullscreen button */ div.full-screen { diff --git a/modules/behavior/hover.js b/modules/behavior/hover.js index 9b2000e62..99d12a9dc 100644 --- a/modules/behavior/hover.js +++ b/modules/behavior/hover.js @@ -6,10 +6,7 @@ import { } from 'd3-selection'; import { d3keybinding as d3_keybinding } from '../lib/d3.keybinding.js'; -import { - osmEntity, - osmNote -} from '../osm'; +import { osmEntity, osmNote } from '../osm'; import { utilRebind } from '../util/rebind'; @@ -154,7 +151,6 @@ export function behaviorHover(context) { dispatch.call('hover', this, null); } } - }; diff --git a/modules/ui/data_editor.js b/modules/ui/data_editor.js new file mode 100644 index 000000000..9adf0a528 --- /dev/null +++ b/modules/ui/data_editor.js @@ -0,0 +1,79 @@ +import { t } from '../util/locale'; +import { modeBrowse } from '../modes'; +import { svgIcon } from '../svg'; + +import { + uiDataHeader, + uiRawTagEditor +} from './index'; + + +export function uiDataEditor(context) { + var dataHeader = uiDataHeader(); + var rawTagEditor = uiRawTagEditor(context); + var _datum; + + + function dataEditor(selection) { + var header = selection.selectAll('.header') + .data([0]); + + var headerEnter = header.enter() + .append('div') + .attr('class', 'header fillL'); + + headerEnter + .append('button') + .attr('class', 'fr data-editor-close') + .on('click', function() { + context.enter(modeBrowse(context)); + }) + .call(svgIcon('#iD-icon-close')); + + headerEnter + .append('h3') + .text(t('map_data.title')); + + + var body = selection.selectAll('.body') + .data([0]); + + body = body.enter() + .append('div') + .attr('class', 'body') + .merge(body); + + var editor = body.selectAll('.data-editor') + .data([0]); + + editor = editor.enter() + .append('div') + .attr('class', 'modal-section data-editor') + .merge(editor) + .call(dataHeader.datum(_datum)); + + var rte = body.selectAll('.raw-tag-editor') + .data([0]); + + rte.enter() + .append('div') + .attr('class', 'inspector-border raw-tag-editor inspector-inner data-editor') + .merge(rte) + .call(rawTagEditor + .expanded(true) + .readOnlyTags([/./]) + .tags((_datum && _datum.properties) || {}) + .state('hover') + ); + } + + + dataEditor.datum = function(val) { + if (!arguments.length) return _datum; + _datum = val; + return this; + }; + + + return dataEditor; +} diff --git a/modules/ui/data_header.js b/modules/ui/data_header.js new file mode 100644 index 000000000..fec0026af --- /dev/null +++ b/modules/ui/data_header.js @@ -0,0 +1,47 @@ +import { t } from '../util/locale'; +import { svgIcon } from '../svg'; + + +export function uiDataHeader() { + var _datum; + + + function dataHeader(selection) { + var header = selection.selectAll('.data-header') + .data( + (_datum ? [_datum] : []), + function(d) { return d.__featurehash__; } + ); + + header.exit() + .remove(); + + var headerEnter = header.enter() + .append('div') + .attr('class', 'data-header'); + + var iconEnter = headerEnter + .append('div') + .attr('class', 'data-header-icon'); + + iconEnter + .append('div') + .attr('class', 'preset-icon-28') + .call(svgIcon('#iD-icon-data', 'note-fill')); + + headerEnter + .append('div') + .attr('class', 'data-header-label') + .text(t('map_data.layers.custom.title')); + } + + + dataHeader.datum = function(val) { + if (!arguments.length) return _datum; + _datum = val; + return this; + }; + + + return dataHeader; +} diff --git a/modules/ui/index.js b/modules/ui/index.js index 35d9a8517..3c2641519 100644 --- a/modules/ui/index.js +++ b/modules/ui/index.js @@ -13,6 +13,8 @@ export { uiConfirm } from './confirm'; export { uiConflicts } from './conflicts'; export { uiContributors } from './contributors'; export { uiCurtain } from './curtain'; +export { uiDataEditor } from './data_editor'; +export { uiDataHeader } from './data_header'; export { uiDisclosure } from './disclosure'; export { uiEditMenu } from './edit_menu'; export { uiEntityEditor } from './entity_editor'; diff --git a/modules/ui/raw_tag_editor.js b/modules/ui/raw_tag_editor.js index 85a7f786e..d5a4fb23a 100644 --- a/modules/ui/raw_tag_editor.js +++ b/modules/ui/raw_tag_editor.js @@ -24,17 +24,17 @@ import { export function uiRawTagEditor(context) { - var taginfo = services.taginfo, - dispatch = d3_dispatch('change'), - _readOnlyTags = [], - _showBlank = false, - _updatePreference = true, - _expanded = false, - _newRow, - _state, - _preset, - _tags, - _entityID; + var taginfo = services.taginfo; + var dispatch = d3_dispatch('change'); + var _readOnlyTags = []; + var _showBlank = false; + var _updatePreference = true; + var _expanded = false; + var _newRow; + var _state; + var _preset; + var _tags; + var _entityID; function rawTagEditor(selection) { @@ -148,16 +148,16 @@ export function uiRawTagEditor(context) { items .each(function(tag) { - var row = d3_select(this), - key = row.select('input.key'), // propagate bound data to child - value = row.select('input.value'); // propagate bound data to child + var row = d3_select(this); + var key = row.select('input.key'); // propagate bound data to child + var value = row.select('input.value'); // propagate bound data to child if (_entityID && taginfo) { bindTypeahead(key, value); } - var isRelation = (_entityID && context.entity(_entityID).type === 'relation'), - reference; + var isRelation = (_entityID && context.entity(_entityID).type === 'relation'); + var reference; if (isRelation && tag.key === 'type') { reference = uiTagReference({ rtype: tag.value }, context); @@ -239,8 +239,8 @@ export function uiRawTagEditor(context) { function sort(value, data) { - var sameletter = [], - other = []; + var sameletter = []; + var other = []; for (var i = 0; i < data.length; i++) { if (data[i].value.substring(0, value.length) === value) { sameletter.push(data[i]); @@ -265,10 +265,9 @@ export function uiRawTagEditor(context) { function keyChange(d) { - var kOld = d.key, - kNew = this.value.trim(), - tag = {}; - + var kOld = d.key; + var kNew = this.value.trim(); + var tag = {}; if (isReadOnly({ key: kNew })) { this.value = kOld; @@ -276,17 +275,17 @@ export function uiRawTagEditor(context) { } if (kNew && kNew !== kOld) { - var match = kNew.match(/^(.*?)(?:_(\d+))?$/), - base = match[1], - suffix = +(match[2] || 1); + var match = kNew.match(/^(.*?)(?:_(\d+))?$/); + var base = match[1]; + var suffix = +(match[2] || 1); while (_tags[kNew]) { // rename key if already in use kNew = base + '_' + suffix++; } if (_includes(kNew, '=')) { - var splitStr = kNew.split('=').map(function(str) { return str.trim(); }), - key = splitStr[0], - value = splitStr[1]; + var splitStr = kNew.split('=').map(function(str) { return str.trim(); }); + var key = splitStr[0]; + var value = splitStr[1]; kNew = key; d.value = value; @@ -295,9 +294,9 @@ export function uiRawTagEditor(context) { tag[kOld] = undefined; tag[kNew] = d.value; - d.key = kNew; // Maintain DOM identity through the subsequent update. + d.key = kNew; // Maintain DOM identity through the subsequent update. - if (_newRow === kOld) { // see if this row is still a new row + if (_newRow === kOld) { // see if this row is still a new row _newRow = ((d.value === '' || kNew === '') ? kNew : undefined); } diff --git a/modules/ui/sidebar.js b/modules/ui/sidebar.js index cb972eb10..33b3df5c0 100644 --- a/modules/ui/sidebar.js +++ b/modules/ui/sidebar.js @@ -2,16 +2,25 @@ import _throttle from 'lodash-es/throttle'; import { selectAll as d3_selectAll } from 'd3-selection'; -import { osmEntity, osmNote } from '../osm'; -import { uiFeatureList } from './feature_list'; -import { uiInspector } from './inspector'; -import { uiNoteEditor } from './note_editor'; +import { + osmEntity, + osmNote +} from '../osm'; + +import { + uiDataEditor, + uiFeatureList, + uiInspector, + uiNoteEditor +} from './index'; export function uiSidebar(context) { var inspector = uiInspector(context); + var dataEditor = uiDataEditor(context); var noteEditor = uiNoteEditor(context); var _current; + var _wasData = false; var _wasNote = false; @@ -28,8 +37,12 @@ export function uiSidebar(context) { function hover(datum) { if (datum && datum.__featurehash__) { // hovering on data - console.log ('hover on data ' + datum.__featurehash__); - // show something + _wasData = true; + sidebar + .show(dataEditor.datum(datum)); + + selection.selectAll('.sidebar-component') + .classed('inspector-hover', true); } else if (datum instanceof osmNote) { if (context.mode().id === 'drag-note') return; @@ -66,10 +79,10 @@ export function uiSidebar(context) { inspector .state('hide'); - } else if (_wasNote) { + } else if (_wasData || _wasNote) { _wasNote = false; - d3_selectAll('.note') - .classed('hover', false); + _wasData = false; + d3_selectAll('.note').classed('hover', false); sidebar.hide(); } }