1/** 2 * MMX / TEXT 3 */ 4class MMX_Text extends MMX_Element { 5 6 static get props() { 7 return { 8 align: { 9 options: [ 10 'left', 11 'center', 12 'right' 13 ], 14 default: 'unset' 15 }, 16 tag: { 17 options: [ 18 'h1', 19 'h2', 20 'h3', 21 'h4', 22 'h5', 23 'h6', 24 'p', 25 'div', 26 'span' 27 ], 28 default: 'div' 29 }, 30 theme: { 31 allowAny: true, 32 isBoolean: true, 33 default: false 34 }, 35 'theme-class': { 36 allowAny: true, 37 default: '' 38 }, 39 style: { 40 options: [ 41 'display-1', 42 'display-2', 43 'display-3', 44 'title-1', 45 'title-2', 46 'title-3', 47 'title-4', 48 'subheading-l', 49 'subheading-s', 50 'subheading-xs', 51 'paragraph-l', 52 'paragraph-s', 53 'paragraph-xs', 54 'call-to-action' 55 ], 56 allowAny: true, 57 default: 'paragraph-s' 58 }, 59 'chars-per-line': { 60 options: [ 61 'auto', 62 'ideal', 63 'none' 64 ], 65 default: 'auto', 66 allowAny: true 67 }, 68 'max-words': { 69 allowAny: true, 70 isNumeric: true 71 }, 72 'max-chars': { 73 allowAny: true, 74 isNumeric: true 75 }, 76 'trim-suffix': { 77 options: [ 78 '', 79 '...' 80 ], 81 default: '', 82 allowAny: true 83 }, 84 dom: { 85 options: ['light', 'shadow'], 86 default: 'light' 87 }, 88 source: { 89 options: ['markdown'], 90 default: '', 91 allowAny: true 92 }, 93 'hide-on-empty': { 94 allowAny: true, 95 isBoolean: true, 96 default: true 97 }, 98 spacing: { 99 options: ['auto', 'none'], 100 default: 'auto' 101 } 102 }; 103 } 104 105 styleResourceCodes = ['mmx-base', 'mmx-text', 'mm-theme-styles']; 106 107 copyTypes = [ 108 'paragraph-l', 109 'paragraph-s', 110 'paragraph-xs', 111 'call-to-action' 112 ]; 113 114 headingTagToStyle = { 115 h1: 'display-2', 116 h2: 'display-3', 117 h3: 'title-1', 118 h4: 'title-2', 119 h5: 'title-3', 120 h6: 'title-4' 121 }; 122 123 idealCharsPerLine = 28; // An ideal count for larger fonts like headings & promo content 124 125 constructor() { 126 super(); 127 this.makeComponent(); 128 } 129 130 render() { 131 const theme = this.getPropValue('theme'); 132 133 if (theme) return this.#renderTheme(); 134 else return this.#renderLegacy(); 135 } 136 137 get hideOnEmpty() { 138 return this.getPropValue('hide-on-empty'); 139 } 140 141 #renderLegacy() { 142 const tag = this.getPropValue('tag'); 143 const legacy_styles = this.#renderLegacyStyles(); 144 145 if (legacy_styles?.length) { 146 this.setAttribute('style', legacy_styles); 147 } 148 149 return /*html*/` 150 <${tag} class="mmx-text type-${this.getStyleProp()}
150 ${this.getStyleState()} ${this.#getSpacingClass()}" part="text" ${this.inheritAttrs()}> 151 <span class="mmx-text__inner" part="text__inner"> 152 ${this.renderContent()} 153 </span> 154 </${tag}> 155 `; 156 } 157 158 #renderLegacyStyles() { 159 return this.querySelector(':scope > template[data-legacy-styles]')?.content?.textContent?.trim?.() ?? ''; 160 } 161 162 #renderTheme() { 163 const tag = this.getPropValue('tag'); 164 const theme_class = this.getPropValue('theme-class'); 165 166 return /*html*/` 167 ${this.#renderThemeStylesheet()} 168 <${tag} class="mmx-theme-text ${MMX.encodeEntities(theme_class)} ${this.#getSpacingClass()}" ${this.inheritAttrs()}> 169 <span class="mmx-text__inner" part="text__inner"> 170 ${this.renderContent()} 171 </span> 172 </${tag}> 173 `; 174 } 175 176 #renderThemeStylesheet() { 177 const theme_stylesheet = this.querySelector(':scope > template[data-theme-stylesheet]'); 178 179 if (!theme_stylesheet) { 180 return ''; 181 } 182 183 return /*html*/` 184 <style type="text/css"> 185 ${theme_stylesheet.content.textContent} 186 </style> 187 `; 188 } 189 190 styles() { 191 return /*css*/` 192 :host { 193 --mmx-text__inner--max-width: ${this.getInnerMaxWidth()}; 194 --mmx-text__text-align: ${this.getPropValue('align')}; 195 } 196 197 ${this.shadowDomStyles()} 198 `; 199 } 200 201 shadowDomStyles() { 202 const style = this.getAttribute('style'); 203 204 if (!style || !this.shouldRenderInShadowDom()) { 205 return ''; 206 } 207 208 return /*css*/` 209 :where(h1, h2, h3, h4, h5, h6) { 210 ${style}; 211 } 212 `; 213 } 214 215 renderContent() { 216 const maxChars = this.getPropValue('max-chars'); 217 const maxWords = this.getPropValue('max-words'); 218 219 if (typeof maxChars === 'number') { 220 return this.renderContentByMaxChars(maxChars); 221 } 222 223 return this.renderContentByMaxWords(maxWords); 224 } 225 226 renderContentByMaxChars(maxChars) { 227 return this.renderMaxContent(maxChars, '', ''); 228 } 229 230 renderContentByMaxWords(maxWords) { 231 return this.renderMaxContent(maxWords, /\s+/, ' '); 232 } 233 234 renderMaxContent(maxCount = 0, splitOn = '', joinWith = '') { 235 const parts = this.textContent.trim().split(splitOn); 236 if (typeof maxCount === 'undefined' || maxCount === 0 || parts.length <= maxCount) { 237 return this.renderSlottedContent(); 238 } 239 240 const trimmedParts = parts.slice(0, maxCount).join(joinWith); 241 return trimmedParts + this.getPropValue('trim-suffix'); 242 } 243 244 shouldRenderInShadowDom() { 245 return this.getPropValue('source') === 'markdown' || this.getPropValue('dom') === 'shadow'; 246 } 247 248 renderSlottedContent() { 249 if (this.shouldRenderInShadowDom()) { 250 return this.innerHTML; 251 } 252 253 return '<slot></slot>'; 254 } 255 256 getInnerMaxWidth() { 257 const charsPerLine = this.getPropValue('chars-per-line'); 258 259 if (charsPerLine === 'none') { 260 return 'unset'; 261 } 262 else if (charsPerLine === 'ideal') { 263 return this.idealCharsPerLine + 'ch'; 264 } 265 else if (charsPerLine === 'auto') { 266 const type = this.getPropValue('style'); 267 const isCopyType = this.copyTypes.includes(type); 268 269 if (isCopyType) { 270 return 'unset'; 271 } else { 272 return this.idealCharsPerLine + 'ch'; 273 } 274 } 275 else if (!isNaN(charsPerLine)) { 276 return charsPerLine + 'ch'; 277 } 278 279 return charsPerLine; 280 } 281 282 getStyleWithoutDefault() { 283 if (this.hasPropValue('style')) { 284 return this.getPropValue('style'); 285 } 286 287 if (this.hasPropValue('tag')) { 288 const tag = this.getPropValue('tag'); 289 if (tag in this.headingTagToStyle) { 290 return this.headingTagToStyle[tag]; 291 } 292 } 293 294 return ''; 295 } 296 297 getStyleProp() { 298 return this.getStyleWithoutDefault() || MMX_Text.props.style.default; 299 } 300 301 getStyleState() { 302 if (this.getStyleWithoutDefault()) { 303 return 'mmx-text--styled'; 304 } 305 306 return 'mmx-text--unstyled'; 307 } 308 309 #getSpacingClass() { 310 return 'mmx-text__spacing--' + this.getPropValue('spacing'); 311 } 312} 313 314if (!customElements.get('mmx-text')) { 315 customElements.define('mmx-text', MMX_Text); 316}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.