/** * 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 Popup, { customElement, html } from "@ff/ui/Popup"; import "@ff/ui/Button"; import "@ff/ui/TextEdit"; import CVLanguageManager from "client/components/CVLanguageManager"; import {getFocusableElements, focusTrap} from "../../utils/focusHelpers"; import { IButtonClickEvent, IButtonKeyboardEvent } from "@ff/ui/Button"; import { ENavigationType } from "client/schema/setup"; //////////////////////////////////////////////////////////////////////////////// enum EHelpSection { Nav, Menu } @customElement("sv-main-help") export default class HelpMain extends Popup { protected url: string; protected language: CVLanguageManager = null; protected navMode: ENavigationType = ENavigationType.Orbit; protected helpView: EHelpSection = EHelpSection.Nav; protected static readonly leftClick = html``; protected static readonly rightClick = html``; protected static readonly mouseWheel = html``; protected static readonly pinch = html` `; protected static readonly oneFinger = html` `; protected static readonly twoFinger = html` `; protected static readonly arrowKeys = html` `; protected static readonly arrowKeysUD = html` `; protected static readonly arrowKeysLR = html` `; static show(parent: HTMLElement, language: CVLanguageManager, navMode: ENavigationType): Promise { const menu = new HelpMain(parent, language, navMode); parent.appendChild(menu); return new Promise((resolve, reject) => { menu.on("close", () => resolve()); }); } constructor(parent: HTMLElement, language: CVLanguageManager, navMode: ENavigationType) { super(); this.language = language; this.navMode = navMode; this.position = "center"; this.modal = true; this.portal = parent; this.url = window.location.href; } close() { this.dispatchEvent(new CustomEvent("close")); this.remove(); } protected firstConnected() { super.firstConnected(); this.classList.add("sv-main-help"); } protected render() { const language = this.language; const navMode = this.navMode; const isOrbit = navMode === ENavigationType.Orbit; const section = this.helpView; const arVisible = this.parentElement.querySelector("#ar-btn") ? true : false; const audioVisible = this.parentElement.querySelector("#audio-btn") ? true : false; const toursVisible = this.parentElement.querySelector("#tour-btn") ? true : false; const readerVisible = this.parentElement.querySelector("#reader-btn") ? true : false; const annosVisible = this.parentElement.querySelector("#anno-btn") ? true : false; const fsVisible = this.parentElement.querySelector("#fullscreen-btn") ? true : false; const toolsVisible = this.parentElement.querySelector("#tools-btn") ? true : false; const fpTileOption = html`
Ctrl + ${HelpMain.arrowKeysUD}Arrow keys
`; const navContent = html`
${isOrbit ? 'Orbit' : 'Look Around'}
${HelpMain.leftClick}Left-click and drag
Or.
${HelpMain.oneFinger}One-finger drag
Or.
${isOrbit ? HelpMain.arrowKeys : HelpMain.arrowKeysLR}Arrow keys
${!isOrbit ? fpTileOption : null}
Pan
${HelpMain.rightClick}Right-click and drag
Or.
${HelpMain.twoFinger}Two-finger drag
Or.
Shift + ${HelpMain.arrowKeys}Arrow keys
${isOrbit ? 'Zoom' : 'Forward/Backward'}
${HelpMain.mouseWheel}Mouse wheel
Or.
${HelpMain.pinch}Two-finger pinch
Or.
${isOrbit ? 'Ctrl + ' : ''}${isOrbit ? HelpMain.arrowKeys : HelpMain.arrowKeysUD}Arrow keys
`; const menuContent = html`
The tools below can be accessed by clicking the corresponding icons on the menu bar to the left of the screen.
${arVisible ? html`
Launch an augmented
reality experience.
`:""} ${audioVisible ? html`
Hear an audio
narration of the scene.
`:""} ${toursVisible ? html`
Take a curated guided
tour of the scene.
`:""} ${readerVisible ? html`
Read articles about
the scene content.
`:""} ${annosVisible ? html`
Show annotations
highlighting key points.
`:""}
Share the experience
with a friend!
${fsVisible ? html`
View the experience
in fullscreen mode.
`:""} ${toolsVisible ? html`
Open the advanced
tool menu.
`:""}
`; return html`
this.onKeyDownMain(e)}>
${language.getLocalizedString("Introduction to Voyager")}
this.onClickSection(e, EHelpSection.Nav)}> this.onClickSection(e, EHelpSection.Menu)}>
${section === EHelpSection.Nav ? navContent : menuContent}
`; } protected firstUpdated(changedProperties) { super.firstUpdated(changedProperties); (Array.from(this.getElementsByClassName("ff-button")).find(elem => elem.getAttribute("text") === "Navigation") as HTMLElement).focus(); // trigger screen reader on first pass setTimeout(() => {this.querySelector("#sr-trigger").textContent = "section end"}, 100); } protected onClickSection(event: IButtonClickEvent, idx: number) { this.helpView = idx; this.requestUpdate(); } protected onKeyDownMain(e: KeyboardEvent) { if (e.code === "Escape") { this.close(); } else if(e.code === "Tab") { focusTrap(getFocusableElements(this) as HTMLElement[], e); } } // resets tabIndex if needed protected tabReset(e: FocusEvent) { const currentActive = e.target instanceof Element ? e.target as Element : null; if(currentActive) { const currentSelected = Array.from(currentActive.parentElement.children).find(elem => elem.hasAttribute("selected")); if(currentSelected !== currentActive) { currentActive.setAttribute("tabIndex", "-1"); currentSelected.setAttribute("tabIndex", "0"); } } } }