123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158 |
- import {
- adopt,
- extend,
- nodeOrNew,
- register,
- wrapWithAttrCheck
- } from '../utils/adopter.js'
- import { registerMethods } from '../utils/methods.js'
- import SVGNumber from '../types/SVGNumber.js'
- import Shape from './Shape.js'
- import { globals } from '../utils/window.js'
- import * as textable from '../modules/core/textable.js'
- import { isDescriptive, writeDataToDom } from '../utils/utils.js'
-
- export default class Text extends Shape {
- // Initialize node
- constructor(node, attrs = node) {
- super(nodeOrNew('text', node), attrs)
-
- this.dom.leading = this.dom.leading ?? new SVGNumber(1.3) // store leading value for rebuilding
- this._rebuild = true // enable automatic updating of dy values
- this._build = false // disable build mode for adding multiple lines
- }
-
- // Set / get leading
- leading(value) {
- // act as getter
- if (value == null) {
- return this.dom.leading
- }
-
- // act as setter
- this.dom.leading = new SVGNumber(value)
-
- return this.rebuild()
- }
-
- // Rebuild appearance type
- rebuild(rebuild) {
- // store new rebuild flag if given
- if (typeof rebuild === 'boolean') {
- this._rebuild = rebuild
- }
-
- // define position of all lines
- if (this._rebuild) {
- const self = this
- let blankLineOffset = 0
- const leading = this.dom.leading
-
- this.each(function (i) {
- if (isDescriptive(this.node)) return
-
- const fontSize = globals.window
- .getComputedStyle(this.node)
- .getPropertyValue('font-size')
-
- const dy = leading * new SVGNumber(fontSize)
-
- if (this.dom.newLined) {
- this.attr('x', self.attr('x'))
-
- if (this.text() === '\n') {
- blankLineOffset += dy
- } else {
- this.attr('dy', i ? dy + blankLineOffset : 0)
- blankLineOffset = 0
- }
- }
- })
-
- this.fire('rebuild')
- }
-
- return this
- }
-
- // overwrite method from parent to set data properly
- setData(o) {
- this.dom = o
- this.dom.leading = new SVGNumber(o.leading || 1.3)
- return this
- }
-
- writeDataToDom() {
- writeDataToDom(this, this.dom, { leading: 1.3 })
- return this
- }
-
- // Set the text content
- text(text) {
- // act as getter
- if (text === undefined) {
- const children = this.node.childNodes
- let firstLine = 0
- text = ''
-
- for (let i = 0, len = children.length; i < len; ++i) {
- // skip textPaths - they are no lines
- if (children[i].nodeName === 'textPath' || isDescriptive(children[i])) {
- if (i === 0) firstLine = i + 1
- continue
- }
-
- // add newline if its not the first child and newLined is set to true
- if (
- i !== firstLine &&
- children[i].nodeType !== 3 &&
- adopt(children[i]).dom.newLined === true
- ) {
- text += '\n'
- }
-
- // add content of this node
- text += children[i].textContent
- }
-
- return text
- }
-
- // remove existing content
- this.clear().build(true)
-
- if (typeof text === 'function') {
- // call block
- text.call(this, this)
- } else {
- // store text and make sure text is not blank
- text = (text + '').split('\n')
-
- // build new lines
- for (let j = 0, jl = text.length; j < jl; j++) {
- this.newLine(text[j])
- }
- }
-
- // disable build mode and rebuild lines
- return this.build(false).rebuild()
- }
- }
-
- extend(Text, textable)
-
- registerMethods({
- Container: {
- // Create text element
- text: wrapWithAttrCheck(function (text = '') {
- return this.put(new Text()).text(text)
- }),
-
- // Create plain text element
- plain: wrapWithAttrCheck(function (text = '') {
- return this.put(new Text()).plain(text)
- })
- }
- })
-
- register(Text, 'Text')
|