Published
Edited
Nov 24, 2019
Importers
1 star
Insert cell
Insert cell
Insert cell
svgLabel = {
const svg = d3.create('svg').attr('viewBox', [0, 0, 900, 200]);

const g = svg.append('g').attr('transform', 'translate(50, 50)');
g.append('rect')
.attr('width', 50)
.attr('height', 50)
.attr('opacity', 0.3);

return Object.assign(svg.node(), {
update(data) {
g.selectAll('g.parent')
.data(data)
.join(enter => enter.append('g').classed('parent', true))
.call(testLabel);
}
});
}
Insert cell
// Update the label outside of the svg itself to avoid rendering the whole thing when the data changes
svgLabel.update([
{ y: 0, text: 'Hi this is text that will wrap when it gets long' },
{ y: 100, text: 'more text, but shorter' }
])
Insert cell
// Testing the simpleLabel
testLabel = simpleLabel()
.text(d => d.text)
.y(d => d.y)
.width(200)
.clamp(false)
.padding(10)
Insert cell
Insert cell
// SimpleLabel
// Takes a context consisting of pre-joined set of points to add simple plaing text+background only labels to
// Parallel master version exists in d3eg, should eventually publish that on npm, also see TODO items there
// TODO: In layerMode, this must also take a key function to join the data
function simpleLabel() {
let x = () => 0,
y = () => 0,
text = d => d,
size = 16,
padding = 5,
margin = 5,
textWrap = wrap(100).clamp(false),
textDims = textWrap.store();

const simpleLabel = function(context, layerMode) {
// Get the selection from a context that will be either a selection or a transition
const selection = context.selection ? context.selection() : context;

// Set up a containing group for the label, using a data join function to cascade the data onto the child element
// Use the layerMode switch to provide ablity to add all labels to a single container
const g = selection
.selectAll('g.label')
.data(d => (layerMode ? d : [d]))
.join(enter => enter.append('g').classed('label', true))
.attr('transform', d => `translate(${x(d)}, ${y(d)})`);

// Add in text nodes
g.selectAll('text')
.data(d => [d])
.join('text')
.attr('dx', margin + padding)
.attr('dy', 0.35 * size)
.attr('font-size', size)
.text(d => text(d))
.call(textWrap);

// Add rectangles using insert to include them underneath the text
g.selectAll('rect')
.data(d => [d])
.join(enter => enter.insert('rect', 'text'))
.attr('width', function() {
// Get the adjacent text node, then return the width from the local variable
const textNode = d3
.select(this.parentElement)
.select('text')
.node();
return textDims.get(textNode).width + 2 * padding;
})
.attr('height', function() {
const textNode = d3
.select(this.parentElement)
.select('text')
.node();
return textDims.get(textNode).height + 2 * padding;
})
.attr('x', margin)
.attr('y', -0.5 * size - padding);
};

// Bind width and clamp accessors from the wrap function
mixin(simpleLabel, textWrap, ['width', 'clamp']);

// X position accessor, accepts a function only
simpleLabel.x = function(_) {
return arguments.length ? ((x = _), this) : x;
};

// Y position accessor, accepts a function only
simpleLabel.y = function(_) {
return arguments.length ? ((y = _), this) : y;
};

// Text label accessor, accepts a function only
simpleLabel.text = function(_) {
return arguments.length ? ((text = _), this) : text;
};

// Font size, accepts a value only
simpleLabel.size = function(_) {
return arguments.length ? ((size = _), this) : size;
};

// Label background padding, accepts a value only
simpleLabel.padding = function(_) {
return arguments.length ? ((padding = _), this) : padding;
};

// Label margin, accepts a value only
// Margin is the gap between the position and the reference point
simpleLabel.margin = function(_) {
return arguments.length ? ((margin = _), this) : margin;
};

return simpleLabel;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more