UNPKG

visjs-network

Version:

A dynamic, browser-based network visualization library.

227 lines (194 loc) 5.61 kB
/** * Callback to determine text dimensions, using the parent label settings. * @callback MeasureText * @param {text} text * @param {text} mod * @return {Object} { width, values} width in pixels and font attributes */ /** * Helper class for Label which collects results of splitting labels into lines and blocks. * * @private */ class LabelAccumulator { /** * @param {MeasureText} measureText */ constructor(measureText) { this.measureText = measureText this.current = 0 this.width = 0 this.height = 0 this.lines = [] } /** * Append given text to the given line. * * @param {number} l index of line to add to * @param {string} text string to append to line * @param {'bold'|'ital'|'boldital'|'mono'|'normal'} [mod='normal'] * @private */ _add(l, text, mod = 'normal') { if (this.lines[l] === undefined) { this.lines[l] = { width: 0, height: 0, blocks: [] } } // We still need to set a block for undefined and empty texts, hence return at this point // This is necessary because we don't know at this point if we're at the // start of an empty line or not. // To compensate, empty blocks are removed in `finalize()`. // // Empty strings should still have a height let tmpText = text if (text === undefined || text === '') tmpText = ' ' // Determine width and get the font properties let result = this.measureText(tmpText, mod) let block = Object.assign({}, result.values) block.text = text block.width = result.width block.mod = mod if (text === undefined || text === '') { block.width = 0 } this.lines[l].blocks.push(block) // Update the line width. We need this for determining if a string goes over max width this.lines[l].width += block.width } /** * Returns the width in pixels of the current line. * * @returns {number} */ curWidth() { let line = this.lines[this.current] if (line === undefined) return 0 return line.width } /** * Add text in block to current line * * @param {string} text * @param {'bold'|'ital'|'boldital'|'mono'|'normal'} [mod='normal'] */ append(text, mod = 'normal') { this._add(this.current, text, mod) } /** * Add text in block to current line and start a new line * * @param {string} text * @param {'bold'|'ital'|'boldital'|'mono'|'normal'} [mod='normal'] */ newLine(text, mod = 'normal') { this._add(this.current, text, mod) this.current++ } /** * Determine and set the heights of all the lines currently contained in this instance * * Note that width has already been set. * * @private */ determineLineHeights() { for (let k = 0; k < this.lines.length; k++) { let line = this.lines[k] // Looking for max height of blocks in line let height = 0 if (line.blocks !== undefined) { // Can happen if text contains e.g. '\n ' for (let l = 0; l < line.blocks.length; l++) { let block = line.blocks[l] if (height < block.height) { height = block.height } } } line.height = height } } /** * Determine the full size of the label text, as determined by current lines and blocks * * @private */ determineLabelSize() { let width = 0 let height = 0 for (let k = 0; k < this.lines.length; k++) { let line = this.lines[k] if (line.width > width) { width = line.width } height += line.height } this.width = width this.height = height } /** * Remove all empty blocks and empty lines we don't need * * This must be done after the width/height determination, * so that these are set properly for processing here. * * @returns {Array<Line>} Lines with empty blocks (and some empty lines) removed * @private */ removeEmptyBlocks() { let tmpLines = [] for (let k = 0; k < this.lines.length; k++) { let line = this.lines[k] // Note: an empty line in between text has width zero but is still relevant to layout. // So we can't use width for testing empty line here if (line.blocks.length === 0) continue // Discard final empty line always if (k === this.lines.length - 1) { if (line.width === 0) continue } let tmpLine = {} Object.assign(tmpLine, line) tmpLine.blocks = [] let firstEmptyBlock let tmpBlocks = [] for (let l = 0; l < line.blocks.length; l++) { let block = line.blocks[l] if (block.width !== 0) { tmpBlocks.push(block) } else { if (firstEmptyBlock === undefined) { firstEmptyBlock = block } } } // Ensure that there is *some* text present if (tmpBlocks.length === 0 && firstEmptyBlock !== undefined) { tmpBlocks.push(firstEmptyBlock) } tmpLine.blocks = tmpBlocks tmpLines.push(tmpLine) } return tmpLines } /** * Set the sizes for all lines and the whole thing. * * @returns {{width: (number|*), height: (number|*), lines: Array}} */ finalize() { //console.log(JSON.stringify(this.lines, null, 2)); this.determineLineHeights() this.determineLabelSize() let tmpLines = this.removeEmptyBlocks() // Return a simple hash object for further processing. return { width: this.width, height: this.height, lines: tmpLines } } } export default LabelAccumulator