modules/ui/intro/point.js (371 lines of code) (raw):
import { dispatch as d3_dispatch } from 'd3-dispatch';
import {
select as d3_select
} from 'd3-selection';
import { presetManager } from '../../presets';
import { t } from '../../core/localizer';
import { actionChangePreset } from '../../actions/change_preset';
import { modeBrowse } from '../../modes/browse';
import { modeSelect } from '../../modes/select';
import { utilRebind } from '../../util/rebind';
import { helpHtml, icon, pointBox, pad, selectMenuItem, transitionTime } from './helper';
export function uiIntroPoint(context, reveal) {
var dispatch = d3_dispatch('done');
var timeouts = [];
var intersection = [-85.63279, 41.94394];
var building = [-85.632422, 41.944045];
var cafePreset = presetManager.item('amenity/cafe');
var _pointID = null;
var chapter = {
title: 'intro.points.title'
};
function timeout(f, t) {
timeouts.push(window.setTimeout(f, t));
}
function eventCancel(d3_event) {
d3_event.stopPropagation();
d3_event.preventDefault();
}
function addPoint() {
context.enter(modeBrowse(context));
context.history().reset('initial');
var msec = transitionTime(intersection, context.map().center());
if (msec) { reveal(null, null, { duration: 0 }); }
context.map().centerZoomEase(intersection, 19, msec);
timeout(function() {
var tooltip = reveal('button.add-point',
helpHtml('intro.points.points_info') + '{br}' + helpHtml('intro.points.add_point'));
_pointID = null;
tooltip.selectAll('.popover-inner')
.insert('svg', 'span')
.attr('class', 'tooltip-illustration')
.append('use')
.attr('xlink:href', '#iD-graphic-points');
context.on('enter.intro', function(mode) {
if (mode.id !== 'add-point') return;
continueTo(placePoint);
});
}, msec + 100);
function continueTo(nextStep) {
context.on('enter.intro', null);
nextStep();
}
}
function placePoint() {
if (context.mode().id !== 'add-point') {
return chapter.restart();
}
var pointBox = pad(building, 150, context);
var textId = context.lastPointerType() === 'mouse' ? 'place_point' : 'place_point_touch';
reveal(pointBox, helpHtml('intro.points.' + textId));
context.map().on('move.intro drawn.intro', function() {
pointBox = pad(building, 150, context);
reveal(pointBox, helpHtml('intro.points.' + textId), { duration: 0 });
});
context.on('enter.intro', function(mode) {
if (mode.id !== 'select') return chapter.restart();
_pointID = context.mode().selectedIDs()[0];
continueTo(searchPreset);
});
function continueTo(nextStep) {
context.map().on('move.intro drawn.intro', null);
context.on('enter.intro', null);
nextStep();
}
}
function searchPreset() {
if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
return addPoint();
}
// disallow scrolling
context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
context.container().select('.preset-search-input')
.on('keydown.intro', null)
.on('keyup.intro', checkPresetSearch);
reveal('.preset-search-input',
helpHtml('intro.points.search_cafe', { preset: cafePreset.name() })
);
context.on('enter.intro', function(mode) {
if (!_pointID || !context.hasEntity(_pointID)) {
return continueTo(addPoint);
}
var ids = context.selectedIDs();
if (mode.id !== 'select' || !ids.length || ids[0] !== _pointID) {
// keep the user's point selected..
context.enter(modeSelect(context, [_pointID]));
// disallow scrolling
context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
context.container().select('.preset-search-input')
.on('keydown.intro', null)
.on('keyup.intro', checkPresetSearch);
reveal('.preset-search-input',
helpHtml('intro.points.search_cafe', { preset: cafePreset.name() })
);
context.history().on('change.intro', null);
}
});
function checkPresetSearch() {
var first = context.container().select('.preset-list-item:first-child');
if (first.classed('preset-amenity-cafe')) {
context.container().select('.preset-search-input')
.on('keydown.intro', eventCancel, true)
.on('keyup.intro', null);
reveal(first.select('.preset-list-button').node(),
helpHtml('intro.points.choose_cafe', { preset: cafePreset.name() }),
{ duration: 300 }
);
context.history().on('change.intro', function() {
continueTo(aboutFeatureEditor);
});
}
}
function continueTo(nextStep) {
context.on('enter.intro', null);
context.history().on('change.intro', null);
context.container().select('.inspector-wrap').on('wheel.intro', null);
context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
nextStep();
}
}
function aboutFeatureEditor() {
if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
return addPoint();
}
timeout(function() {
reveal('.entity-editor-pane', helpHtml('intro.points.feature_editor'), {
tooltipClass: 'intro-points-describe',
buttonText: t.html('intro.ok'),
buttonCallback: function() { continueTo(addName); }
});
}, 400);
context.on('exit.intro', function() {
// if user leaves select mode here, just continue with the tutorial.
continueTo(reselectPoint);
});
function continueTo(nextStep) {
context.on('exit.intro', null);
nextStep();
}
}
function addName() {
if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
return addPoint();
}
// reset pane, in case user happened to change it..
context.container().select('.inspector-wrap .panewrap').style('right', '0%');
var addNameString = helpHtml('intro.points.fields_info') + '{br}' + helpHtml('intro.points.add_name');
timeout(function() {
// It's possible for the user to add a name in a previous step..
// If so, don't tell them to add the name in this step.
// Give them an OK button instead.
var entity = context.entity(_pointID);
if (entity.tags.name) {
var tooltip = reveal('.entity-editor-pane', addNameString, {
tooltipClass: 'intro-points-describe',
buttonText: t.html('intro.ok'),
buttonCallback: function() { continueTo(addCloseEditor); }
});
tooltip.select('.instruction').style('display', 'none');
} else {
reveal('.entity-editor-pane', addNameString,
{ tooltipClass: 'intro-points-describe' }
);
}
}, 400);
context.history().on('change.intro', function() {
continueTo(addCloseEditor);
});
context.on('exit.intro', function() {
// if user leaves select mode here, just continue with the tutorial.
continueTo(reselectPoint);
});
function continueTo(nextStep) {
context.on('exit.intro', null);
context.history().on('change.intro', null);
nextStep();
}
}
function addCloseEditor() {
// reset pane, in case user happened to change it..
context.container().select('.inspector-wrap .panewrap').style('right', '0%');
var selector = '.entity-editor-pane button.close svg use';
var href = d3_select(selector).attr('href') || '#iD-icon-close';
context.on('exit.intro', function() {
continueTo(reselectPoint);
});
reveal('.entity-editor-pane',
helpHtml('intro.points.add_close', { button: icon(href, 'inline') })
);
function continueTo(nextStep) {
context.on('exit.intro', null);
nextStep();
}
}
function reselectPoint() {
if (!_pointID) return chapter.restart();
var entity = context.hasEntity(_pointID);
if (!entity) return chapter.restart();
// make sure it's still a cafe, in case user somehow changed it..
var oldPreset = presetManager.match(entity, context.graph());
context.replace(actionChangePreset(_pointID, oldPreset, cafePreset));
context.enter(modeBrowse(context));
var msec = transitionTime(entity.loc, context.map().center());
if (msec) { reveal(null, null, { duration: 0 }); }
context.map().centerEase(entity.loc, msec);
timeout(function() {
var box = pointBox(entity.loc, context);
reveal(box, helpHtml('intro.points.reselect'), { duration: 600 });
timeout(function() {
context.map().on('move.intro drawn.intro', function() {
var entity = context.hasEntity(_pointID);
if (!entity) return chapter.restart();
var box = pointBox(entity.loc, context);
reveal(box, helpHtml('intro.points.reselect'), { duration: 0 });
});
}, 600); // after reveal..
context.on('enter.intro', function(mode) {
if (mode.id !== 'select') return;
continueTo(updatePoint);
});
}, msec + 100);
function continueTo(nextStep) {
context.map().on('move.intro drawn.intro', null);
context.on('enter.intro', null);
nextStep();
}
}
function updatePoint() {
if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
return continueTo(reselectPoint);
}
// reset pane, in case user happened to untag the point..
context.container().select('.inspector-wrap .panewrap').style('right', '0%');
context.on('exit.intro', function() {
continueTo(reselectPoint);
});
context.history().on('change.intro', function() {
continueTo(updateCloseEditor);
});
timeout(function() {
reveal('.entity-editor-pane', helpHtml('intro.points.update'),
{ tooltipClass: 'intro-points-describe' }
);
}, 400);
function continueTo(nextStep) {
context.on('exit.intro', null);
context.history().on('change.intro', null);
nextStep();
}
}
function updateCloseEditor() {
if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
return continueTo(reselectPoint);
}
// reset pane, in case user happened to change it..
context.container().select('.inspector-wrap .panewrap').style('right', '0%');
context.on('exit.intro', function() {
continueTo(rightClickPoint);
});
timeout(function() {
reveal('.entity-editor-pane',
helpHtml('intro.points.update_close', { button: icon('#iD-icon-close', 'inline') })
);
}, 500);
function continueTo(nextStep) {
context.on('exit.intro', null);
nextStep();
}
}
function rightClickPoint() {
if (!_pointID) return chapter.restart();
var entity = context.hasEntity(_pointID);
if (!entity) return chapter.restart();
context.enter(modeBrowse(context));
var box = pointBox(entity.loc, context);
var textId = context.lastPointerType() === 'mouse' ? 'rightclick' : 'edit_menu_touch';
reveal(box, helpHtml('intro.points.' + textId), { duration: 600 });
timeout(function() {
context.map().on('move.intro', function() {
var entity = context.hasEntity(_pointID);
if (!entity) return chapter.restart();
var box = pointBox(entity.loc, context);
reveal(box, helpHtml('intro.points.' + textId), { duration: 0 });
});
}, 600); // after reveal
context.on('enter.intro', function(mode) {
if (mode.id !== 'select') return;
var ids = context.selectedIDs();
if (ids.length !== 1 || ids[0] !== _pointID) return;
timeout(function() {
var node = selectMenuItem(context, 'delete').node();
if (!node) return;
continueTo(enterDelete);
}, 50); // after menu visible
});
function continueTo(nextStep) {
context.on('enter.intro', null);
context.map().on('move.intro', null);
nextStep();
}
}
function enterDelete() {
if (!_pointID) return chapter.restart();
var entity = context.hasEntity(_pointID);
if (!entity) return chapter.restart();
var node = selectMenuItem(context, 'delete').node();
if (!node) { return continueTo(rightClickPoint); }
reveal('.edit-menu',
helpHtml('intro.points.delete'),
{ padding: 50 }
);
timeout(function() {
context.map().on('move.intro', function() {
reveal('.edit-menu',
helpHtml('intro.points.delete'),
{ duration: 0, padding: 50 }
);
});
}, 300); // after menu visible
context.on('exit.intro', function() {
if (!_pointID) return chapter.restart();
var entity = context.hasEntity(_pointID);
if (entity) return continueTo(rightClickPoint); // point still exists
});
context.history().on('change.intro', function(changed) {
if (changed.deleted().length) {
continueTo(undo);
}
});
function continueTo(nextStep) {
context.map().on('move.intro', null);
context.history().on('change.intro', null);
context.on('exit.intro', null);
nextStep();
}
}
function undo() {
context.history().on('change.intro', function() {
continueTo(play);
});
reveal('.top-toolbar button.undo-button',
helpHtml('intro.points.undo')
);
function continueTo(nextStep) {
context.history().on('change.intro', null);
nextStep();
}
}
function play() {
dispatch.call('done');
reveal('.ideditor',
helpHtml('intro.points.play', { next: t('intro.areas.title') }), {
tooltipBox: '.intro-nav-wrap .chapter-area',
buttonText: t.html('intro.ok'),
buttonCallback: function() { reveal('.ideditor'); }
}
);
}
chapter.enter = function() {
addPoint();
};
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);
context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
};
chapter.restart = function() {
chapter.exit();
chapter.enter();
};
return utilRebind(chapter, dispatch, 'on');
}