Model preset field state with persistent objects

This commit is contained in:
John Firebaugh
2013-03-25 16:01:05 -07:00
parent e43144c1ae
commit 43ab682262
9 changed files with 189 additions and 124 deletions
+160 -122
View File
@@ -1,109 +1,104 @@
iD.ui.preset = function(context, entity) {
var event = d3.dispatch('change', 'setTags', 'close'),
tags,
keys,
preset,
iD.ui.preset = function(context, entity, preset) {
var original = context.graph().base().entities[entity.id],
event = d3.dispatch('change', 'close'),
fields = [],
tags = {},
formwrap,
formbuttonwrap;
function presets(selection) {
selection.html('');
function UIField(field, show) {
field = _.clone(field);
keys = [];
formwrap = selection.append('div');
field.input = iD.ui.preset[field.type](field, context)
.on('close', event.close)
.on('change', event.change);
var geometry = entity.geometry(context.graph()),
fields = preset.fields.filter(function(f) {
return f.matchGeometry(geometry);
if (field.type === 'address') {
field.input.entity(entity);
}
field.keys = field.keys || [field.key];
field.show = show;
field.shown = function() {
return field.id === 'name' || field.show || _.any(field.keys, function(key) { return !!tags[key]; });
};
field.modified = function() {
return _.any(field.keys, function(key) {
return original ? tags[key] !== original.tags[key] : tags[key];
});
};
fields.unshift(context.presets().field('name'));
draw(formwrap, fields);
var wrap = selection.append('div')
.attr('class', 'col12 more-buttons inspector-inner');
formbuttonwrap = wrap.append('div')
.attr('class', 'col12 preset-input');
formbuttonwrap.selectAll('button')
.data(context.presets().universal().filter(notInForm))
.enter()
.append('button')
.attr('class', 'preset-add-field')
.on('click', addForm)
.call(bootstrap.tooltip()
.placement('top')
.title(function(d) { return d.label(); }))
.append('span')
.attr('class', function(d) { return 'icon ' + d.icon; });
function notInForm(p) {
return preset.fields.indexOf(p) < 0;
}
function addForm(d) {
var field = draw(formwrap, [d]);
var input = field.selectAll('input, textarea').node();
if (input) input.focus();
d3.select(this)
.style('opacity', 1)
.transition()
.style('opacity', 0)
.remove();
if (!wrap.selectAll('button').node()) {
wrap.remove();
}
}
return field;
}
function draw(selection, fields) {
var sections = selection.selectAll('div.form-field')
.data(fields, function(field) { return field.id; })
.enter()
.append('div')
fields.push(UIField(context.presets().field('name')));
var geometry = entity.geometry(context.graph());
preset.fields.forEach(function(field) {
if (field.matchGeometry(geometry)) {
fields.push(UIField(field, true));
}
});
context.presets().universal().forEach(function(field) {
if (fields.indexOf(field) < 0) {
fields.push(UIField(field));
}
});
function fieldKey(field) {
return field.id;
}
function shown() {
return fields.filter(function(field) { return field.shown(); });
}
function notShown() {
return fields.filter(function(field) { return !field.shown(); });
}
function show(field) {
field.show = true;
render();
field.input.focus();
}
function revert(field) {
var t = {};
field.keys.forEach(function(key) {
t[key] = original ? original.tags[key] : undefined;
});
event.change(t);
}
function toggleReference(field) {
_.forEach(fields, function(other) {
if (other.id === field.id) {
other.showingReference = !other.showingReference;
} else {
other.showingReference = false;
}
});
render();
}
function render() {
var selection = formwrap.selectAll('.form-field')
.data(shown(), fieldKey);
var enter = selection.enter()
.insert('div', '.more-buttons')
.style('opacity', 0)
.attr('class', function(field) {
return 'form-field form-field-' + field.id + ' fillL col12';
});
var label = sections.append('label')
.attr('class', 'form-label')
.attr('for', function(field) { return 'preset-input-' + field.id; })
.text(function(field) { return field.label(); });
label.append('button')
.attr('class', 'fr icon undo modified-icon')
.attr('tabindex', -1)
.on('click', function(field) {
var original = context.graph().base().entities[entity.id];
var t = {};
(field.keys || [field.key]).forEach(function(key) {
t[key] = original ? original.tags[key] : undefined;
});
event.change(t);
});
label.append('button')
.attr('class', 'tag-reference-button')
.attr('tabindex', -1)
.on('click', function(field) {
selection.selectAll('div.tag-help')
.style('display', 'none');
d3.select(d3.select(this).node().parentNode.parentNode)
.select('div.tag-help')
.style('display', 'block')
.call(iD.ui.TagReference(entity, {key: field.key}));
})
.append('span')
.attr('class', 'icon inspect');
sections.transition()
enter.transition()
.style('max-height', '0px')
.style('padding-top', '0px')
.style('opacity', '0')
@@ -113,30 +108,83 @@ iD.ui.preset = function(context, entity) {
.style('max-height', '200px')
.style('opacity', '1');
sections.each(function(field) {
var i = iD.ui.preset[field.type](field, context)
.on('close', event.close)
.on('change', event.change);
var label = enter.append('label')
.attr('class', 'form-label')
.attr('for', function(field) { return 'preset-input-' + field.id; })
.text(function(field) { return field.label(); });
event.on('setTags.' + field.id, function(tags) {
i.tags(_.clone(tags));
});
label.append('button')
.attr('class', 'fr icon undo modified-icon')
.attr('tabindex', -1)
.on('click', revert);
if (field.type === 'address') i.entity(entity);
label.append('button')
.attr('class', 'tag-reference-button')
.attr('tabindex', -1)
.on('click', toggleReference)
.append('span')
.attr('class', 'icon inspect');
keys = keys.concat(field.key ? [field.key] : field.keys);
d3.select(this).call(i);
enter.each(function(field) {
d3.select(this).call(field.input);
});
sections.append('div')
enter.append('div')
.attr('class', 'tag-help');
return sections;
selection
.classed('modified', function(field) {
return field.modified();
});
selection.selectAll('.tag-help')
.style('display', function(field) {
return field.showingReference ? 'block' : 'none';
})
.each(function(field) {
if (field.showingReference) {
d3.select(this)
.call(iD.ui.TagReference(entity, {key: field.key}));
}
});
selection.exit()
.remove();
var addFields = formbuttonwrap.selectAll('.preset-add-field')
.data(notShown(), fieldKey);
addFields.enter()
.append('button')
.attr('class', 'preset-add-field')
.on('click', show)
.call(bootstrap.tooltip()
.placement('top')
.title(function(d) { return d.label(); }))
.append('span')
.attr('class', function(d) { return 'icon ' + d.icon; });
addFields.exit()
.transition()
.style('opacity', 0)
.remove();
return selection;
}
function presets(selection) {
selection.html('');
formwrap = selection;
formbuttonwrap = selection.append('div')
.attr('class', 'col12 more-buttons inspector-inner');
render();
}
presets.rendered = function() {
return keys;
return _.flatten(shown().map(function(field) { return field.keys; }));
};
presets.preset = function(_) {
@@ -145,27 +193,17 @@ iD.ui.preset = function(context, entity) {
return presets;
};
presets.change = function(t) {
tags = t;
presets.change = function(_) {
tags = _;
function haveKey(k) { return k && !!tags[k]; }
formbuttonwrap.selectAll('button').each(function(p) {
if (haveKey(p.key) || _.any(p.keys, haveKey)) {
draw(formwrap, [p]);
d3.select(this).remove();
fields.forEach(function(field) {
if (field.shown()) {
field.input.tags(_);
}
});
formwrap.selectAll('div.form-field')
.classed('modified', function(d) {
var original = context.graph().base().entities[entity.id];
return _.any(d.keys || [d.key], function(key) {
return original ? tags[key] !== original.tags[key] : tags[key];
});
});
render();
event.setTags(tags);
return presets;
};
+4
View File
@@ -101,5 +101,9 @@ iD.ui.preset.address = function(field, context) {
return address;
};
address.focus = function() {
housename.node().focus();
};
return d3.rebind(address, event, 'on');
};
+4
View File
@@ -40,5 +40,9 @@ iD.ui.preset.check = function(field) {
label.classed('set', !!value);
};
check.focus = function() {
box.node().focus();
};
return d3.rebind(check, event, 'on');
};
+4
View File
@@ -48,5 +48,9 @@ iD.ui.preset.combo = function(field) {
input.property('value', tags[field.key] || '');
};
combo.focus = function() {
input.node().focus();
};
return d3.rebind(combo, event, 'on');
};
+4
View File
@@ -19,5 +19,9 @@ iD.ui.preset.defaultcheck = function(field) {
input.property('checked', !!tags[field.key] && tags[field.key] !== 'no');
};
check.focus = function() {
input.node().focus();
};
return d3.rebind(check, event, 'on');
};
+4
View File
@@ -52,5 +52,9 @@ iD.ui.preset.url = function(field) {
input.property('value', tags[field.key] || '');
};
i.focus = function() {
input.node().focus();
};
return d3.rebind(i, event, 'on');
};
+4
View File
@@ -52,5 +52,9 @@ iD.ui.preset.radio = function(field) {
});
};
radio.focus = function() {
buttons.node().focus();
};
return d3.rebind(radio, event, 'on');
};
+4
View File
@@ -23,5 +23,9 @@ iD.ui.preset.textarea = function(field) {
input.text(tags[field.key] || '');
};
i.focus = function() {
input.node().focus();
};
return d3.rebind(i, event, 'on');
};
+1 -2
View File
@@ -56,8 +56,7 @@ iD.ui.TagEditor = function(context, entity) {
.append('span')
.attr('class', geometry + ' preset-icon icon feature-' + icon);
presetUI = iD.ui.preset(context, entity)
.preset(preset)
presetUI = iD.ui.preset(context, entity, preset)
.on('change', changeTags)
.on('close', event.close);