mirror of
https://github.com/FoggedLens/iD.git
synced 2026-02-27 16:03: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
507 lines
16 KiB
JavaScript
507 lines
16 KiB
JavaScript
import { dispatch as d3_dispatch } from 'd3-dispatch';
|
|
|
|
import {
|
|
event as d3_event,
|
|
select as d3_select
|
|
} from 'd3-selection';
|
|
|
|
import {
|
|
interpolateNumber as d3_interpolateNumber
|
|
} from 'd3-interpolate';
|
|
|
|
import { t } from '../../util/locale';
|
|
import { modeBrowse } from '../../modes/browse';
|
|
import { modeSelect } from '../../modes/select';
|
|
import { utilRebind } from '../../util/rebind';
|
|
import { uiCmd } from '../cmd';
|
|
import { icon, pad, transitionTime } from './helper';
|
|
|
|
|
|
export function uiIntroArea(context, reveal) {
|
|
var dispatch = d3_dispatch('done');
|
|
var playground = [-85.63552, 41.94159];
|
|
var playgroundPreset = context.presets().item('leisure/playground');
|
|
var descriptionField = context.presets().field('description');
|
|
var timeouts = [];
|
|
var _areaID;
|
|
|
|
|
|
var chapter = {
|
|
title: 'intro.areas.title'
|
|
};
|
|
|
|
|
|
function timeout(f, t) {
|
|
timeouts.push(window.setTimeout(f, t));
|
|
}
|
|
|
|
|
|
function eventCancel() {
|
|
d3_event.stopPropagation();
|
|
d3_event.preventDefault();
|
|
}
|
|
|
|
|
|
function revealPlayground(center, text, options) {
|
|
var padding = 180 * Math.pow(2, context.map().zoom() - 19.5);
|
|
var box = pad(center, padding, context);
|
|
reveal(box, text, options);
|
|
}
|
|
|
|
|
|
function addArea() {
|
|
context.enter(modeBrowse(context));
|
|
context.history().reset('initial');
|
|
_areaID = null;
|
|
|
|
var msec = transitionTime(playground, context.map().center());
|
|
if (msec) { reveal(null, null, { duration: 0 }); }
|
|
context.map().centerZoomEase(playground, 19, msec);
|
|
|
|
timeout(function() {
|
|
var tooltip = reveal('button.add-area',
|
|
t('intro.areas.add_playground', { button: icon('#iD-icon-area', 'pre-text') }));
|
|
|
|
tooltip.selectAll('.popover-inner')
|
|
.insert('svg', 'span')
|
|
.attr('class', 'tooltip-illustration')
|
|
.append('use')
|
|
.attr('xlink:href', '#iD-graphic-areas');
|
|
|
|
context.on('enter.intro', function(mode) {
|
|
if (mode.id !== 'add-area') return;
|
|
continueTo(startPlayground);
|
|
});
|
|
}, msec + 100);
|
|
|
|
function continueTo(nextStep) {
|
|
context.on('enter.intro', null);
|
|
nextStep();
|
|
}
|
|
}
|
|
|
|
|
|
function startPlayground() {
|
|
if (context.mode().id !== 'add-area') {
|
|
return chapter.restart();
|
|
}
|
|
|
|
_areaID = null;
|
|
context.map().zoomEase(19.5, 500);
|
|
|
|
timeout(function() {
|
|
revealPlayground(playground,
|
|
t('intro.areas.start_playground'), { duration: 250 }
|
|
);
|
|
|
|
timeout(function() {
|
|
context.map().on('move.intro drawn.intro', function() {
|
|
revealPlayground(playground,
|
|
t('intro.areas.start_playground'), { duration: 0 }
|
|
);
|
|
});
|
|
context.on('enter.intro', function(mode) {
|
|
if (mode.id !== 'draw-area') return chapter.restart();
|
|
continueTo(continuePlayground);
|
|
});
|
|
}, 250); // after reveal
|
|
|
|
}, 550); // after easing
|
|
|
|
function continueTo(nextStep) {
|
|
context.map().on('move.intro drawn.intro', null);
|
|
context.on('enter.intro', null);
|
|
nextStep();
|
|
}
|
|
}
|
|
|
|
|
|
function continuePlayground() {
|
|
if (context.mode().id !== 'draw-area') {
|
|
return chapter.restart();
|
|
}
|
|
|
|
_areaID = null;
|
|
revealPlayground(playground,
|
|
t('intro.areas.continue_playground', { alt: uiCmd.display('⌥') }),
|
|
{ duration: 250 }
|
|
);
|
|
|
|
timeout(function() {
|
|
context.map().on('move.intro drawn.intro', function() {
|
|
revealPlayground(playground,
|
|
t('intro.areas.continue_playground', { alt: uiCmd.display('⌥') }),
|
|
{ duration: 0 }
|
|
);
|
|
});
|
|
}, 250); // after reveal
|
|
|
|
context.on('enter.intro', function(mode) {
|
|
if (mode.id === 'draw-area') {
|
|
var entity = context.hasEntity(context.selectedIDs()[0]);
|
|
if (entity && entity.nodes.length >= 6) {
|
|
return continueTo(finishPlayground);
|
|
} else {
|
|
return;
|
|
}
|
|
} else if (mode.id === 'select') {
|
|
_areaID = context.selectedIDs()[0];
|
|
return continueTo(searchPresets);
|
|
} else {
|
|
return chapter.restart();
|
|
}
|
|
});
|
|
|
|
function continueTo(nextStep) {
|
|
context.map().on('move.intro drawn.intro', null);
|
|
context.on('enter.intro', null);
|
|
nextStep();
|
|
}
|
|
}
|
|
|
|
|
|
function finishPlayground() {
|
|
if (context.mode().id !== 'draw-area') {
|
|
return chapter.restart();
|
|
}
|
|
|
|
_areaID = null;
|
|
revealPlayground(playground,
|
|
t('intro.areas.finish_playground'), { duration: 250 }
|
|
);
|
|
|
|
timeout(function() {
|
|
context.map().on('move.intro drawn.intro', function() {
|
|
revealPlayground(playground,
|
|
t('intro.areas.finish_playground'), { duration: 0 }
|
|
);
|
|
});
|
|
}, 250); // after reveal
|
|
|
|
context.on('enter.intro', function(mode) {
|
|
if (mode.id === 'draw-area') {
|
|
return;
|
|
} else if (mode.id === 'select') {
|
|
_areaID = context.selectedIDs()[0];
|
|
return continueTo(searchPresets);
|
|
} else {
|
|
return chapter.restart();
|
|
}
|
|
});
|
|
|
|
function continueTo(nextStep) {
|
|
context.map().on('move.intro drawn.intro', null);
|
|
context.on('enter.intro', null);
|
|
nextStep();
|
|
}
|
|
}
|
|
|
|
|
|
function searchPresets() {
|
|
if (!_areaID || !context.hasEntity(_areaID)) {
|
|
return addArea();
|
|
}
|
|
var ids = context.selectedIDs();
|
|
if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
|
|
context.enter(modeSelect(context, [_areaID]));
|
|
}
|
|
|
|
// disallow scrolling
|
|
d3_select('.inspector-wrap').on('wheel.intro', eventCancel);
|
|
|
|
timeout(function() {
|
|
// reset pane, in case user somehow happened to change it..
|
|
d3_select('.inspector-wrap .panewrap').style('right', '-100%');
|
|
|
|
d3_select('.preset-search-input')
|
|
.on('keydown.intro', null)
|
|
.on('keyup.intro', checkPresetSearch);
|
|
|
|
reveal('.preset-search-input',
|
|
t('intro.areas.search_playground', { preset: playgroundPreset.name() })
|
|
);
|
|
}, 400); // after preset list pane visible..
|
|
|
|
context.on('enter.intro', function(mode) {
|
|
if (!_areaID || !context.hasEntity(_areaID)) {
|
|
return continueTo(addArea);
|
|
}
|
|
|
|
var ids = context.selectedIDs();
|
|
if (mode.id !== 'select' || !ids.length || ids[0] !== _areaID) {
|
|
// keep the user's area selected..
|
|
context.enter(modeSelect(context, [_areaID]));
|
|
|
|
// reset pane, in case user somehow happened to change it..
|
|
d3_select('.inspector-wrap .panewrap').style('right', '-100%');
|
|
// disallow scrolling
|
|
d3_select('.inspector-wrap').on('wheel.intro', eventCancel);
|
|
|
|
d3_select('.preset-search-input')
|
|
.on('keydown.intro', null)
|
|
.on('keyup.intro', checkPresetSearch);
|
|
|
|
reveal('.preset-search-input',
|
|
t('intro.areas.search_playground', { preset: playgroundPreset.name() })
|
|
);
|
|
|
|
context.history().on('change.intro', null);
|
|
}
|
|
});
|
|
|
|
function checkPresetSearch() {
|
|
var first = d3_select('.preset-list-item:first-child');
|
|
|
|
if (first.classed('preset-leisure-playground')) {
|
|
reveal(first.select('.preset-list-button').node(),
|
|
t('intro.areas.choose_playground', { preset: playgroundPreset.name() }),
|
|
{ duration: 300 }
|
|
);
|
|
|
|
d3_select('.preset-search-input')
|
|
.on('keydown.intro', eventCancel, true)
|
|
.on('keyup.intro', null);
|
|
|
|
context.history().on('change.intro', function() {
|
|
continueTo(clickAddField);
|
|
});
|
|
}
|
|
}
|
|
|
|
function continueTo(nextStep) {
|
|
d3_select('.inspector-wrap').on('wheel.intro', null);
|
|
context.on('enter.intro', null);
|
|
context.history().on('change.intro', null);
|
|
d3_select('.preset-search-input').on('keydown.intro keyup.intro', null);
|
|
nextStep();
|
|
}
|
|
}
|
|
|
|
|
|
function clickAddField() {
|
|
if (!_areaID || !context.hasEntity(_areaID)) {
|
|
return addArea();
|
|
}
|
|
var ids = context.selectedIDs();
|
|
if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
|
|
return searchPresets();
|
|
}
|
|
|
|
if (!d3_select('.form-field-description').empty()) {
|
|
return continueTo(describePlayground);
|
|
}
|
|
|
|
// disallow scrolling
|
|
d3_select('.inspector-wrap').on('wheel.intro', eventCancel);
|
|
|
|
timeout(function() {
|
|
// reset pane, in case user somehow happened to change it..
|
|
d3_select('.inspector-wrap .panewrap').style('right', '0%');
|
|
|
|
// It's possible for the user to add a description in a previous step..
|
|
// If they did this already, just continue to next step.
|
|
var entity = context.entity(_areaID);
|
|
if (entity.tags.description) {
|
|
return continueTo(play);
|
|
}
|
|
|
|
// scroll "Add field" into view
|
|
var box = d3_select('.more-fields').node().getBoundingClientRect();
|
|
if (box.top > 300) {
|
|
var pane = d3_select('.entity-editor-pane .inspector-body');
|
|
var start = pane.node().scrollTop;
|
|
var end = start + (box.top - 300);
|
|
|
|
pane
|
|
.transition()
|
|
.duration(250)
|
|
.tween('scroll.inspector', function() {
|
|
var node = this;
|
|
var i = d3_interpolateNumber(start, end);
|
|
return function(t) {
|
|
node.scrollTop = i(t);
|
|
};
|
|
});
|
|
}
|
|
|
|
timeout(function() {
|
|
reveal('.more-fields .combobox-input',
|
|
t('intro.areas.add_field'),
|
|
{ duration: 300 }
|
|
);
|
|
|
|
d3_select('.more-fields .combobox-input')
|
|
.on('click.intro', function() {
|
|
// Watch for the combobox to appear...
|
|
var watcher;
|
|
watcher = window.setInterval(function() {
|
|
if (!d3_select('div.combobox').empty()) {
|
|
window.clearInterval(watcher);
|
|
continueTo(chooseDescriptionField);
|
|
}
|
|
}, 300);
|
|
});
|
|
}, 300); // after "Add Field" visible
|
|
|
|
}, 400); // after editor pane visible
|
|
|
|
context.on('exit.intro', function() {
|
|
return continueTo(searchPresets);
|
|
});
|
|
|
|
function continueTo(nextStep) {
|
|
d3_select('.inspector-wrap').on('wheel.intro', null);
|
|
d3_select('.more-fields .combobox-input').on('click.intro', null);
|
|
context.on('exit.intro', null);
|
|
nextStep();
|
|
}
|
|
}
|
|
|
|
|
|
function chooseDescriptionField() {
|
|
if (!_areaID || !context.hasEntity(_areaID)) {
|
|
return addArea();
|
|
}
|
|
var ids = context.selectedIDs();
|
|
if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
|
|
return searchPresets();
|
|
}
|
|
|
|
if (!d3_select('.form-field-description').empty()) {
|
|
return continueTo(describePlayground);
|
|
}
|
|
|
|
// Make sure combobox is ready..
|
|
if (d3_select('div.combobox').empty()) {
|
|
return continueTo(clickAddField);
|
|
}
|
|
// Watch for the combobox to go away..
|
|
var watcher;
|
|
watcher = window.setInterval(function() {
|
|
if (d3_select('div.combobox').empty()) {
|
|
window.clearInterval(watcher);
|
|
timeout(function() {
|
|
if (d3_select('.form-field-description').empty()) {
|
|
continueTo(retryChooseDescription);
|
|
} else {
|
|
continueTo(describePlayground);
|
|
}
|
|
}, 300); // after description field added.
|
|
}
|
|
}, 300);
|
|
|
|
reveal('div.combobox',
|
|
t('intro.areas.choose_field', { field: descriptionField.label() }),
|
|
{ duration: 300 }
|
|
);
|
|
|
|
context.on('exit.intro', function() {
|
|
return continueTo(searchPresets);
|
|
});
|
|
|
|
function continueTo(nextStep) {
|
|
if (watcher) window.clearInterval(watcher);
|
|
context.on('exit.intro', null);
|
|
nextStep();
|
|
}
|
|
}
|
|
|
|
|
|
function describePlayground() {
|
|
if (!_areaID || !context.hasEntity(_areaID)) {
|
|
return addArea();
|
|
}
|
|
var ids = context.selectedIDs();
|
|
if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
|
|
return searchPresets();
|
|
}
|
|
|
|
// reset pane, in case user happened to change it..
|
|
d3_select('.inspector-wrap .panewrap').style('right', '0%');
|
|
|
|
if (d3_select('.form-field-description').empty()) {
|
|
return continueTo(retryChooseDescription);
|
|
}
|
|
|
|
context.on('exit.intro', function() {
|
|
continueTo(play);
|
|
});
|
|
|
|
reveal('.entity-editor-pane',
|
|
t('intro.areas.describe_playground', { button: icon('#iD-icon-apply', 'pre-text') }),
|
|
{ duration: 300 }
|
|
);
|
|
|
|
function continueTo(nextStep) {
|
|
context.on('exit.intro', null);
|
|
nextStep();
|
|
}
|
|
}
|
|
|
|
|
|
function retryChooseDescription() {
|
|
if (!_areaID || !context.hasEntity(_areaID)) {
|
|
return addArea();
|
|
}
|
|
var ids = context.selectedIDs();
|
|
if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
|
|
return searchPresets();
|
|
}
|
|
|
|
// reset pane, in case user happened to change it..
|
|
d3_select('.inspector-wrap .panewrap').style('right', '0%');
|
|
|
|
reveal('.entity-editor-pane',
|
|
t('intro.areas.retry_add_field', { field: descriptionField.label() }), {
|
|
buttonText: t('intro.ok'),
|
|
buttonCallback: function() { continueTo(clickAddField); }
|
|
});
|
|
|
|
context.on('exit.intro', function() {
|
|
return continueTo(searchPresets);
|
|
});
|
|
|
|
function continueTo(nextStep) {
|
|
context.on('exit.intro', null);
|
|
nextStep();
|
|
}
|
|
}
|
|
|
|
|
|
function play() {
|
|
dispatch.call('done');
|
|
reveal('#id-container',
|
|
t('intro.areas.play', { next: t('intro.lines.title') }), {
|
|
tooltipBox: '.intro-nav-wrap .chapter-line',
|
|
buttonText: t('intro.ok'),
|
|
buttonCallback: function() { reveal('#id-container'); }
|
|
}
|
|
);
|
|
}
|
|
|
|
|
|
chapter.enter = function() {
|
|
addArea();
|
|
};
|
|
|
|
|
|
chapter.exit = function() {
|
|
timeouts.forEach(window.clearTimeout);
|
|
context.on('enter.intro exit.intro', null);
|
|
context.map().on('move.intro drawn.intro', null);
|
|
context.history().on('change.intro', null);
|
|
d3_select('.inspector-wrap').on('wheel.intro', null);
|
|
d3_select('.preset-search-input').on('keydown.intro keyup.intro', null);
|
|
d3_select('.more-fields .combobox-input').on('click.intro', null);
|
|
};
|
|
|
|
|
|
chapter.restart = function() {
|
|
chapter.exit();
|
|
chapter.enter();
|
|
};
|
|
|
|
|
|
return utilRebind(chapter, dispatch, 'on');
|
|
}
|