/** * 3D Foundation Project * Copyright 2026 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 CVModel2 from "client/components/CVModel2"; //////////////////////////////////////////////////////////////////////////////// @customElement("sv-img-import-menu") export default class ImageImportMenu extends Popup { protected url: string; protected language: CVLanguageManager = null; protected filename: string = ""; protected parentSelection: {name: string, id: string} = null; protected modelOptions: {name: string, id: string}[] = []; protected type: string = ""; protected errorString: string = ""; static show(parent: HTMLElement, language: CVLanguageManager, filename: string): Promise<[string, string]> { const menu = new ImageImportMenu(language, filename); parent.appendChild(menu); return new Promise((resolve, reject) => { menu.on("confirm", () => resolve([menu.type, menu.parentSelection?.name])); menu.on("close", () => reject()); }); } constructor( language: CVLanguageManager, filename: string ) { super(); this.language = language; this.filename = filename; this.modelOptions = this.modelOptions.concat(language.getGraphComponents(CVModel2).map(model => ({name: model.node.name, id: model.id}))); this.position = "center"; this.modal = true; this.parentSelection = this.modelOptions.length > 0 ? this.modelOptions[0] : {name: "Model"+this.modelOptions.length.toString(), id: "-1"}; this.url = window.location.href; } close() { this.dispatchEvent(new CustomEvent("close")); this.remove(); } confirm() { if(this.type === "overlay" && this.parentSelection === null) { this.errorString = this.language.getUILocalizedString("Please select model to overlay."); this.requestUpdate(); } else { this.dispatchEvent(new CustomEvent("confirm")); this.remove(); } } protected firstConnected() { super.firstConnected(); this.classList.add("sv-option-menu", "sv-import-menu"); } protected renderParentEntry(option: string, index: number) { return html`
this.onClickParent(e, index)} ?selected=${ option === this.parentSelection.name }> ${option}
`; } protected render() { const language = this.language; const modelSelect = this.type === "overlay" ? html`
${language.getUILocalizedString("Select Model:")}
${this.modelOptions.length > 0 ? html` ${this.modelOptions.map((option, index) => this.renderParentEntry(option.name, index))}` : html`
${language.getUILocalizedString("No Models In Scene")}
`}` : null; return html`
${language.getUILocalizedString("File:")} ${this.filename}
${language.getUILocalizedString("Select Import Type:")}
this.onType(e, "environment")} ?selected=${ this.type === "environment" }>Environment Map
this.onType(e, "overlay")} ?selected=${ this.type === "overlay" }>Overlay Map
this.onType(e, "plane")} ?selected=${ this.type === "plane" }>Image Plane
this.onType(e, "misc")} ?selected=${ this.type === "misc" }>Misc Asset
${modelSelect}
${this.errorString}
`; } protected onType(e: MouseEvent, type: string) { e.stopPropagation(); this.type = type; this.requestUpdate(); } protected onClickParent(e: MouseEvent, index: number) { e.stopPropagation(); this.parentSelection = this.modelOptions[index] || {name: (this.querySelector("#modelName") as HTMLInputElement).value, id: "-1"}; this.requestUpdate(); } protected onNameChange() { this.parentSelection.name = (this.querySelector("#modelName") as HTMLInputElement).value; } }