/** * 3D Foundation Project * Copyright 2025 Smithsonian Institution * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import CFullscreen from "@ff/scene/components/CFullscreen"; import CVARManager from "client/components/CVARManager"; import CVViewer from "client/components/CVViewer"; import CustomElement, { customElement, html } from "@ff/ui/CustomElement"; import Icon from "@ff/ui/Icon"; import Notification from "@ff/ui/Notification"; import ExplorerApplication, { IExplorerApplicationProps } from "../../applications/ExplorerApplication"; import ContentView from "./ContentView"; import ChromeView from "./ChromeView"; import styles from "./styles.scss"; //////////////////////////////////////////////////////////////////////////////// // EXPLORER ICONS Icon.add("globe", html``); Icon.add("cog", html``); Icon.add("eye", html``); Icon.add("palette", html``); Icon.add("comment", html``); //Icon.add("information", html``); Icon.add("article", html``); Icon.add("document", html``); Icon.add("share", html``); Icon.add("expand", html``); Icon.add("zoom", html``); Icon.add("tools", html``); Icon.add("environment", html``); Icon.add("bulb", html``); Icon.add("spot", html``); Icon.add("area", html``); Icon.add("sun", html``); Icon.add("half-sun", html``); Icon.add("tape", html``); Icon.add("knife", html``); Icon.add("bars", html``); Icon.add("triangle-left", html``); Icon.add("triangle-right", html``); Icon.add("twitter", html``); Icon.add("facebook", html``); Icon.add("instagram", html``); Icon.add("linkedin", html``); Icon.add("email", html``); Icon.add("copy", html``); Icon.add("ar", html``); Icon.add("device-move", html``); Icon.add("audio", html``); Icon.add("help", html`?`); Icon.add("move", html``); Icon.add("rotate", html``); Icon.add("pause", html``); Icon.add("caption", html``); Icon.add("undo", html``); //Icon.add("name", html``); //////////////////////////////////////////////////////////////////////////////// /** * Main UI view for the Voyager Explorer application. */ @customElement("voyager-explorer") export default class MainView extends CustomElement { application: ExplorerApplication = null; static get observedAttributes() { return ['root', 'document']; } constructor(application?: ExplorerApplication) { super(); if (application) { this.application = application; } this.addEventListener('focus', this.onFocus); } protected get fullscreen() { return this.application.system.getMainComponent(CFullscreen); } protected get arManager() { return this.application.system.getMainComponent(CVARManager); } protected get viewer() { return this.application.system.getComponent(CVViewer); } protected firstConnected() { super.firstConnected(); if (!this.application) { const props: IExplorerApplicationProps = { root: this.getAttribute("root"), dracoRoot: this.getAttribute("dracoRoot"), resourceRoot: this.getAttribute("resourceRoot"), document: this.getAttribute("document"), model: this.getAttribute("model"), geometry: this.getAttribute("geometry"), texture: this.getAttribute("texture"), quality: this.getAttribute("quality"), uiMode: this.getAttribute("uiMode"), bgColor: this.getAttribute("bgColor"), bgStyle: this.getAttribute("bgStyle"), controls: this.getAttribute("controls"), prompt: this.getAttribute("prompt"), reader: this.getAttribute("reader"), lang: this.getAttribute("lang") }; this.application = new ExplorerApplication(null, props); } this.attachShadow({mode: 'open'}); const shadowRoot = this.shadowRoot; // add style const styleElement = document.createElement("style"); styleElement.innerText = styles; shadowRoot.appendChild(styleElement); const system = this.application.system; shadowRoot.appendChild(new ContentView(system)); shadowRoot.appendChild(new ChromeView(system)); const notifications = document.createElement("div"); notifications.setAttribute("id", Notification.stackId); shadowRoot.appendChild(notifications); Notification.shadowRootNode = shadowRoot; //this.setAttribute("tabindex", "0"); const introAnnouncement = document.createElement("div"); introAnnouncement.classList.add("sr-only"); introAnnouncement.setAttribute("id", "sr-intro"); introAnnouncement.setAttribute("aria-live", "polite"); shadowRoot.appendChild(introAnnouncement); } protected connected() { this.fullscreen.fullscreenElement = this; this.viewer.rootElement = this; this.arManager.shadowRoot = this.shadowRoot; } protected disconnected() { super.disconnected(); this.fullscreen.fullscreenElement = null; this.viewer.rootElement = null; if(!window["VoyagerStory"]) { this.application.dispose(); this.application = null; } } attributeChangedCallback(name: string, old: string | null, value: string | null) { const app = this.application; super.attributeChangedCallback(name, old, value); if(app && name === "root") { const newRoot = this.getAttribute("root"); if(newRoot.length > 0) { app.props.root = newRoot app.reloadDocument(); this.connected(); } } else if(app && name === "document") { app.props.document = this.getAttribute("document"); app.reloadDocument(); this.connected(); } else if(app && name === "controls") { app.enableNavigation(value); } } protected onFocus() { this.shadowRoot.getElementById("sr-intro").innerText = "The Voyager web application allows you to view " + "and interact with a 3D model from the Smithsonian collection. Use the tab key to " + "move through interactive elements, enter or spacebar keys to activate, and the escape key to exit menus."; } //** Pass-through for API functions so they can be called from the main component element */ toggleAnnotations() { if(this.application) { this.application.toggleAnnotations(); } } setAnnotationsEnabled(visible: boolean) { if(this.application) { this.application.setAnnotationsEnabled(visible); } } toggleReader() { if(this.application) { this.application.toggleReader(); } } setReaderEnabled(enabled: boolean) { if(this.application) { this.application.setReaderEnabled(enabled); } } toggleTours() { if(this.application) { this.application.toggleTours(); } } setToursEnabled(enabled: boolean) { if(this.application) { this.application.setToursEnabled(enabled); } } toggleTools() { if(this.application) { this.application.toggleTools(); } } setToolsEnabled(visible: boolean) { if(this.application) { this.application.setToolsEnabled(visible); } } toggleMeasurement() { if(this.application) { this.application.toggleMeasurement(); } } setMeasurementEnabled(visible: boolean) { if(this.application) { this.application.setMeasurementEnabled(visible); } } enableAR() { if(this.application) { this.application.enableAR(); } } getArticles() { if(this.application) { return this.application.getArticles(); } } getAnnotations() { if(this.application) { return this.application.getAnnotations(); } } getCameraOrbit(type?: string) { if(this.application) { return this.application.getCameraOrbit(type ? type : null); } } setCameraOrbit( yaw: string, pitch: string) { if(this.application) { this.application.setCameraOrbit(yaw, pitch); } } getCameraOffset(type?: string) { if(this.application) { return this.application.getCameraOffset(type || null); } } setCameraOffset( x: string, y: string, z: string) { if(this.application) { this.application.setCameraOffset(x, y, z); } } setBackgroundColor(color0: string, color1?: string) { if(this.application) { this.application.setBackgroundColor(color0, color1 || null); } } setBackgroundStyle(style: string) { if(this.application) { this.application.setBackgroundStyle(style); } } setActiveAnnotation(id: string) { if(this.application) { this.viewer.ins.activeAnnotation.setValue(id); } } setActiveArticle(id: string) { if(this.application) { this.application.setActiveArticle(id); } } setTourStep(tourIdx: string, stepIdx: string, interpolate?: boolean) { if(this.application) { this.application.setTourStep(tourIdx, stepIdx, interpolate ?? true); } } getTours() { if(this.application) { return this.application.getTours(); } } setLanguage(languageID: string) { if(this.application) { this.application.setLanguage(languageID); } } getLanguages() { if(this.application) { return this.application.getLanguages(); } } getActiveLanguage() { if(this.application) { return this.application.getActiveLanguage(); } } resetViewer() { if(this.application) { this.application.resetViewer(); } } // get tags as displayed by the tag cloud getTags() { if(this.application) { return this.viewer.outs.tagCloud.value; } } // set active tags setActiveTags(tags: string) { if(this.application) { const viewerActiveTags = this.viewer.ins.activeTags; viewerActiveTags.setValue(tags); } } }