PageSourceSearch

https://www.jboss.org/js/pfelement/dist/pfelement.js

js jboss.org collected 2026-09-24 08:33:14 UTC 13,514 bytes, 479 lines download raw bytes

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.