mirror of
https://github.com/FoggedLens/iD.git
synced 2026-03-08 04:11:23 +00:00
Fix issue where entries in the raw member editor wouldn't update after downloading the member manually (close #6358)
This commit is contained in:
@@ -21,13 +21,16 @@ import { utilDisplayName, utilDisplayType, utilHighlightEntities, utilNoAuto } f
|
||||
export function uiRawMemberEditor(context) {
|
||||
var taginfo = services.taginfo;
|
||||
var _entityID;
|
||||
var _contentSelection = d3_select(null);
|
||||
|
||||
function downloadMember(d) {
|
||||
d3_event.preventDefault();
|
||||
|
||||
// display the loading indicator
|
||||
d3_select(this.parentNode).classed('tag-reference-loading', true);
|
||||
context.loadEntity(d.id);
|
||||
context.loadEntity(d.id, function() {
|
||||
updateDisclosureContent(_contentSelection);
|
||||
});
|
||||
}
|
||||
|
||||
function zoomToMember(d) {
|
||||
@@ -87,11 +90,11 @@ export function uiRawMemberEditor(context) {
|
||||
}
|
||||
}
|
||||
|
||||
function updateDisclosureContent(selection) {
|
||||
_contentSelection = selection;
|
||||
|
||||
function rawMemberEditor(selection) {
|
||||
var entity = context.entity(_entityID);
|
||||
var memberships = [];
|
||||
|
||||
var entity = context.entity(_entityID);
|
||||
entity.members.slice(0, 1000).forEach(function(member, index) {
|
||||
memberships.push({
|
||||
index: index,
|
||||
@@ -103,9 +106,269 @@ export function uiRawMemberEditor(context) {
|
||||
});
|
||||
});
|
||||
|
||||
var list = selection.selectAll('.member-list')
|
||||
.data([0]);
|
||||
|
||||
list = list.enter()
|
||||
.append('ul')
|
||||
.attr('class', 'member-list')
|
||||
.merge(list);
|
||||
|
||||
|
||||
var items = list.selectAll('li')
|
||||
.data(memberships, function(d) {
|
||||
return osmEntity.key(d.relation) + ',' + d.index + ',' +
|
||||
(d.member ? osmEntity.key(d.member) : 'incomplete');
|
||||
});
|
||||
|
||||
items.exit()
|
||||
.each(unbind)
|
||||
.remove();
|
||||
|
||||
var itemsEnter = items.enter()
|
||||
.append('li')
|
||||
.attr('class', 'member-row form-field')
|
||||
.classed('member-incomplete', function(d) { return !d.member; });
|
||||
|
||||
itemsEnter
|
||||
.each(function(d) {
|
||||
var item = d3_select(this);
|
||||
|
||||
var label = item
|
||||
.append('label')
|
||||
.attr('class', 'field-label');
|
||||
|
||||
if (d.member) {
|
||||
// highlight the member feature in the map while hovering on the list item
|
||||
item
|
||||
.on('mouseover', function() {
|
||||
utilHighlightEntities([d.id], true, context);
|
||||
})
|
||||
.on('mouseout', function() {
|
||||
utilHighlightEntities([d.id], false, context);
|
||||
});
|
||||
|
||||
var labelLink = label
|
||||
.append('span')
|
||||
.attr('class', 'label-text')
|
||||
.append('a')
|
||||
.attr('href', '#')
|
||||
.on('click', selectMember);
|
||||
|
||||
labelLink
|
||||
.append('span')
|
||||
.attr('class', 'member-entity-type')
|
||||
.text(function(d) {
|
||||
var matched = context.presets().match(d.member, context.graph());
|
||||
return (matched && matched.name()) || utilDisplayType(d.member.id);
|
||||
});
|
||||
|
||||
labelLink
|
||||
.append('span')
|
||||
.attr('class', 'member-entity-name')
|
||||
.text(function(d) { return utilDisplayName(d.member); });
|
||||
|
||||
label
|
||||
.append('button')
|
||||
.attr('class', 'member-zoom')
|
||||
.attr('title', t('icons.zoom_to'))
|
||||
.attr('tabindex', -1)
|
||||
.call(svgIcon('#iD-icon-geolocate'))
|
||||
.on('click', zoomToMember);
|
||||
|
||||
} else {
|
||||
var labelText = label
|
||||
.append('span')
|
||||
.attr('class', 'label-text');
|
||||
|
||||
labelText
|
||||
.append('span')
|
||||
.attr('class', 'member-entity-type')
|
||||
.text(t('inspector.' + d.type, { id: d.id }));
|
||||
|
||||
labelText
|
||||
.append('span')
|
||||
.attr('class', 'member-entity-name')
|
||||
.text(t('inspector.incomplete', { id: d.id }));
|
||||
|
||||
label
|
||||
.append('button')
|
||||
.attr('class', 'member-download')
|
||||
.attr('title', t('icons.download'))
|
||||
.attr('tabindex', -1)
|
||||
.call(svgIcon('#iD-icon-load'))
|
||||
.on('click', downloadMember);
|
||||
}
|
||||
});
|
||||
|
||||
var wrapEnter = itemsEnter
|
||||
.append('div')
|
||||
.attr('class', 'form-field-input-wrap form-field-input-member');
|
||||
|
||||
wrapEnter
|
||||
.append('input')
|
||||
.attr('class', 'member-role')
|
||||
.property('type', 'text')
|
||||
.attr('maxlength', 255)
|
||||
.attr('placeholder', t('inspector.role'))
|
||||
.call(utilNoAuto);
|
||||
|
||||
wrapEnter
|
||||
.append('button')
|
||||
.attr('tabindex', -1)
|
||||
.attr('title', t('icons.remove'))
|
||||
.attr('class', 'remove form-field-button member-delete')
|
||||
.call(svgIcon('#iD-operation-delete'));
|
||||
|
||||
if (taginfo) {
|
||||
wrapEnter.each(bindTypeahead);
|
||||
}
|
||||
|
||||
var dragOrigin, targetIndex;
|
||||
|
||||
itemsEnter.call(d3_drag()
|
||||
.on('start', function() {
|
||||
dragOrigin = {
|
||||
x: d3_event.x,
|
||||
y: d3_event.y
|
||||
};
|
||||
targetIndex = null;
|
||||
})
|
||||
.on('drag', function(d, index) {
|
||||
var x = d3_event.x - dragOrigin.x,
|
||||
y = d3_event.y - dragOrigin.y;
|
||||
|
||||
if (!d3_select(this).classed('dragging') &&
|
||||
// don't display drag until dragging beyond a distance threshold
|
||||
Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) <= 5) return;
|
||||
|
||||
d3_select(this)
|
||||
.classed('dragging', true);
|
||||
|
||||
targetIndex = null;
|
||||
|
||||
selection.selectAll('li.member-row')
|
||||
.style('transform', function(d2, index2) {
|
||||
var node = d3_select(this).node();
|
||||
if (index === index2) {
|
||||
return 'translate(' + x + 'px, ' + y + 'px)';
|
||||
} else if (index2 > index && d3_event.y > node.offsetTop - node.offsetHeight) {
|
||||
if (targetIndex === null || index2 > targetIndex) {
|
||||
targetIndex = index2;
|
||||
}
|
||||
return 'translateY(-100%)';
|
||||
} else if (index2 < index && d3_event.y < node.offsetTop) {
|
||||
if (targetIndex === null || index2 < targetIndex) {
|
||||
targetIndex = index2;
|
||||
}
|
||||
return 'translateY(100%)';
|
||||
}
|
||||
return null;
|
||||
});
|
||||
})
|
||||
.on('end', function(d, index) {
|
||||
|
||||
if (!d3_select(this).classed('dragging')) {
|
||||
return;
|
||||
}
|
||||
|
||||
d3_select(this)
|
||||
.classed('dragging', false);
|
||||
|
||||
selection.selectAll('li.member-row')
|
||||
.style('transform', null);
|
||||
|
||||
if (targetIndex !== null) {
|
||||
// dragged to a new position, reorder
|
||||
context.perform(
|
||||
actionMoveMember(d.relation.id, index, targetIndex),
|
||||
t('operations.reorder_members.annotation')
|
||||
);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
// update
|
||||
items = items
|
||||
.merge(itemsEnter);
|
||||
|
||||
items.select('input.member-role')
|
||||
.property('value', function(d) { return d.role; })
|
||||
.on('blur', changeRole)
|
||||
.on('change', changeRole);
|
||||
|
||||
items.select('button.member-delete')
|
||||
.on('click', deleteMember);
|
||||
|
||||
|
||||
|
||||
function bindTypeahead(d) {
|
||||
var row = d3_select(this);
|
||||
var role = row.selectAll('input.member-role');
|
||||
var origValue = role.property('value');
|
||||
|
||||
function sort(value, data) {
|
||||
var sameletter = [];
|
||||
var other = [];
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
if (data[i].value.substring(0, value.length) === value) {
|
||||
sameletter.push(data[i]);
|
||||
} else {
|
||||
other.push(data[i]);
|
||||
}
|
||||
}
|
||||
return sameletter.concat(other);
|
||||
}
|
||||
|
||||
role.call(uiCombobox(context, 'member-role')
|
||||
.fetcher(function(role, callback) {
|
||||
// The `geometry` param is used in the `taginfo.js` interface for
|
||||
// filtering results, as a key into the `tag_members_fractions`
|
||||
// object. If we don't know the geometry because the member is
|
||||
// not yet downloaded, it's ok to guess based on type.
|
||||
var geometry;
|
||||
if (d.member) {
|
||||
geometry = context.geometry(d.member.id);
|
||||
} else if (d.type === 'relation') {
|
||||
geometry = 'relation';
|
||||
} else if (d.type === 'way') {
|
||||
geometry = 'line';
|
||||
} else {
|
||||
geometry = 'point';
|
||||
}
|
||||
|
||||
var rtype = entity.tags.type;
|
||||
taginfo.roles({
|
||||
debounce: true,
|
||||
rtype: rtype || '',
|
||||
geometry: geometry,
|
||||
query: role
|
||||
}, function(err, data) {
|
||||
if (!err) callback(sort(role, data));
|
||||
});
|
||||
})
|
||||
.on('cancel', function() {
|
||||
role.property('value', origValue);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function unbind() {
|
||||
var row = d3_select(this);
|
||||
|
||||
row.selectAll('input.member-role')
|
||||
.call(uiCombobox.off);
|
||||
}
|
||||
}
|
||||
|
||||
function rawMemberEditor(selection) {
|
||||
var entity = context.entity(_entityID);
|
||||
|
||||
var gt = entity.members.length > 1000 ? '>' : '';
|
||||
selection.call(uiDisclosure(context, 'raw_member_editor', true)
|
||||
.title(t('inspector.all_members') + ' (' + gt + memberships.length + ')')
|
||||
.title(t('inspector.all_members') + ' (' + gt + entity.members.slice(0, 1000).length + ')')
|
||||
.expanded(true)
|
||||
.updatePreference(false)
|
||||
.on('toggled', function(expanded) {
|
||||
@@ -113,270 +376,10 @@ export function uiRawMemberEditor(context) {
|
||||
selection.node().parentNode.scrollTop += 200;
|
||||
}
|
||||
})
|
||||
.content(content)
|
||||
.content(updateDisclosureContent)
|
||||
);
|
||||
|
||||
|
||||
function content(selection) {
|
||||
var list = selection.selectAll('.member-list')
|
||||
.data([0]);
|
||||
|
||||
list = list.enter()
|
||||
.append('ul')
|
||||
.attr('class', 'member-list')
|
||||
.merge(list);
|
||||
|
||||
|
||||
var items = list.selectAll('li')
|
||||
.data(memberships, function(d) {
|
||||
return osmEntity.key(d.relation) + ',' + d.index + ',' +
|
||||
(d.member ? osmEntity.key(d.member) : 'incomplete');
|
||||
});
|
||||
|
||||
items.exit()
|
||||
.each(unbind)
|
||||
.remove();
|
||||
|
||||
var itemsEnter = items.enter()
|
||||
.append('li')
|
||||
.attr('class', 'member-row form-field')
|
||||
.classed('member-incomplete', function(d) { return !d.member; });
|
||||
|
||||
itemsEnter
|
||||
.each(function(d) {
|
||||
var item = d3_select(this);
|
||||
|
||||
var label = item
|
||||
.append('label')
|
||||
.attr('class', 'field-label');
|
||||
|
||||
if (d.member) {
|
||||
// highlight the member feature in the map while hovering on the list item
|
||||
item
|
||||
.on('mouseover', function() {
|
||||
utilHighlightEntities([d.id], true, context);
|
||||
})
|
||||
.on('mouseout', function() {
|
||||
utilHighlightEntities([d.id], false, context);
|
||||
});
|
||||
|
||||
var labelLink = label
|
||||
.append('span')
|
||||
.attr('class', 'label-text')
|
||||
.append('a')
|
||||
.attr('href', '#')
|
||||
.on('click', selectMember);
|
||||
|
||||
labelLink
|
||||
.append('span')
|
||||
.attr('class', 'member-entity-type')
|
||||
.text(function(d) {
|
||||
var matched = context.presets().match(d.member, context.graph());
|
||||
return (matched && matched.name()) || utilDisplayType(d.member.id);
|
||||
});
|
||||
|
||||
labelLink
|
||||
.append('span')
|
||||
.attr('class', 'member-entity-name')
|
||||
.text(function(d) { return utilDisplayName(d.member); });
|
||||
|
||||
label
|
||||
.append('button')
|
||||
.attr('class', 'member-zoom')
|
||||
.attr('title', t('icons.zoom_to'))
|
||||
.attr('tabindex', -1)
|
||||
.call(svgIcon('#iD-icon-geolocate'))
|
||||
.on('click', zoomToMember);
|
||||
|
||||
} else {
|
||||
var labelText = label
|
||||
.append('span')
|
||||
.attr('class', 'label-text');
|
||||
|
||||
labelText
|
||||
.append('span')
|
||||
.attr('class', 'member-entity-type')
|
||||
.text(t('inspector.' + d.type, { id: d.id }));
|
||||
|
||||
labelText
|
||||
.append('span')
|
||||
.attr('class', 'member-entity-name')
|
||||
.text(t('inspector.incomplete', { id: d.id }));
|
||||
|
||||
label
|
||||
.append('button')
|
||||
.attr('class', 'member-download')
|
||||
.attr('title', t('icons.download'))
|
||||
.attr('tabindex', -1)
|
||||
.call(svgIcon('#iD-icon-load'))
|
||||
.on('click', downloadMember);
|
||||
}
|
||||
});
|
||||
|
||||
var wrapEnter = itemsEnter
|
||||
.append('div')
|
||||
.attr('class', 'form-field-input-wrap form-field-input-member');
|
||||
|
||||
wrapEnter
|
||||
.append('input')
|
||||
.attr('class', 'member-role')
|
||||
.property('type', 'text')
|
||||
.attr('maxlength', 255)
|
||||
.attr('placeholder', t('inspector.role'))
|
||||
.call(utilNoAuto);
|
||||
|
||||
wrapEnter
|
||||
.append('button')
|
||||
.attr('tabindex', -1)
|
||||
.attr('title', t('icons.remove'))
|
||||
.attr('class', 'remove form-field-button member-delete')
|
||||
.call(svgIcon('#iD-operation-delete'));
|
||||
|
||||
if (taginfo) {
|
||||
wrapEnter.each(bindTypeahead);
|
||||
}
|
||||
|
||||
var dragOrigin, targetIndex;
|
||||
|
||||
itemsEnter.call(d3_drag()
|
||||
.on('start', function() {
|
||||
dragOrigin = {
|
||||
x: d3_event.x,
|
||||
y: d3_event.y
|
||||
};
|
||||
targetIndex = null;
|
||||
})
|
||||
.on('drag', function(d, index) {
|
||||
var x = d3_event.x - dragOrigin.x,
|
||||
y = d3_event.y - dragOrigin.y;
|
||||
|
||||
if (!d3_select(this).classed('dragging') &&
|
||||
// don't display drag until dragging beyond a distance threshold
|
||||
Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) <= 5) return;
|
||||
|
||||
d3_select(this)
|
||||
.classed('dragging', true);
|
||||
|
||||
targetIndex = null;
|
||||
|
||||
selection.selectAll('li.member-row')
|
||||
.style('transform', function(d2, index2) {
|
||||
var node = d3_select(this).node();
|
||||
if (index === index2) {
|
||||
return 'translate(' + x + 'px, ' + y + 'px)';
|
||||
} else if (index2 > index && d3_event.y > node.offsetTop - node.offsetHeight) {
|
||||
if (targetIndex === null || index2 > targetIndex) {
|
||||
targetIndex = index2;
|
||||
}
|
||||
return 'translateY(-100%)';
|
||||
} else if (index2 < index && d3_event.y < node.offsetTop) {
|
||||
if (targetIndex === null || index2 < targetIndex) {
|
||||
targetIndex = index2;
|
||||
}
|
||||
return 'translateY(100%)';
|
||||
}
|
||||
return null;
|
||||
});
|
||||
})
|
||||
.on('end', function(d, index) {
|
||||
|
||||
if (!d3_select(this).classed('dragging')) {
|
||||
return;
|
||||
}
|
||||
|
||||
d3_select(this)
|
||||
.classed('dragging', false);
|
||||
|
||||
selection.selectAll('li.member-row')
|
||||
.style('transform', null);
|
||||
|
||||
if (targetIndex !== null) {
|
||||
// dragged to a new position, reorder
|
||||
context.perform(
|
||||
actionMoveMember(d.relation.id, index, targetIndex),
|
||||
t('operations.reorder_members.annotation')
|
||||
);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
// update
|
||||
items = items
|
||||
.merge(itemsEnter);
|
||||
|
||||
items.select('input.member-role')
|
||||
.property('value', function(d) { return d.role; })
|
||||
.on('blur', changeRole)
|
||||
.on('change', changeRole);
|
||||
|
||||
items.select('button.member-delete')
|
||||
.on('click', deleteMember);
|
||||
|
||||
|
||||
|
||||
function bindTypeahead(d) {
|
||||
var row = d3_select(this);
|
||||
var role = row.selectAll('input.member-role');
|
||||
var origValue = role.property('value');
|
||||
|
||||
function sort(value, data) {
|
||||
var sameletter = [];
|
||||
var other = [];
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
if (data[i].value.substring(0, value.length) === value) {
|
||||
sameletter.push(data[i]);
|
||||
} else {
|
||||
other.push(data[i]);
|
||||
}
|
||||
}
|
||||
return sameletter.concat(other);
|
||||
}
|
||||
|
||||
role.call(uiCombobox(context, 'member-role')
|
||||
.fetcher(function(role, callback) {
|
||||
// The `geometry` param is used in the `taginfo.js` interface for
|
||||
// filtering results, as a key into the `tag_members_fractions`
|
||||
// object. If we don't know the geometry because the member is
|
||||
// not yet downloaded, it's ok to guess based on type.
|
||||
var geometry;
|
||||
if (d.member) {
|
||||
geometry = context.geometry(d.member.id);
|
||||
} else if (d.type === 'relation') {
|
||||
geometry = 'relation';
|
||||
} else if (d.type === 'way') {
|
||||
geometry = 'line';
|
||||
} else {
|
||||
geometry = 'point';
|
||||
}
|
||||
|
||||
var rtype = entity.tags.type;
|
||||
taginfo.roles({
|
||||
debounce: true,
|
||||
rtype: rtype || '',
|
||||
geometry: geometry,
|
||||
query: role
|
||||
}, function(err, data) {
|
||||
if (!err) callback(sort(role, data));
|
||||
});
|
||||
})
|
||||
.on('cancel', function() {
|
||||
role.property('value', origValue);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function unbind() {
|
||||
var row = d3_select(this);
|
||||
|
||||
row.selectAll('input.member-role')
|
||||
.call(uiCombobox.off);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
rawMemberEditor.entityID = function(val) {
|
||||
if (!arguments.length) return _entityID;
|
||||
_entityID = val;
|
||||
|
||||
Reference in New Issue
Block a user