UNPKG

@d3plus/text

Version:

A smart SVG text box with line wrapping and automatic font size scaling.

101 lines 9.12 kB
/* @d3plus/text v3.0.6 A smart SVG text box with line wrapping and automatic font size scaling. Copyright (c) 2025 D3plus - https://d3plus.org @license MIT */ (e=>{"function"==typeof define&&define.amd?define(e):e()})(function(){if("undefined"!=typeof window){try{if("undefined"==typeof SVGElement||Boolean(SVGElement.prototype.innerHTML))return}catch(e){return}function r(e){switch(e.nodeType){case 1:var t=e,n="";return n+="<"+t.tagName,t.hasAttributes()&&[].forEach.call(t.attributes,function(e){n+=" "+e.name+'="'+e.value+'"'}),n+=">",t.hasChildNodes()&&[].forEach.call(t.childNodes,function(e){n+=r(e)}),n+="</"+t.tagName+">";case 3:return e.textContent.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");case 8:return"\x3c!--"+e.nodeValue+"--\x3e"}}Object.defineProperty(SVGElement.prototype,"innerHTML",{get:function(){var t="";return[].forEach.call(this.childNodes,function(e){t+=r(e)}),t},set:function(e){for(;this.firstChild;)this.removeChild(this.firstChild);try{var t=new DOMParser,n=(t.async=!1,"<svg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'>"+e+"</svg>"),r=t.parseFromString(n,"text/xml").documentElement;[].forEach.call(r.childNodes,function(e){this.appendChild(this.ownerDocument.importNode(e,!0))}.bind(this))}catch(e){throw new Error("Error parsing markup string")}}}),Object.defineProperty(SVGElement.prototype,"innerSVG",{get:function(){return this.innerHTML},set:function(e){this.innerHTML=e}})}}),((e,t)=>{"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("linebreak"),require("@d3plus/dom")):"function"==typeof define&&define.amd?define("@d3plus/text",["exports","linebreak","@d3plus/dom"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).d3plus={},e.LineBreaker,e.dom)})(this,function(e,u,x){ /** @function stringify @desc Coerces value into a String. @param {String} value */function C(e){return void 0===e?e="undefined":"string"==typeof e||e instanceof String||(e=JSON.stringify(e)),e} // great unicode list: http://asecuritysite.com/coding/asc2 let i=[[/[\300-\305]/g,"A"],[/[\340-\345]/g,"a"],[/[\306]/g,"AE"],[/[\346]/g,"ae"],[/[\337]/g,"B"],[/[\307]/g,"C"],[/[\347]/g,"c"],[/[\320\336\376]/g,"D"],[/[\360]/g,"d"],[/[\310-\313]/g,"E"],[/[\350-\353]/g,"e"],[/[\314-\317]/g,"I"],[/[\354-\357]/g,"i"],[/[\321]/g,"N"],[/[\361]/g,"n"],[/[\u014c\322-\326\330]/g,"O"],[/[\u014d\362-\366\370]/g,"o"],[/[\u016a\331-\334]/g,"U"],[/[\u016b\371-\374]/g,"u"],[/[\327]/g,"x"],[/[\335]/g,"Y"],[/[\375\377]/g,"y"]]; /** @function strip @desc Removes all non ASCII characters from a string. @param {String} value @param {String} [spacer = "-"] */ /** @function textSplit @desc Splits a given sentence into an array of words. @param {String} sentence */function n(e){var t=new u(e);let n,r,i=[];for(;n=t.nextBreak();){var o=e.slice(r,n.position);i.push(o),r=n.position}return i} /** @function trim @desc Cross-browser implementation of [trim](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/Trim). @param {String} str */ /** @function trimRight @desc Cross-browser implementation of [trimRight](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/TrimRight). @param {String} str */function b(e){return e.toString().replace(/\s+$/,"")} /** @function textWrap @desc Based on the defined styles and dimensions, breaks a string into an array of strings for each line of text. */let o=["a","an","and","as","at","but","by","for","from","if","in","into","near","nor","of","on","onto","or","per","that","the","to","with","via","vs","vs."];var t=["CEO","CFO","CNC","COO","CPU","GDP","HVAC","ID","IT","R&D","TV","UI"];let a=t.reduce((e,t)=>(e.push(t+"s"),e),t.map(e=>e.toLowerCase())); /** @function titleCase @desc Capitalizes the first letter of each word in a phrase/sentence, accounting for words in English that should be kept lowercase such as "and" or "of", as well as acronym that should be kept uppercase such as "CEO" or "TVs". @param {String} str The string to apply the title case logic. */e.fontFamily=["Inter","Helvetica Neue","HelveticaNeue","Helvetica","Arial","sans-serif"],e.fontFamilyStringify=e=>("string"==typeof e?[e]:e).map(e=>e.match(/^[a-z-_]{1,}$/)?e:`'${e}'`).join(", "),e.stringify=C,e.strip=function(e,r="-"){return(""+e).replace(/[^A-Za-z0-9\-_\u0621-\u064A]/g,t=>{if(" "===t)return r;let n=!1;for(let e=0;e<i.length;e++)if(new RegExp(i[e][0]).test(t)){n=i[e][1];break}return n||""})},e.textSplit=n,e.textWrap=function(){let c="sans-serif",g=10,h=400,d=200,p,m=null,y=!1,w=n,v=200; /** The inner return object and wraps the text and returns the line data array. @private */function t(e){e=C(e),void 0===p&&(p=Math.ceil(1.4*g));var t=w(e),n={"font-family":c,"font-size":g,"font-weight":h,"line-height":p};let r=1,i="",o=!1,u=0;var a=[],l=x.textWidth(t,n);for(let e=0;e<t.length;e++){var f=t[e],s=l[t.indexOf(f)]; // newline if breaking character or not enough width if("\n"===i.slice(-1)||u+s>v){if(!e&&!y){o=!0;break}if(a.length>=r&&(a[r-1]=b(a[r-1])),r++,p*r>d||s>v&&!y||m&&r>m){o=!0;break}u=0,a.push(f)}else e?a[r-1]+=f:a[0]=f;i+=f,u+=s}return{lines:a,sentence:e,truncated:o,widths:x.textWidth(a,n),words:t}} /** @memberof textWrap @desc If *value* is specified, sets the font family accessor to the specified function or string and returns this generator. If *value* is not specified, returns the current font family. @param {Function|String} [*value* = "sans-serif"] */return t.fontFamily=function(e){return arguments.length?(c=e,t):c}, /** @memberof textWrap @desc If *value* is specified, sets the font size accessor to the specified function or number and returns this generator. If *value* is not specified, returns the current font size. @param {Function|Number} [*value* = 10] */t.fontSize=function(e){return arguments.length?(g=e,t):g}, /** @memberof textWrap @desc If *value* is specified, sets the font weight accessor to the specified function or number and returns this generator. If *value* is not specified, returns the current font weight. @param {Function|Number|String} [*value* = 400] */t.fontWeight=function(e){return arguments.length?(h=e,t):h}, /** @memberof textWrap @desc If *value* is specified, sets height limit to the specified value and returns this generator. If *value* is not specified, returns the current value. @param {Number} [*value* = 200] */t.height=function(e){return arguments.length?(d=e,t):d}, /** @memberof textWrap @desc If *value* is specified, sets the line height accessor to the specified function or number and returns this generator. If *value* is not specified, returns the current line height accessor, which is 1.1 times the [font size](#textWrap.fontSize) by default. @param {Function|Number} [*value*] */t.lineHeight=function(e){return arguments.length?(p=e,t):p}, /** @memberof textWrap @desc If *value* is specified, sets the maximum number of lines allowed when wrapping. @param {Function|Number} [*value*] */t.maxLines=function(e){return arguments.length?(m=e,t):m}, /** @memberof textWrap @desc If *value* is specified, sets the overflow to the specified boolean and returns this generator. If *value* is not specified, returns the current overflow value. @param {Boolean} [*value* = false] */t.overflow=function(e){return arguments.length?(y=e,t):y}, /** @memberof textWrap @desc If *value* is specified, sets the word split function to the specified function and returns this generator. If *value* is not specified, returns the current word split function. @param {Function} [*value*] A function that, when passed a string, is expected to return that string split into an array of words to textWrap. The default split function splits strings on the following characters: `-`, `/`, `;`, `:`, `&` */t.split=function(e){return arguments.length?(w=e,t):w}, /** @memberof textWrap @desc If *value* is specified, sets width limit to the specified value and returns this generator. If *value* is not specified, returns the current value. @param {Number} [*value* = 200] */t.width=function(e){return arguments.length?(v=e,t):v},t},e.titleCase=function(e){return void 0===e?"":n(e).reduce((e,t,n)=>{let r=t;var i=t.toLowerCase().slice(0,-1),n=a.includes(i)||o.includes(i)&&0!==n&&t.toLowerCase()!==i;return e+(r=n?r:t.charAt(0).toUpperCase()+t.slice(1))},"")},e.trim=function(e){return e.toString().replace(/^\s+|\s+$/g,"")} /** @function trimLeft @desc Cross-browser implementation of [trimLeft](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/TrimLeft). @param {String} str */,e.trimLeft=function(e){return e.toString().replace(/^\s+/,"")},e.trimRight=b}); //# sourceMappingURL=d3plus-text.js.map