diff --git a/css/app.css b/css/app.css index ba36d4625..6a7c9a920 100644 --- a/css/app.css +++ b/css/app.css @@ -1163,8 +1163,10 @@ button.save.has-count .count::before { font-weight: bold; } -.more-fields label { padding: 5px 10px 5px 0; } -.more-fields input { width: 50%;} +.more-fields input { + margin-left: 10px; + width: 50%; +} /* preset form access */ diff --git a/modules/lib/d3.combobox.js b/modules/lib/d3.combobox.js index 56ca04623..c154c4117 100644 --- a/modules/lib/d3.combobox.js +++ b/modules/lib/d3.combobox.js @@ -223,10 +223,10 @@ export function d3combobox() { .remove(); options.enter() - .append('a') + .append('a') .attr('class', 'combobox-option') .text(function(d) { return d.value; }) - .merge(options) + .merge(options) .attr('title', function(d) { return d.title; }) .classed('selected', function(d, i) { return i === idx; }) .on('mouseover', select) diff --git a/modules/ui/fields/access.js b/modules/ui/fields/access.js index f4e08b282..737598d39 100644 --- a/modules/ui/fields/access.js +++ b/modules/ui/fields/access.js @@ -19,7 +19,15 @@ export function access(field) { .append('ul') .merge(wrap); - items = wrap.select('ul').selectAll('li') + var list = wrap.selectAll('ul') + .data([0]); + + list = list.enter() + .append('ul') + .merge(list); + + + items = list.selectAll('li') .data(field.keys); // Enter diff --git a/modules/ui/fields/combo.js b/modules/ui/fields/combo.js index 393cb0d0e..fbf18df32 100644 --- a/modules/ui/fields/combo.js +++ b/modules/ui/fields/combo.js @@ -223,12 +223,13 @@ export function combo(field, context) { if (isMulti) { container = selection.selectAll('ul').data([0]); - container.enter() + container = container.enter() .append('ul') .attr('class', 'form-field-multicombo') .on('click', function() { window.setTimeout(function() { input.node().focus(); }, 10); - }); + }) + .merge(container); } else { container = selection; @@ -237,11 +238,12 @@ export function combo(field, context) { input = container.selectAll('input') .data([0]); - input.enter() + input = input.enter() .append('input') .attr('type', 'text') .attr('id', 'preset-input-' + field.id) - .call(initCombo, selection); + .call(initCombo, selection) + .merge(input); if (isNetwork) { var center = entity.extent(context.graph()).center(); diff --git a/modules/ui/fields/cycleway.js b/modules/ui/fields/cycleway.js index fa68e197a..1b775f8c8 100644 --- a/modules/ui/fields/cycleway.js +++ b/modules/ui/fields/cycleway.js @@ -3,48 +3,62 @@ import { rebind } from '../../util/rebind'; import { getSetValue } from '../../util/get_set_value'; import { d3combobox } from '../../lib/d3.combobox.js'; + export function cycleway(field) { var dispatch = d3.dispatch('change'), - items; + items = d3.select(null); + function cycleway(selection) { var wrap = selection.selectAll('.preset-input-wrap') .data([0]); - wrap.enter().append('div') + wrap = wrap.enter() + .append('div') .attr('class', 'cf preset-input-wrap') - .append('ul'); + .merge(wrap); - items = wrap.select('ul').selectAll('li') + + var div = wrap.selectAll('ul') + .data([0]); + + div = div.enter() + .append('ul') + .merge(div); + + + items = div.selectAll('li') .data(field.keys); - // Enter - var enter = items.enter().append('li') + var enter = items.enter() + .append('li') .attr('class', function(d) { return 'cf preset-cycleway-' + d; }); - enter.append('span') + enter + .append('span') .attr('class', 'col6 label preset-label-cycleway') .attr('for', function(d) { return 'preset-input-cycleway-' + d; }) .text(function(d) { return field.t('types.' + d); }); - enter.append('div') + enter + .append('div') .attr('class', 'col6 preset-input-cycleway-wrap') .append('input') .attr('type', 'text') .attr('class', 'preset-input-cycleway') .attr('id', function(d) { return 'preset-input-cycleway-' + d; }) .each(function(d) { - d3.select(this) - .call(d3combobox() - .data(cycleway.options(d))); + d3.select(this).call(d3combobox().data(cycleway.options(d))); }); + // Update wrap.selectAll('.preset-input-cycleway') .on('change', change) .on('blur', change); } + function change() { var inputs = d3.selectAll('.preset-input-cycleway')[0], left = getSetValue(d3.select(inputs[0])), @@ -74,6 +88,7 @@ export function cycleway(field) { dispatch.call('change', this, tag); } + cycleway.options = function() { return d3.keys(field.strings.options).map(function(option) { return { @@ -83,6 +98,7 @@ export function cycleway(field) { }); }; + cycleway.tags = function(tags) { getSetValue(items.selectAll('.preset-input-cycleway'), function(d) { // If cycleway is set, always return that @@ -94,10 +110,12 @@ export function cycleway(field) { .attr('placeholder', field.placeholder()); }; + cycleway.focus = function() { items.selectAll('.preset-input-cycleway') .node().focus(); }; + return rebind(cycleway, dispatch, 'on'); } diff --git a/modules/ui/fields/input.js b/modules/ui/fields/input.js index 902cb44ee..d28990a16 100644 --- a/modules/ui/fields/input.js +++ b/modules/ui/fields/input.js @@ -13,21 +13,23 @@ export { }; export function url(field, context) { - var dispatch = d3.dispatch('change'), input, entity; + function i(selection) { var fieldId = 'preset-input-' + field.id; input = selection.selectAll('input') .data([0]); - input.enter().append('input') + input = input.enter() + .append('input') .attr('type', field.type) .attr('id', fieldId) - .attr('placeholder', field.placeholder() || t('inspector.unknown')); + .attr('placeholder', field.placeholder() || t('inspector.unknown')) + .merge(input); input .on('input', change(true)) @@ -49,19 +51,25 @@ export function url(field, context) { var spinControl = selection.selectAll('.spin-control') .data([0]); - var enter = spinControl.enter().append('div') + var enter = spinControl.enter() + .append('div') .attr('class', 'spin-control'); - enter.append('button') + enter + .append('button') .datum(1) .attr('class', 'increment') .attr('tabindex', -1); - enter.append('button') + enter + .append('button') .datum(-1) .attr('class', 'decrement') .attr('tabindex', -1); + spinControl = spinControl + .merge(enter); + spinControl.selectAll('button') .on('click', function(d) { d3.event.preventDefault(); @@ -72,6 +80,7 @@ export function url(field, context) { } } + function change(onInput) { return function() { var t = {}; @@ -80,16 +89,19 @@ export function url(field, context) { }; } + i.entity = function(_) { if (!arguments.length) return entity; entity = _; return i; }; + i.tags = function(tags) { getSetValue(input, tags[field.key] || ''); }; + i.focus = function() { var node = input.node(); if (node) node.focus(); diff --git a/modules/ui/fields/lanes.js b/modules/ui/fields/lanes.js index 309bc295e..a7386cf9d 100644 --- a/modules/ui/fields/lanes.js +++ b/modules/ui/fields/lanes.js @@ -20,9 +20,10 @@ export function lanes(field, context) { var wrap = selection.selectAll('.preset-input-wrap') .data([0]); - wrap.enter() + wrap = wrap.enter() .append('div') - .attr('class', 'preset-input-wrap'); + .attr('class', 'preset-input-wrap') + .merge(wrap); var surface = wrap.selectAll('.surface') .data([0]); @@ -30,27 +31,34 @@ export function lanes(field, context) { var d = getDimensions(wrap); var freeSpace = d[0] - lanesData.lanes.length * LANE_WIDTH * 1.5 + LANE_WIDTH * 0.5; - surface.enter() + surface = surface.enter() .append('svg') .attr('width', d[0]) .attr('height', 300) - .attr('class', 'surface'); + .attr('class', 'surface') + .merge(surface); + var lanesSelection = surface.selectAll('.lanes') .data([0]); - lanesSelection.enter() + lanesSelection = lanesSelection.enter() .append('g') - .attr('class', 'lanes'); + .attr('class', 'lanes') + .merge(lanesSelection); lanesSelection .attr('transform', function () { return 'translate(' + (freeSpace / 2) + ', 0)'; }); + var lane = lanesSelection.selectAll('.lane') .data(lanesData.lanes); + lane.exit() + .remove(); + var enter = lane.enter() .append('g') .attr('class', 'lane'); @@ -86,7 +94,9 @@ export function lanes(field, context) { .attr('x', 14) .text('▼'); - lane.exit().remove(); + + lane = lane + .merge(enter); lane .attr('transform', function(d) { diff --git a/modules/ui/fields/localized.js b/modules/ui/fields/localized.js index 87780b201..3b5f31fe5 100644 --- a/modules/ui/fields/localized.js +++ b/modules/ui/fields/localized.js @@ -11,17 +11,22 @@ import { SuggestNames } from '../../util/index'; import { wikipedia as wikipediaService } from '../../services/index'; import { suggestions, wikipedia as wikipediaData } from '../../../data/index'; + export function localized(field, context) { var dispatch = d3.dispatch('change', 'input'), wikipedia = wikipediaService.init(), - input, localizedInputs, wikiTitles, + input = d3.select(null), + localizedInputs = d3.select(null), + wikiTitles, entity; + function localized(selection) { input = selection.selectAll('.localized-main') .data([0]); - input = input.enter().append('input') + input = input.enter() + .append('input') .attr('type', 'text') .attr('id', 'preset-input-' + field.id) .attr('class', 'localized-main') @@ -40,6 +45,7 @@ export function localized(field, context) { .on('blur', change()) .on('change', change()); + var translateButton = selection.selectAll('.localized-add') .data([0]); @@ -56,6 +62,7 @@ export function localized(field, context) { translateButton .on('click', addNew); + localizedInputs = selection.selectAll('.localized-wrap') .data([0]); @@ -64,6 +71,7 @@ export function localized(field, context) { .merge(localizedInputs); } + function addNew() { d3.event.preventDefault(); var data = localizedInputs.selectAll('div.entry').data(); @@ -71,12 +79,13 @@ export function localized(field, context) { var langExists = _.find(data, function(datum) { return datum.lang === defaultLang;}); var isLangEn = defaultLang.indexOf('en') > -1; if (isLangEn || langExists) { - defaultLang = ''; + defaultLang = ''; } data.push({ lang: defaultLang, value: '' }); localizedInputs.call(render, data); } + function change(onInput) { return function() { var t = {}; @@ -85,7 +94,11 @@ export function localized(field, context) { }; } - function key(lang) { return field.key + ':' + lang; } + + function key(lang) { + return field.key + ':' + lang; + } + function changeLang(d) { var lang = getSetValue(d3.select(this)), @@ -114,6 +127,7 @@ export function localized(field, context) { dispatch.call('change', this, t); } + function changeValue(d) { if (!d.lang) return; var t = {}; @@ -121,6 +135,7 @@ export function localized(field, context) { dispatch.call('change', this, t); } + function fetcher(value, cb) { var v = value.toLowerCase(); @@ -133,10 +148,19 @@ export function localized(field, context) { })); } + function render(selection, data) { var wraps = selection.selectAll('div.entry'). data(data, function(d) { return d.lang; }); + wraps.exit() + .transition() + .duration(200) + .style('max-height','0px') + .style('opacity', '0') + .style('top','-10px') + .remove(); + var innerWrap = wraps.enter() .insert('div', ':first-child'); @@ -145,12 +169,14 @@ export function localized(field, context) { var wrap = d3.select(this); var langcombo = d3combobox().fetcher(fetcher).minItems(0); - var label = wrap.append('label') + var label = wrap + .append('label') .attr('class','form-label') .text(t('translate.localized_translation_label')) .attr('for','localized-lang'); - label.append('button') + label + .append('button') .attr('class', 'minor remove') .on('click', function(d){ d3.event.preventDefault(); @@ -167,7 +193,8 @@ export function localized(field, context) { }) .call(Icon('#operation-delete')); - wrap.append('input') + wrap + .append('input') .attr('class', 'localized-lang') .attr('type', 'text') .attr('placeholder',t('translate.localized_translation_language')) @@ -175,7 +202,8 @@ export function localized(field, context) { .on('change', changeLang) .call(langcombo); - wrap.append('input') + wrap + .append('input') .on('blur', changeValue) .on('change', changeValue) .attr('type', 'text') @@ -198,13 +226,6 @@ export function localized(field, context) { .style('overflow', 'visible'); }); - wraps.exit() - .transition() - .duration(200) - .style('max-height','0px') - .style('opacity', '0') - .style('top','-10px') - .remove(); var entry = selection.selectAll('.entry'); @@ -217,6 +238,7 @@ export function localized(field, context) { function(d) { return d.value; }); } + localized.tags = function(tags) { // Fetch translations from wikipedia if (tags.wikipedia && !wikiTitles) { @@ -242,10 +264,12 @@ export function localized(field, context) { localizedInputs.call(render, postfixed.reverse()); }; + localized.focus = function() { input.node().focus(); }; + localized.entity = function(_) { if (!arguments.length) return entity; entity = _; diff --git a/modules/ui/fields/maxspeed.js b/modules/ui/fields/maxspeed.js index fb2ecd3ff..a8aa74b23 100644 --- a/modules/ui/fields/maxspeed.js +++ b/modules/ui/fields/maxspeed.js @@ -9,14 +9,15 @@ import { imperial as imperialData } from '../../../data/index'; export function maxspeed(field, context) { var dispatch = d3.dispatch('change'), entity, - imperial, - unitInput, - combobox, - input; + isImperial, + unitInput = d3.select(null), + input = d3.select(null), + combobox; var metricValues = [20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120], imperialValues = [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80]; + function maxspeed(selection) { combobox = d3combobox(); var unitCombobox = d3combobox().data(['km/h', 'mph'].map(comboValues)); @@ -24,20 +25,22 @@ export function maxspeed(field, context) { input = selection.selectAll('#preset-input-' + field.id) .data([0]); - input.enter().append('input') + input = input.enter() + .append('input') .attr('type', 'text') .attr('id', 'preset-input-' + field.id) - .attr('placeholder', field.placeholder()); + .attr('placeholder', field.placeholder()) + .call(combobox) + .merge(input); input - .call(combobox) .on('change', change) .on('blur', change); var childNodes = context.graph().childNodes(context.entity(entity.id)), loc = childNodes[~~(childNodes.length/2)].loc; - imperial = _.some(imperialData.features, function(f) { + isImperial = _.some(imperialData.features, function(f) { return _.some(f.geometry.coordinates, function(d) { return pointInPolygon(loc, d); }); @@ -46,29 +49,33 @@ export function maxspeed(field, context) { unitInput = selection.selectAll('input.maxspeed-unit') .data([0]); - unitInput.enter().append('input') + unitInput = unitInput.enter() + .append('input') .attr('type', 'text') - .attr('class', 'maxspeed-unit'); + .attr('class', 'maxspeed-unit') + .call(unitCombobox) + .merge(unitInput); unitInput .on('blur', changeUnits) - .on('change', changeUnits) - .call(unitCombobox); + .on('change', changeUnits); + function changeUnits() { - imperial = getSetValue(unitInput) === 'mph'; - getSetValue(unitInput, imperial ? 'mph' : 'km/h'); + isImperial = getSetValue(unitInput) === 'mph'; + getSetValue(unitInput, isImperial ? 'mph' : 'km/h'); setSuggestions(); change(); } - } + function setSuggestions() { - combobox.data((imperial ? imperialValues : metricValues).map(comboValues)); - getSetValue(unitInput, imperial ? 'mph' : 'km/h'); + combobox.data((isImperial ? imperialValues : metricValues).map(comboValues)); + getSetValue(unitInput, isImperial ? 'mph' : 'km/h'); } + function comboValues(d) { return { value: d.toString(), @@ -76,13 +83,14 @@ export function maxspeed(field, context) { }; } + function change() { var tag = {}, value = getSetValue(input); if (!value) { tag[field.key] = undefined; - } else if (isNaN(value) || !imperial) { + } else if (isNaN(value) || !isImperial) { tag[field.key] = value; } else { tag[field.key] = value + ' mph'; @@ -91,28 +99,31 @@ export function maxspeed(field, context) { dispatch.call('change', this, tag); } + maxspeed.tags = function(tags) { var value = tags[field.key]; if (value && value.indexOf('mph') >= 0) { value = parseInt(value, 10); - imperial = true; + isImperial = true; } else if (value) { - imperial = false; + isImperial = false; } setSuggestions(); - getSetValue(input, value || ''); }; + maxspeed.focus = function() { input.node().focus(); }; + maxspeed.entity = function(_) { entity = _; }; + return rebind(maxspeed, dispatch, 'on'); } diff --git a/modules/ui/fields/radio.js b/modules/ui/fields/radio.js index 24093129f..4f8cc031f 100644 --- a/modules/ui/fields/radio.js +++ b/modules/ui/fields/radio.js @@ -2,9 +2,13 @@ import * as d3 from 'd3'; import { rebind } from '../../util/rebind'; import { t } from '../../util/locale'; + export function radio(field) { var dispatch = d3.dispatch('change'), - labels, radios, placeholder; + placeholder = d3.select(null), + labels = d3.select(null), + radios = d3.select(null); + function radio(selection) { selection.classed('preset-radio', true); @@ -12,18 +16,25 @@ export function radio(field) { var wrap = selection.selectAll('.preset-input-wrap') .data([0]); - var buttonWrap = wrap.enter().append('div') + var enter = wrap.enter() + .append('div') .attr('class', 'preset-input-wrap toggle-list'); - buttonWrap.append('span') + enter + .append('span') .attr('class', 'placeholder'); - placeholder = selection.selectAll('.placeholder'); + wrap = wrap + .merge(enter); + + + placeholder = wrap.selectAll('.placeholder'); labels = wrap.selectAll('label') .data(field.options || field.keys); - var enter = labels.enter().append('label'); + enter = labels.enter() + .append('label'); enter.append('input') .attr('type', 'radio') @@ -34,10 +45,14 @@ export function radio(field) { enter.append('span') .text(function(d) { return field.t('options.' + d, { 'default': d }); }); + labels = labels + .merge(enter); + radios = labels.selectAll('input') .on('change', change); } + function change() { var t = {}; if (field.key) t[field.key] = undefined; @@ -52,6 +67,7 @@ export function radio(field) { dispatch.call('change', this, t); } + radio.tags = function(tags) { function checked(d) { if (field.key) { @@ -71,9 +87,11 @@ export function radio(field) { } }; + radio.focus = function() { radios.node().focus(); }; + return rebind(radio, dispatch, 'on'); } diff --git a/modules/ui/fields/restrictions.js b/modules/ui/fields/restrictions.js index 287f548c8..10bf073b5 100644 --- a/modules/ui/fields/restrictions.js +++ b/modules/ui/fields/restrictions.js @@ -9,12 +9,14 @@ import { RestrictTurn, UnrestrictTurn, } from '../../actions/index'; import { Entity } from '../../core/index'; import { Hover } from '../../behavior/index'; + export function restrictions(field, context) { var dispatch = d3.dispatch('change'), hover = Hover(context), vertexID, fromNodeID; + function restrictions(selection) { // if form field is hidden or has detached from dom, clean up. if (!d3.select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode) { @@ -65,6 +67,9 @@ export function restrictions(field, context) { .call(hover); + wrap = wrap + .merge(enter); + var surface = wrap.selectAll('.surface') .call(setDimensions, d) .call(drawVertices, graph, [vertex], filter, extent, z) @@ -97,6 +102,7 @@ export function restrictions(field, context) { render(); }); + function click() { var datum = d3.event.target.__data__; if (datum instanceof Entity) { @@ -115,6 +121,7 @@ export function restrictions(field, context) { } } + function mouseover() { var datum = d3.event.target.__data__; if (datum instanceof Turn) { @@ -137,16 +144,18 @@ export function restrictions(field, context) { wrap.selectAll('.restriction-help') .text(t('operations.restriction.help.' + (datum.restriction ? 'toggle_off' : 'toggle_on'), - {restriction: preset.name()})); + { restriction: preset.name() })); } } + function mouseout() { wrap.selectAll('.restriction-help') .text(t('operations.restriction.help.' + (fromNodeID ? 'toggle' : 'select'))); } + function render() { if (context.hasEntity(vertexID)) { restrictions(selection); @@ -154,6 +163,7 @@ export function restrictions(field, context) { } } + restrictions.entity = function(_) { if (!vertexID || vertexID !== _.id) { fromNodeID = null; @@ -161,9 +171,11 @@ export function restrictions(field, context) { } }; + restrictions.tags = function() {}; restrictions.focus = function() {}; + restrictions.off = function(selection) { selection.selectAll('.surface') .call(hover.off) @@ -178,5 +190,6 @@ export function restrictions(field, context) { .on('resize.restrictions', null); }; + return rebind(restrictions, dispatch, 'on'); } diff --git a/modules/ui/fields/textarea.js b/modules/ui/fields/textarea.js index 8a8bbd011..5a7fc0e43 100644 --- a/modules/ui/fields/textarea.js +++ b/modules/ui/fields/textarea.js @@ -3,18 +3,22 @@ import { getSetValue } from '../../util/get_set_value'; import { rebind } from '../../util/rebind'; import { t } from '../../util/locale'; + export function textarea(field) { var dispatch = d3.dispatch('change'), - input; + input = d3.select(null); + function textarea(selection) { input = selection.selectAll('textarea') .data([0]); - input.enter().append('textarea') + input.enter() + .append('textarea') .attr('id', 'preset-input-' + field.id) .attr('placeholder', field.placeholder() || t('inspector.unknown')) - .attr('maxlength', 255); + .attr('maxlength', 255) + .merge(input); input .on('input', change(true)) @@ -22,6 +26,7 @@ export function textarea(field) { .on('change', change()); } + function change(onInput) { return function() { var t = {}; @@ -30,13 +35,16 @@ export function textarea(field) { }; } + textarea.tags = function(tags) { getSetValue(input, tags[field.key] || ''); }; + textarea.focus = function() { input.node().focus(); }; + return rebind(textarea, dispatch, 'on'); } diff --git a/modules/ui/fields/wikipedia.js b/modules/ui/fields/wikipedia.js index c8f252072..d6d7c2096 100644 --- a/modules/ui/fields/wikipedia.js +++ b/modules/ui/fields/wikipedia.js @@ -14,11 +14,16 @@ import { wikidata as wikidataService } from '../../services/index'; + export function wikipedia(field, context) { var dispatch = d3.dispatch('change'), wikipedia = wikipediaService.init(), wikidata = wikidataService.init(), - link, entity, lang, title; + link = d3.select(null), + lang = d3.select(null), + title = d3.select(null), + entity; + function wiki(selection) { var langcombo = d3combobox() @@ -47,10 +52,12 @@ export function wikipedia(field, context) { }); }); + lang = selection.selectAll('input.wiki-lang') .data([0]); - lang = lang.enter().append('input') + lang = lang.enter() + .append('input') .attr('type', 'text') .attr('class', 'wiki-lang') .attr('placeholder', t('translate.localized_translation_language')) @@ -63,10 +70,12 @@ export function wikipedia(field, context) { .on('blur', changeLang) .on('change', changeLang); + title = selection.selectAll('input.wiki-title') .data([0]); - title = title.enter().append('input') + title = title.enter() + .append('input') .attr('type', 'text') .attr('class', 'wiki-title') .attr('id', 'preset-input-' + field.id) @@ -77,10 +86,12 @@ export function wikipedia(field, context) { .on('blur', blur) .on('change', change); + link = selection.selectAll('a.wiki-link') .data([0]); - link = link.enter().append('a') + link = link.enter() + .append('a') .attr('class', 'wiki-link button-input-action minor') .attr('tabindex', -1) .attr('target', '_blank') @@ -88,6 +99,7 @@ export function wikipedia(field, context) { .merge(link); } + function language() { var value = getSetValue(lang).toLowerCase(); var locale = Detect().locale.toLowerCase(); @@ -100,15 +112,18 @@ export function wikipedia(field, context) { }) || localeLanguage || ['English', 'English', 'en']; } + function changeLang() { getSetValue(lang, language()[1]); change(true); } + function blur() { change(true); } + function change(skipWikidata) { var value = getSetValue(title), m = value.match(/https?:\/\/([-a-z]+)\.wikipedia\.org\/(?:wiki|\1-[-a-z]+)\/([^#]+)(?:#(.+))?/), @@ -173,6 +188,7 @@ export function wikipedia(field, context) { }); } + wiki.tags = function(tags) { var value = tags[field.key] || '', m = value.match(/([^:]+):([^#]+)(?:#(.+))?/), @@ -204,15 +220,18 @@ export function wikipedia(field, context) { } }; + wiki.entity = function(_) { if (!arguments.length) return entity; entity = _; return wiki; }; + wiki.focus = function() { title.node().focus(); }; + return rebind(wiki, dispatch, 'on'); }