1let logger = () => null; 2 3function reveal() { 4 logger(`[reveal] elements ready, revealing the body`); 5 window.document.body.removeAttribute("unresolved"); 6} 7 8function autoReveal(logFunction) { 9 logger = logFunction; 10 // If Web Components are already ready, run the handler right away. If they 11 // are not yet ready, wait. 12 // 13 // see https://github.com/github/webcomponentsjs#webcomponents-loaderjs for 14 // info about web component readiness events 15 const polyfillPresent = window.WebComponents; 16 const polyfillReady = polyfillPresent && window.WebComponents.ready; 17 18 if (!polyfillPresent || polyfillReady) { 19 handleWebComponentsReady(); 20 } else { 21 window.addEventListener("WebComponentsReady", handleWebComponentsReady); 22 } 23} 24 25function handleWebComponentsReady() { 26 logger("[reveal] web components ready"); 27 reveal(); 28} 29 30/*! 31 * PatternFly Elements: PFElement 1.0.0-prerelease.55 32 * @license 33 * Copyright 2020 Red Hat, Inc. 34 * 35 * Permission is hereby granted, free of charge, to any person obtaining a copy 36 * of this software and associated documentation files (the "Software"), to deal 37 * in the Software without restriction, including without limitation the rights 38 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 39 * copies of the Software, and to permit persons to whom the Software is 40 * furnished to do so, subject to the following conditions: 41 * 42 * The above copyright notice and this permission notice shall be included in 43 * all copies or substantial portions of the Software. 44 * 45 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 46 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 47 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 48 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 49 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 50 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 51 * SOFTWARE. 52 * 53*/ 54const prefix = "pfe-"; 55 56class PFElement extends HTMLElement { 57 static create(pfe) { 58 window.customElements.define(pfe.tag, pfe); 59 } 60 61 static debugLog(preference = null) { 62 if (preference !== null) { 63 PFElement._debugLog = !!preference; 64 } 65 return PFElement._debugLog; 66 } 67 68 static log(...msgs) { 69 if (PFElement.debugLog()) { 70 console.log(...msgs); 71 } 72 } 73 74 static get PfeTypes() { 75 return { 76 Container: "container", 77 Content: "content", 78 Combo: "combo" 79 }; 80 } 81 82 static get version() { 83 return "{{version}}"; 84 } 85 86 static get observedAttributes() { 87 return ["pfe-theme"]; 88 } 89 90 get randomId() { 91 return ( 92 "pfe-" + 93 Math.random() 94 .toString(36) 95 .substr(2, 9) 96 ); 97 } 98 99 get version() { 100 return this._pfeClass.version; 101 } 102 103 get pfeType() { 104 return this.getAttribute(`${prefix}type`); 105 } 106 107 set pfeType(value) { 108 this.setAttribute(`${prefix}type`, value); 109 } 110 111 cssVariable(name, value, element = this) { 112 name = name.substr(0, 2) !== "--" ? "--" + name : name; 113 if (value) { 114 element.style.setProperty(name, value); 115 } 116 return window 117 .getComputedStyle(element) 118 .getPropertyValue(name) 119 .trim(); 120 } 121 122 // Returns a single element assigned to that slot; if multiple, it returns the first 123 has_slot(name) { 124 return this.querySelector(`[slot='${name}']`); 125 } 126 127 // Returns an array with all elements assigned to that slot 128 has_slots(name) { 129 return [...this.querySelectorAll(`[slot='${name}']`)]; 130 } 131 132 // Update the theme context for self and children 133 context_update() { 134 // TODO: update this to use :defined? 135 const children = this.querySelectorAll("[pfelement]"); 136 let theme = this.cssVariable("theme"); 137 138 // Manually adding `pfe-theme` overrides the css variable 139 if (this.hasAttribute("pfe-theme")) { 140 theme = this.getAttribute("pfe-theme"); 141 // Update the css variable to match the data attribute 142 this.cssVariable("theme", theme); 143 } 144 145 // Update theme for self 146 this.context_set(theme); 147 148 // For each nested, already upgraded component 149 // set the context based on the child's value of --theme 150 // Note: this prevents contexts from parents overriding 151 // the child's context should it exist 152 [...children].map(child => { 153 if (child.connected) { 154 child.context_set(theme); 155 } 156 }); 157 } 158 159 // Get the theme variable if it exists, set it as an attribute 160 context_set(fallback) { 161 let theme = this.cssVariable("theme"); 162 if (!theme) { 163 theme = this.getAttribute("pfe-theme"); 164 } 165 if (!theme && fallback) { 166 theme = fallback; 167 } 168 if (theme && this.hasAttribute("pfelement")) { 169 this.setAttribute("on", theme); 170 } 171 } 172 173 constructor(pfeClass, { type = null, delayRender = false } = {}) { 174 super(); 175 176 this.connected = false;
177 this._pfeClass = pfeClass; 178 this.tag = pfeClass.tag; 179 this.props = pfeClass.properties; 180 this.slots = pfeClass.slots; 181 this._queue = []; 182 this.template = document.createElement("template"); 183 184 this.log(`Constructing...`); 185 186 this.attachShadow({ mode: "open" }); 187 188 if (type) { 189 this._queueAction({ 190 type: "setProperty", 191 data: { 192 name: "pfeType", 193 value: type 194 } 195 }); 196 } 197 198 if (!delayRender) { 199 this.log(`Render...`); 200 this.render(); 201 this.log(`Rendered.`); 202 } 203 204 this.log(`Constructed.`); 205 } 206 207 connectedCallback() { 208 this.connected = true; 209 this.log(`Connecting...`); 210 211 if (window.ShadyCSS) { 212 this.log(`Styling...`); 213 window.ShadyCSS.styleElement(this); 214 this.log(`Styled.`); 215 } 216 217 // Throw a warning if the on attribute was manually added before upgrade 218 if (!this.hasAttribute("pfelement") && this.hasAttribute("on")) { 219 console.warn( 220 `${this.tag}${ 221 this.id ? `[#${this.id}]` : `` 222 }: The "on" attribute is protected and should not be manually added to a component. The base class will manage this value for you on upgrade.` 223 ); 224 } 225 226 // @TODO maybe we should use just the attribute instead of the class? 227 // https://github.com/angular/angular/issues/15399#issuecomment-318785677 228 this.classList.add("PFElement"); 229 this.setAttribute("pfelement", ""); 230 231 if (typeof this.props === "object") { 232 this._mapSchemaToProperties(this.tag, this.props); 233 this.log(`Properties attached.`); 234 } 235 236 if (typeof this.slots === "object") { 237 this._mapSchemaToSlots(this.tag, this.slots); 238 this.log(`Slots attached.`); 239 } 240 241 if (this._queue.length) { 242 this._processQueue(); 243 } 244 245 // Initialize the on attribute if a theme variable is set 246 // do not update the on attribute if a user has manually added it 247 // then trigger an update in nested components 248 this.context_update(); 249 250 this.log(`Connected.`); 251 } 252 253 disconnectedCallback() { 254 this.log(`Disconnecting...`); 255 256 this.connected = false; 257 258 this.log(`Disconnected.`); 259 } 260 261 attributeChangedCallback(attr, oldVal, newVal) { 262 if (!this._pfeClass.cascadingAttributes) { 263 return; 264 } 265 266 const cascadeTo = this._pfeClass.cascadingAttributes[attr]; 267 if (cascadeTo) { 268 this._copyAttribute(attr, cascadeTo); 269 } 270 271 if (attr === "pfe-theme") { 272 this.context_update(); 273 } 274 } 275 276 _copyAttribute(name, to) { 277 const recipients = [ 278 ...this.querySelectorAll(to), 279 ...this.shadowRoot.querySelectorAll(to) 280 ]; 281 const value = this.getAttribute(name); 282 const fname = value == null ? "removeAttribute" : "setAttribute"; 283 for (const node of recipients) { 284 node[fname](name, value); 285 } 286 } 287 288 // Map the imported properties json to real props on the element 289 // @notice static getter of properties is built via tooling 290 // to edit modify src/element.json 291 _mapSchemaToProperties(tag, properties) { 292 this.log("Mapping properties..."); 293 // Loop over the properties provided by the schema 294 Object.keys(properties).forEach(attr => { 295 let data = properties[attr]; 296 297 // Only attach the information if the data provided is a schema object 298 if (typeof data === "object") { 299 // Prefix default is true 300 let hasPrefix = true; 301 let attrName = attr; 302 // Set the attribute's property equal to the schema input 303 this[attr] = data; 304 // Initialize the value to null 305 this[attr].value = null; 306 307 if (typeof this[attr].prefixed !== "undefined") { 308 hasPrefix = this[attr].prefixed; 309 } 310 311 if (hasPrefix) { 312 attrName = `${prefix}${attr}`; 313 } 314 315 // If the attribute exists on the host 316 if (this.hasAttribute(attrName)) { 317 // Set property value based on the existing attribute 318 this[attr].value = this.getAttribute(attrName); 319 } 320 // Otherwise, look for a default and use that instead 321 else if (data.default) { 322 const dependency_exists = this._hasDependency(tag, data.options);
323 const no_dependencies = 324 !data.options || 325 (data.options && !data.options.dependencies.length); 326 // If the dependency exists or there are no dependencies, set the default 327 if (dependency_exists || no_dependencies) { 328 this.setAttribute(attrName, data.default); 329 this[attr].value = data.default; 330 } 331 } 332 } 333 }); 334 335 this.log("Properties mapped."); 336 } 337 338 // Test whether expected dependencies exist 339 _hasDependency(tag, opts) { 340 // Get any possible dependencies for this attribute to exist 341 let dependencies = opts ? opts.dependencies : []; 342 // Initialize the dependency return value 343 let hasDependency = false; 344 // Check that dependent item exists 345 // Loop through the dependencies defined 346 for (let i = 0; i < dependencies.length; i += 1) { 347 const slot_exists = 348 dependencies[i].type === "slot" && 349 this.has_slots(`${tag}--${dependencies[i].id}`).length > 0; 350 const attribute_exists = 351 dependencies[i].type === "attribute" && 352 this.getAttribute(`${prefix}${dependencies[i].id}`); 353 // If the type is slot, check that it exists OR 354 // if the type is an attribute, check if the attribute is defined 355 if (slot_exists || attribute_exists) { 356 // If the slot does exist, add the attribute with the default value 357 hasDependency = true; 358 // Exit the loop 359 break; 360 } 361 } 362 // Return a boolean if the dependency exists 363 return hasDependency; 364 } 365 366 // Map the imported slots json 367 // @notice static getter of properties is built via tooling 368 // to edit modify src/element.json 369 _mapSchemaToSlots(tag, slots) { 370 this.log("Validate slots..."); 371 // Loop over the properties provided by the schema 372 Object.keys(slots).forEach(slot => { 373 let slotObj = slots[slot]; 374 375 // Only attach the information if the data provided is a schema object 376 if (typeof slotObj === "object") { 377 let slotExists = false; 378 let result = []; 379 // If it's a named slot, look for that slot definition 380 if (slotObj.namedSlot) { 381 // Check prefixed slots 382 result = this.has_slots(`${tag}--${slot}`); 383 if (result.length > 0) { 384 slotObj.nodes = result; 385 slotExists = true; 386 } 387 388 // Check for unprefixed slots 389 result = this.has_slots(`${slot}`); 390 if (result.length > 0) { 391 slotObj.nodes = result; 392 slotExists = true; 393 } 394 // If it's the default slot, look for direct children not assigned to a slot 395 } else { 396 result = [...this.children].filter( 397 child => !child.hasAttribute("slot") 398 ); 399 400 if (result.length > 0) { 401 slotObj.nodes = result; 402 slotExists = true; 403 } 404 } 405 406 // If the slot exists, attach an attribute to the parent to indicate that 407 if (slotExists) { 408 this.setAttribute(`has_${slot}`, ""); 409 } else { 410 this.removeAttribute(`has_${slot}`); 411 } 412 } 413 }); 414 this.log("Slots validated."); 415 } 416 417 _queueAction(action) { 418 this._queue.push(action); 419 } 420 421 _processQueue() { 422 this._queue.forEach(action => { 423 this[`_${action.type}`](action.data); 424 }); 425 426 this._queue = []; 427 } 428 429 _setProperty({ name, value }) { 430 this[name] = value; 431 } 432 433 // @TODO This is a duplicate function to cssVariable above, combine them 434 static var(name, element = document.body) { 435 return window 436 .getComputedStyle(element) 437 .getPropertyValue(name) 438 .trim(); 439 } 440 441 var(name) { 442 return PFElement.var(name, this); 443 } 444 445 render() { 446 this.shadowRoot.innerHTML = ""; 447 this.template.innerHTML = this.html; 448 449 if (window.ShadyCSS) { 450 window.ShadyCSS.prepareTemplate(this.template, this.tag); 451 } 452 453 this.shadowRoot.appendChild(this.template.content.cloneNode(true)); 454 } 455 456 log(...msgs) { 457 PFElement.log(`[${this.tag}]`, ...msgs); 458 } 459 460 emitEvent( 461 name, 462 { bubbles = true, cancelable = false, composed = false, detail = {} } = {} 463 ) { 464 this.log(`Custom event: ${name}`); 465 this.dispatchEvent( 466 new CustomEvent(name, { 467 bubbles, 468 cancelable, 469 composed, 470 detail 471 }) 472 ); 473 } 474} 475 476autoReveal(PFElement.log); 477 478export default PFElement; 479//# sourceMappingURL=pfelement.js.map
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.