mirror of
https://github.com/FoggedLens/iD.git
synced 2026-02-13 09:12:52 +00:00
Generalize tooltip into popover control Use the same popover control for tooltip as the preset browser and tools list popovers Smartly position the preset browser popover and menu bar tooltips to stay fully onscreen Position most tooltips closer to their controls Fix small gap that could appear between a tooltip and its arrow Allow wider toolbar tooltips
420 lines
12 KiB
JavaScript
420 lines
12 KiB
JavaScript
import _debounce from 'lodash-es/debounce';
|
|
|
|
import { descending as d3_descending, ascending as d3_ascending } from 'd3-array';
|
|
import { event as d3_event, select as d3_select } from 'd3-selection';
|
|
|
|
import { t, textDirection } from '../util/locale';
|
|
import { svgIcon } from '../svg/icon';
|
|
import { uiBackgroundDisplayOptions } from './background_display_options';
|
|
import { uiBackgroundOffset } from './background_offset';
|
|
import { uiCmd } from './cmd';
|
|
import { uiDisclosure } from './disclosure';
|
|
import { uiMapInMap } from './map_in_map';
|
|
import { uiSettingsCustomBackground } from './settings/custom_background';
|
|
import { uiTooltipHtml } from './tooltipHtml';
|
|
import { tooltip } from '../util/tooltip';
|
|
|
|
|
|
export function uiBackground(context) {
|
|
var key = t('background.key');
|
|
|
|
var _pane = d3_select(null);
|
|
|
|
var _customSource = context.background().findSource('custom');
|
|
var _previousBackground = context.background().findSource(context.storage('background-last-used-toggle'));
|
|
|
|
var _backgroundList = d3_select(null);
|
|
var _overlayList = d3_select(null);
|
|
var _displayOptionsContainer = d3_select(null);
|
|
var _offsetContainer = d3_select(null);
|
|
|
|
var backgroundDisplayOptions = uiBackgroundDisplayOptions(context);
|
|
var backgroundOffset = uiBackgroundOffset(context);
|
|
|
|
var settingsCustomBackground = uiSettingsCustomBackground(context)
|
|
.on('change', customChanged);
|
|
|
|
|
|
function setTooltips(selection) {
|
|
selection.each(function(d, i, nodes) {
|
|
var item = d3_select(this).select('label');
|
|
var span = item.select('span');
|
|
var placement = (i < nodes.length / 2) ? 'bottom' : 'top';
|
|
var description = d.description();
|
|
var isOverflowing = (span.property('clientWidth') !== span.property('scrollWidth'));
|
|
|
|
item.call(tooltip().destroyAny);
|
|
|
|
if (d === _previousBackground) {
|
|
item.call(tooltip()
|
|
.placement(placement)
|
|
.html(true)
|
|
.title(function() {
|
|
var tip = '<div>' + t('background.switch') + '</div>';
|
|
return uiTooltipHtml(tip, uiCmd('⌘' + key));
|
|
})
|
|
);
|
|
} else if (description || isOverflowing) {
|
|
item.call(tooltip()
|
|
.placement(placement)
|
|
.title(description || d.name())
|
|
);
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
function updateLayerSelections(selection) {
|
|
function active(d) {
|
|
return context.background().showsLayer(d);
|
|
}
|
|
|
|
selection.selectAll('li')
|
|
.classed('active', active)
|
|
.classed('switch', function(d) { return d === _previousBackground; })
|
|
.call(setTooltips)
|
|
.selectAll('input')
|
|
.property('checked', active);
|
|
}
|
|
|
|
|
|
function chooseBackground(d) {
|
|
if (d.id === 'custom' && !d.template()) {
|
|
return editCustom();
|
|
}
|
|
|
|
d3_event.preventDefault();
|
|
_previousBackground = context.background().baseLayerSource();
|
|
context.storage('background-last-used-toggle', _previousBackground.id);
|
|
context.storage('background-last-used', d.id);
|
|
context.background().baseLayerSource(d);
|
|
_backgroundList.call(updateLayerSelections);
|
|
document.activeElement.blur();
|
|
}
|
|
|
|
|
|
function customChanged(d) {
|
|
if (d && d.template) {
|
|
_customSource.template(d.template);
|
|
chooseBackground(_customSource);
|
|
} else {
|
|
_customSource.template('');
|
|
chooseBackground(context.background().findSource('none'));
|
|
}
|
|
}
|
|
|
|
|
|
function editCustom() {
|
|
d3_event.preventDefault();
|
|
context.container()
|
|
.call(settingsCustomBackground);
|
|
}
|
|
|
|
|
|
function chooseOverlay(d) {
|
|
d3_event.preventDefault();
|
|
context.background().toggleOverlayLayer(d);
|
|
_overlayList.call(updateLayerSelections);
|
|
document.activeElement.blur();
|
|
}
|
|
|
|
|
|
function drawListItems(layerList, type, change, filter) {
|
|
var sources = context.background()
|
|
.sources(context.map().extent(), context.map().zoom(), true)
|
|
.filter(filter);
|
|
|
|
var layerLinks = layerList.selectAll('li')
|
|
.data(sources, function(d) { return d.name(); });
|
|
|
|
layerLinks.exit()
|
|
.remove();
|
|
|
|
var enter = layerLinks.enter()
|
|
.append('li')
|
|
.classed('layer-custom', function(d) { return d.id === 'custom'; })
|
|
.classed('best', function(d) { return d.best(); });
|
|
|
|
var label = enter
|
|
.append('label');
|
|
|
|
label
|
|
.append('input')
|
|
.attr('type', type)
|
|
.attr('name', 'layers')
|
|
.on('change', change);
|
|
|
|
label
|
|
.append('span')
|
|
.text(function(d) { return d.name(); });
|
|
|
|
enter.filter(function(d) { return d.id === 'custom'; })
|
|
.append('button')
|
|
.attr('class', 'layer-browse')
|
|
.call(tooltip()
|
|
.title(t('settings.custom_background.tooltip'))
|
|
.placement((textDirection === 'rtl') ? 'right' : 'left')
|
|
)
|
|
.on('click', editCustom)
|
|
.call(svgIcon('#iD-icon-more'));
|
|
|
|
enter.filter(function(d) { return d.best(); })
|
|
.append('div')
|
|
.attr('class', 'best')
|
|
.call(tooltip()
|
|
.title(t('background.best_imagery'))
|
|
.placement((textDirection === 'rtl') ? 'right' : 'left')
|
|
)
|
|
.append('span')
|
|
.html('★');
|
|
|
|
|
|
layerList.selectAll('li')
|
|
.sort(sortSources);
|
|
|
|
layerList
|
|
.call(updateLayerSelections);
|
|
|
|
|
|
function sortSources(a, b) {
|
|
return a.best() && !b.best() ? -1
|
|
: b.best() && !a.best() ? 1
|
|
: d3_descending(a.area(), b.area()) || d3_ascending(a.name(), b.name()) || 0;
|
|
}
|
|
}
|
|
|
|
|
|
function renderBackgroundList(selection) {
|
|
|
|
// the background list
|
|
var container = selection.selectAll('.layer-background-list')
|
|
.data([0]);
|
|
|
|
_backgroundList = container.enter()
|
|
.append('ul')
|
|
.attr('class', 'layer-list layer-background-list')
|
|
.attr('dir', 'auto')
|
|
.merge(container);
|
|
|
|
|
|
// add minimap toggle below list
|
|
var bgExtrasListEnter = selection.selectAll('.bg-extras-list')
|
|
.data([0])
|
|
.enter()
|
|
.append('ul')
|
|
.attr('class', 'layer-list bg-extras-list');
|
|
|
|
var minimapLabelEnter = bgExtrasListEnter
|
|
.append('li')
|
|
.attr('class', 'minimap-toggle-item')
|
|
.append('label')
|
|
.call(tooltip()
|
|
.html(true)
|
|
.title(uiTooltipHtml(t('background.minimap.tooltip'), t('background.minimap.key')))
|
|
.placement('top')
|
|
);
|
|
|
|
minimapLabelEnter
|
|
.append('input')
|
|
.attr('type', 'checkbox')
|
|
.on('change', function() {
|
|
d3_event.preventDefault();
|
|
uiMapInMap.toggle();
|
|
});
|
|
|
|
minimapLabelEnter
|
|
.append('span')
|
|
.text(t('background.minimap.description'));
|
|
|
|
|
|
var panelLabelEnter = bgExtrasListEnter
|
|
.append('li')
|
|
.attr('class', 'background-panel-toggle-item')
|
|
.append('label')
|
|
.call(tooltip()
|
|
.html(true)
|
|
.title(uiTooltipHtml(t('background.panel.tooltip'), uiCmd('⌘⇧' + t('info_panels.background.key'))))
|
|
.placement('top')
|
|
);
|
|
|
|
panelLabelEnter
|
|
.append('input')
|
|
.attr('type', 'checkbox')
|
|
.on('change', function() {
|
|
d3_event.preventDefault();
|
|
context.ui().info.toggle('background');
|
|
});
|
|
|
|
panelLabelEnter
|
|
.append('span')
|
|
.text(t('background.panel.description'));
|
|
|
|
|
|
// "Info / Report a Problem" link
|
|
selection.selectAll('.imagery-faq')
|
|
.data([0])
|
|
.enter()
|
|
.append('div')
|
|
.attr('class', 'imagery-faq')
|
|
.append('a')
|
|
.attr('target', '_blank')
|
|
.call(svgIcon('#iD-icon-out-link', 'inline'))
|
|
.attr('href', 'https://github.com/openstreetmap/iD/blob/master/FAQ.md#how-can-i-report-an-issue-with-background-imagery')
|
|
.append('span')
|
|
.text(t('background.imagery_problem_faq'));
|
|
|
|
updateBackgroundList();
|
|
}
|
|
|
|
|
|
function renderOverlayList(selection) {
|
|
var container = selection.selectAll('.layer-overlay-list')
|
|
.data([0]);
|
|
|
|
_overlayList = container.enter()
|
|
.append('ul')
|
|
.attr('class', 'layer-list layer-overlay-list')
|
|
.attr('dir', 'auto')
|
|
.merge(container);
|
|
|
|
updateOverlayList();
|
|
}
|
|
|
|
function updateBackgroundList() {
|
|
_backgroundList
|
|
.call(drawListItems, 'radio', chooseBackground, function(d) { return !d.isHidden() && !d.overlay; });
|
|
}
|
|
|
|
function updateOverlayList() {
|
|
_overlayList
|
|
.call(drawListItems, 'checkbox', chooseOverlay, function(d) { return !d.isHidden() && d.overlay; });
|
|
}
|
|
|
|
|
|
function update() {
|
|
if (!_pane.select('.disclosure-wrap-background_list').classed('hide')) {
|
|
updateBackgroundList();
|
|
}
|
|
|
|
if (!_pane.select('.disclosure-wrap-overlay_list').classed('hide')) {
|
|
updateOverlayList();
|
|
}
|
|
|
|
_displayOptionsContainer
|
|
.call(backgroundDisplayOptions);
|
|
|
|
_offsetContainer
|
|
.call(backgroundOffset);
|
|
}
|
|
|
|
|
|
function quickSwitch() {
|
|
if (d3_event) {
|
|
d3_event.stopImmediatePropagation();
|
|
d3_event.preventDefault();
|
|
}
|
|
if (_previousBackground) {
|
|
chooseBackground(_previousBackground);
|
|
}
|
|
}
|
|
|
|
var paneTooltip = tooltip()
|
|
.placement((textDirection === 'rtl') ? 'right' : 'left')
|
|
.html(true)
|
|
.title(uiTooltipHtml(t('background.description'), key));
|
|
|
|
uiBackground.togglePane = function() {
|
|
if (d3_event) d3_event.preventDefault();
|
|
paneTooltip.hide();
|
|
context.ui().togglePanes(!_pane.classed('shown') ? _pane : undefined);
|
|
};
|
|
|
|
function hidePane() {
|
|
context.ui().togglePanes();
|
|
}
|
|
|
|
uiBackground.renderToggleButton = function(selection) {
|
|
|
|
selection
|
|
.append('button')
|
|
.on('click', uiBackground.togglePane)
|
|
.call(svgIcon('#iD-icon-layers', 'light'))
|
|
.call(paneTooltip);
|
|
};
|
|
|
|
uiBackground.renderPane = function(selection) {
|
|
|
|
_pane = selection
|
|
.append('div')
|
|
.attr('class', 'fillL map-pane background-pane hide')
|
|
.attr('pane', 'background');
|
|
|
|
|
|
var heading = _pane
|
|
.append('div')
|
|
.attr('class', 'pane-heading');
|
|
|
|
heading
|
|
.append('h2')
|
|
.text(t('background.title'));
|
|
|
|
heading
|
|
.append('button')
|
|
.on('click', hidePane)
|
|
.call(svgIcon('#iD-icon-close'));
|
|
|
|
|
|
var content = _pane
|
|
.append('div')
|
|
.attr('class', 'pane-content');
|
|
|
|
// background list
|
|
content
|
|
.append('div')
|
|
.attr('class', 'background-background-list-container')
|
|
.call(uiDisclosure(context, 'background_list', true)
|
|
.title(t('background.backgrounds'))
|
|
.content(renderBackgroundList)
|
|
);
|
|
|
|
// overlay list
|
|
content
|
|
.append('div')
|
|
.attr('class', 'background-overlay-list-container')
|
|
.call(uiDisclosure(context, 'overlay_list', true)
|
|
.title(t('background.overlays'))
|
|
.content(renderOverlayList)
|
|
);
|
|
|
|
// display options
|
|
_displayOptionsContainer = content
|
|
.append('div')
|
|
.attr('class', 'background-display-options');
|
|
|
|
// offset controls
|
|
_offsetContainer = content
|
|
.append('div')
|
|
.attr('class', 'background-offset');
|
|
|
|
|
|
// add listeners
|
|
context.map()
|
|
.on('move.background-update',
|
|
_debounce(function() { window.requestIdleCallback(update); }, 1000)
|
|
);
|
|
|
|
|
|
context.background()
|
|
.on('change.background-update', update);
|
|
|
|
|
|
update();
|
|
|
|
context.keybinding()
|
|
.on(key, uiBackground.togglePane)
|
|
.on(uiCmd('⌘' + key), quickSwitch);
|
|
};
|
|
|
|
return uiBackground;
|
|
}
|