UNPKG

@needle-tools/engine

Version:

Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in.

162 lines (161 loc) 5.86 kB
import { isDevEnvironment } from "../debug/index.js"; import { iconFontUrl, loadFont } from "./fonts.js"; import { WebXRButtonFactory } from "./WebXRButtons.js"; const htmlTagName = "needle-button"; const isDev = isDevEnvironment(); /** * A <needle-button> can be used to simply add VR, AR or Quicklook buttons to your website without having to write any code. * @example * ```html * <needle-button ar></needle-button> * <needle-button vr></needle-button> * <needle-button quicklook></needle-button> * ``` * * @example custom label * ```html * <needle-button ar>Start AR</needle-button> * <needle-button vr>Start VR</needle-button> * <needle-button quicklook>View in AR</needle-button> * ``` * * @example custom styling * ```html * <!-- You can either style the element directly or use a CSS stylesheet --> * <style> * needle-button { * background-color: red; * color: white; * } * </style> * <needle-button ar>Start AR</needle-button> * ``` */ export class NeedleButtonElement extends HTMLElement { static observedAttributes = ["ar", "vr", "quicklook"]; constructor() { super(); this.removeEventListener("click", this.#onclick); this.addEventListener("click", this.#onclick); } attributeChangedCallback(_name, _oldValue, _newValue) { this.#update(); } #root; #slot; /** These are the default styles that can be overridden by the user from the outside by styling <needle-button> */ #styles; /** This is the button that was generated using one of the factories */ #button; /** If AR or VR is requested we create and use the webxr button factory to create a button with default behaviour */ #webxrfactory; #observer; #update() { this.#button?.remove(); if (this.getAttribute("ar") != null) { this.#webxrfactory ??= new WebXRButtonFactory(); this.#button = this.#webxrfactory.createARButton(); } else if (this.getAttribute("vr") != null) { this.#webxrfactory ??= new WebXRButtonFactory(); this.#button = this.#webxrfactory.createVRButton(); } else if (this.getAttribute("quicklook") != null) { this.#webxrfactory ??= new WebXRButtonFactory(); this.#button = this.#webxrfactory.createQuicklookButton(); } else { if (isDev) { console.warn("No button type specified for <needle-button>. Use either ar, vr or quicklook attribute."); } else { console.debug("No button type specified for <needle-button>. Use either ar, vr or quicklook attribute."); } return; } this.#root ??= this.attachShadow({ mode: "open" }); this.#slot ??= document.createElement("slot"); this.#styles ??= document.createElement("style"); this.#styles.innerHTML = ` button { all: initial; cursor: inherit; color: inherit; font-family: inherit; gap: inherit; white-space: nowrap; } `; const hasUnstyledAttribute = this.getAttribute("unstyled") != undefined; if (!hasUnstyledAttribute) { this.#styles.innerHTML += ` :host { display: inline-block; background: rgba(255, 255, 255, .8); backdrop-filter: blur(10px); width: fit-content; transition: background .2s; cursor: pointer; padding: 0.4rem .5rem; border-radius: 0.8rem; color: black; background: rgba(245, 245, 245, .8); outline: rgba(0,0,0,.05) 1px solid; } :host(:hover) { background: rgba(255, 255, 255, 1); transition: background .2s; } slot { display: flex; align-items: center; justify-content: center; gap: .5rem; } `; } /** * We now structure the results as follows: * <button> * <slot> * <original_button_content> * </slot> * </button> */ this.#slot.innerHTML = this.#button.innerHTML; this.#slot.style.cssText = `display: flex; align-items: center; justify-content: center;`; this.#button.innerHTML = this.#slot.outerHTML; this.#root.innerHTML = this.#button.outerHTML; this.#root.prepend(this.#styles); loadFont(iconFontUrl, { element: this.#root }); this.#observer?.disconnect(); this.#observer ??= new MutationObserver(() => this.#updateVisibility()); this.#observer.observe(this.#button, { attributes: true }); if (isDev) { console.log("Needle Button updated"); } } #updateVisibility() { if (this.#button) { if (this.#button.style.display === "none") { this.style.display = "none"; } else if (this.style.display === "none") { this.style.display = ""; } } } #onclick = (_ev) => { if (isDev) { console.log("Needle Button clicked"); } if (_ev.defaultPrevented) return; if (this.#button) { this.#button.click(); } }; } if (typeof window !== "undefined" && !window.customElements.get(htmlTagName)) window.customElements.define(htmlTagName, NeedleButtonElement); //# sourceMappingURL=needle-button.js.map