Files
iD/modules/ui/intro/area.js
Quincy Morgan 523a467836 2.x: Make toolbar horizontally scrollable when it overflows (re: #6755, re: 7545f67063b5e7007ef2d8367e3181e59c04a487)
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
2019-12-16 13:30:07 -05:00

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');
}