From 0ae4099ff651ec1ca76961d86af662111d76008e Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Fri, 17 Aug 2018 17:26:12 -0400 Subject: [PATCH] Add custom data settings modal with file picker or vector tile url --- css/80_app.css | 20 +++- data/core.yaml | 10 ++ dist/locales/en.json | 13 +++ modules/ui/map_data.js | 69 ++++++++++---- modules/ui/settings/custom_background.js | 9 +- modules/ui/settings/custom_data.js | 113 +++++++++++++++++++++++ modules/ui/settings/index.js | 1 + 7 files changed, 207 insertions(+), 28 deletions(-) create mode 100644 modules/ui/settings/custom_data.js diff --git a/css/80_app.css b/css/80_app.css index 7749c193c..c33de8c30 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -3875,16 +3875,26 @@ svg.mouseclick use.right { /* Settings Modals ------------------------------------------------------- */ -.settings-custom-background .instructions { - margin-bottom: 20px; -} -.settings-custom-background textarea { +.settings-modal textarea { height: 60px; } -.settings-custom-background .buttons .button.col3 { +.settings-modal .buttons .button.col3 { float: none; /* undo float left */ } +.settings-custom-background .instructions-template { + margin-bottom: 20px; +} + + +.settings-custom-data .instructions-file { + margin-bottom: 10px; +} +.settings-custom-data .field-file, +.settings-custom-data .instructions-template { + margin-bottom: 20px; +} + /* Save Mode ------------------------------------------------------- */ diff --git a/data/core.yaml b/data/core.yaml index 62285567c..0faecc857 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -519,6 +519,16 @@ en: instructions: "Enter a tile URL template. Valid tokens are:\n {zoom} or {z}, {x}, {y} for Z/X/Y tile scheme\n {-y} or {ty} for flipped TMS-style Y coordinates\n {u} for quadtile scheme\n {switch:a,b,c} for DNS server multiplexing\n\nExample:\n{example}" template: placeholder: Enter a url template + custom_data: + tooltip: Edit custom data layer + header: Custom Data Settings + file: + instructions: "Choose a local data file. Supported types are:\n .gpx, .kml, .geojson/.json, .pbf, .mvt" + label: "Browse files" + or: "Or" + template: + instructions: "Enter a vector tile URL template. Valid tokens are:\n {zoom} or {z}, {x}, {y} for Z/X/Y tile scheme" + placeholder: Enter a url template restore: heading: You have unsaved changes description: "Do you wish to restore unsaved changes from a previous editing session?" diff --git a/dist/locales/en.json b/dist/locales/en.json index ca551b5a0..8489e25b1 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -638,6 +638,19 @@ "template": { "placeholder": "Enter a url template" } + }, + "custom_data": { + "tooltip": "Edit custom data layer", + "header": "Custom Data Settings", + "file": { + "instructions": "Choose a local data file. Supported types are:\n .gpx, .kml, .geojson/.json, .pbf, .mvt", + "label": "Browse files" + }, + "or": "Or", + "template": { + "instructions": "Enter a vector tile URL template. Valid tokens are:\n {zoom} or {z}, {x}, {y} for Z/X/Y tile scheme", + "placeholder": "Enter a url template" + } } }, "restore": { diff --git a/modules/ui/map_data.js b/modules/ui/map_data.js index d1f86a7b1..ad4ba47fd 100644 --- a/modules/ui/map_data.js +++ b/modules/ui/map_data.js @@ -12,6 +12,7 @@ import { modeBrowse } from '../modes'; import { uiBackground } from './background'; import { uiDisclosure } from './disclosure'; import { uiHelp } from './help'; +import { uiSettingsCustomData } from './settings/custom_data'; import { uiTooltipHtml } from './tooltipHtml'; @@ -21,6 +22,9 @@ export function uiMapData(context) { var layers = context.layers(); var fills = ['wireframe', 'partial', 'full']; + var settingsCustomData = uiSettingsCustomData(context) + .on('change', customChanged); + var _fillSelected = context.storage('area-fill') || 'partial'; var _shown = false; var _dataLayerContainer = d3_select(null); @@ -207,7 +211,7 @@ export function uiMapData(context) { } - function drawDataItems(selection) { + function drawCustomDataItems(selection) { var dataLayer = layers.layer('data'); var hasData = dataLayer && dataLayer.hasData(); var showsData = hasData && dataLayer.enabled(); @@ -231,7 +235,15 @@ export function uiMapData(context) { liEnter .append('button') - .attr('class', 'list-item-data-extent') + .call(tooltip() + .title(t('settings.custom_data.tooltip')) + .placement((textDirection === 'rtl') ? 'right' : 'left') + ) + .on('click', editCustom) + .call(svgIcon('#iD-icon-more')); + + liEnter + .append('button') .call(tooltip() .title(t('gpx.zoom')) .placement((textDirection === 'rtl') ? 'right' : 'left') @@ -243,22 +255,22 @@ export function uiMapData(context) { }) .call(svgIcon('#iD-icon-search')); - liEnter - .append('button') - .attr('class', 'list-item-data-browse') - .call(tooltip() - .title(t('gpx.browse')) - .placement((textDirection === 'rtl') ? 'right' : 'left') - ) - .on('click', function() { - d3_select(document.createElement('input')) - .attr('type', 'file') - .on('change', function() { - dataLayer.files(d3_event.target.files); - }) - .node().click(); - }) - .call(svgIcon('#iD-icon-geolocate')); + // liEnter + // .append('button') + // .attr('class', 'list-item-data-browse') + // .call(tooltip() + // .title(t('gpx.browse')) + // .placement((textDirection === 'rtl') ? 'right' : 'left') + // ) + // .on('click', function() { + // d3_select(document.createElement('input')) + // .attr('type', 'file') + // .on('change', function() { + // dataLayer.files(d3_event.target.files); + // }) + // .node().click(); + // }) + // .call(svgIcon('#iD-icon-geolocate')); var labelEnter = liEnter .append('label') @@ -290,6 +302,25 @@ export function uiMapData(context) { } + function editCustom() { + d3_event.preventDefault(); + context.container() + .call(settingsCustomData); + } + + + function customChanged(d) { +console.log('custom was changed'); + // if (d && d.template) { + // _customSource.template(d.template); + // chooseBackground(_customSource); + // } else { + // _customSource.template(''); + // chooseBackground(context.background().findSource('none')); + // } + } + + function drawListItems(selection, data, type, name, change, active) { var items = selection.selectAll('li') .data(data); @@ -381,7 +412,7 @@ export function uiMapData(context) { _dataLayerContainer .call(drawOsmItems) .call(drawPhotoItems) - .call(drawDataItems); + .call(drawCustomDataItems); _fillList .call(drawListItems, fills, 'radio', 'area_fill', setFill, showsFill); diff --git a/modules/ui/settings/custom_background.js b/modules/ui/settings/custom_background.js index e69176053..9b2fc4e05 100644 --- a/modules/ui/settings/custom_background.js +++ b/modules/ui/settings/custom_background.js @@ -19,7 +19,7 @@ export function uiSettingsCustomBackground(context) { var modal = uiConfirm(selection).okButton(); modal - .classed('settings-custom-background', true); + .classed('settings-modal settings-custom-background', true); modal.select('.modal-section.header') .append('h3') @@ -30,11 +30,12 @@ export function uiSettingsCustomBackground(context) { textSection .append('pre') - .attr('class', 'instructions') + .attr('class', 'instructions-template') .text(t('settings.custom_background.instructions', { example: example })); textSection .append('textarea') + .attr('class', 'field-template') .attr('placeholder', t('settings.custom_background.template.placeholder')) .call(utilNoAuto) .property('value', _currSettings.template); @@ -66,7 +67,7 @@ export function uiSettingsCustomBackground(context) { // restore the original template function clickCancel() { - textSection.select('textarea').property('value', _origSettings.template); + textSection.select('.field-template').property('value', _origSettings.template); context.storage('background-custom-template', _origSettings.template); this.blur(); modal.close(); @@ -74,7 +75,7 @@ export function uiSettingsCustomBackground(context) { // accept the current template function clickSave() { - _currSettings.template = textSection.select('textarea').property('value'); + _currSettings.template = textSection.select('.field-template').property('value'); context.storage('background-custom-template', _currSettings.template); this.blur(); modal.close(); diff --git a/modules/ui/settings/custom_data.js b/modules/ui/settings/custom_data.js new file mode 100644 index 000000000..26c7039c2 --- /dev/null +++ b/modules/ui/settings/custom_data.js @@ -0,0 +1,113 @@ +import _cloneDeep from 'lodash-es/cloneDeep'; + +import { dispatch as d3_dispatch } from 'd3-dispatch'; +import { event as d3_event } from 'd3-selection'; + +import { t } from '../../util/locale'; +import { uiConfirm } from '../confirm'; +import { utilNoAuto, utilRebind } from '../../util'; + + +export function uiSettingsCustomData(context) { + var dispatch = d3_dispatch('change'); + + function render(selection) { + var _origSettings = { + file: context.storage('settings-custom-data-file'), + template: context.storage('settings-custom-data-template') + }; + var _currSettings = _cloneDeep(_origSettings); + // var example = 'https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png'; + var modal = uiConfirm(selection).okButton(); + + modal + .classed('settings-modal settings-custom-data', true); + + modal.select('.modal-section.header') + .append('h3') + .text(t('settings.custom_data.header')); + + + var textSection = modal.select('.modal-section.message-text'); + + textSection + .append('pre') + .attr('class', 'instructions-file') + .text(t('settings.custom_data.file.instructions')); + + textSection + .append('input') + .attr('class', 'field-file') + .attr('type', 'file') + .on('change', function() { + var files = d3_event.target.files; + if (files && files.length) { + _currSettings.file = files[0]; + } else { + _currSettings.file = undefined; + } + }); + + textSection + .append('h4') + .text(t('settings.custom_data.or')); + + textSection + .append('pre') + .attr('class', 'instructions-template') + .text(t('settings.custom_data.template.instructions')); + + textSection + .append('textarea') + .attr('class', 'field-template') + .attr('placeholder', t('settings.custom_data.template.placeholder')) + .call(utilNoAuto) + .property('value', _currSettings.template); + + + // insert a cancel button, and adjust the button widths + var buttonSection = modal.select('.modal-section.buttons'); + + buttonSection + .insert('button', '.ok-button') + .attr('class', 'button col3 cancel-button secondary-action') + .text(t('confirm.cancel')); + + + buttonSection.select('.cancel-button') + .on('click.cancel', clickCancel); + + buttonSection.select('.ok-button') + .classed('col3', true) + .classed('col4', false) + .attr('disabled', isSaveDisabled) + .on('click.save', clickSave); + + + function isSaveDisabled() { + return null; + } + + + // restore the original template + function clickCancel() { + textSection.select('.field-template').property('value', _origSettings.template); + context.storage('settings-custom-data-template', _origSettings.template); + context.storage('settings-custom-data-file', _origSettings.file); + this.blur(); + modal.close(); + } + + // accept the current template + function clickSave() { + _currSettings.template = textSection.select('.field-template').property('value'); + context.storage('settings-custom-data-template', _currSettings.template); + context.storage('settings-custom-data-file', _currSettings.file); + this.blur(); + modal.close(); + dispatch.call('change', this, _currSettings); + } + } + + return utilRebind(render, dispatch, 'on'); +} diff --git a/modules/ui/settings/index.js b/modules/ui/settings/index.js index 52579d5d5..b8a047b26 100644 --- a/modules/ui/settings/index.js +++ b/modules/ui/settings/index.js @@ -1 +1,2 @@ export { uiSettingsCustomBackground } from './custom_background'; +export { uiSettingsCustomData } from './custom_data';