/**
* 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 ExplorerPanel from "./ExplorerPanel";
import localStorage from "@ff/browser/localStorage";
import StoryApplication, { IStoryApplicationProps } from "../../applications/StoryApplication";
import CustomElement, { customElement, html } from "@ff/ui/CustomElement";
import Icon from "@ff/ui/Icon";
import DockView, { DockContentRegistry, IDockElementLayout } from "@ff/ui/DockView";
import HierarchyTreeView from "@ff/scene/ui/HierarchyTreeView";
import NavigatorPanel from "./NavigatorPanel";
import CVTaskProvider, { ETaskMode } from "../../components/CVTaskProvider";
import TaskBar from "./TaskBar";
import EditorPanel from "./EditorPanel";
import TourPanel from "./TourPanel";
import TaskPanel from "./TaskPanel";
import NotesPanel from "./NotesPanel";
import ConsolePanel from "./ConsolePanel";
import InspectorPanel from "./InspectorPanel";
import AssetPanel from "./AssetPanel";
import CollectionPanel from "./CollectionPanel";
import "./styles.scss";
////////////////////////////////////////////////////////////////////////////////
// STORY ICONS
Icon.add("hierarchy", html``);
Icon.add("select", html``);
Icon.add("create", html``);
Icon.add("compress", html``);
Icon.add("camera", html``);
Icon.add("save", html``);
Icon.add("exit", html``);
Icon.add("expert", html``);
Icon.add("pen", html``);
Icon.add("redo", html``);
Icon.add("video", html``);
Icon.add("upload", html``);
Icon.add("download", html``);
Icon.add("trash", html``);
Icon.add("cube", html``);
Icon.add("brush", html``);
Icon.add("pointer", html``);
Icon.add("eraser", html``);
//Icon.add("lock", html``);
//Icon.add("unlock", html``);
////////////////////////////////////////////////////////////////////////////////
interface IUIState
{
regularLayout: IDockElementLayout;
expertLayout: IDockElementLayout;
expertMode: boolean;
}
@customElement("voyager-story")
export default class MainView extends CustomElement
{
static readonly stateKey: string = "main-view-2";
protected application: StoryApplication;
protected dockView: DockView;
protected registry: DockContentRegistry;
protected state: IUIState;
protected get taskProvider() {
return this.application.system.getMainComponent(CVTaskProvider);
}
get app() {
return this.application;
}
constructor(application?: StoryApplication)
{
super();
this.onUnload = this.onUnload.bind(this);
if (application) {
this.application = application;
}
else {
const props: IStoryApplicationProps = {
document: this.getAttribute("document"),
root: this.getAttribute("root"),
dracoRoot: this.getAttribute("dracoRoot"),
resourceRoot: this.getAttribute("resourceRoot"),
model: this.getAttribute("model"),
geometry: this.getAttribute("geometry"),
texture: this.getAttribute("texture"),
quality: this.getAttribute("quality"),
referrer: this.getAttribute("referrer"),
mode: this.getAttribute("mode"),
expert: this.hasAttribute("expert"),
dragdrop: this.hasAttribute("dragdrop"),
uiMode: this.getAttribute("uiMode"),
uiLang: this.getAttribute("uiLang")
};
this.application = new StoryApplication(null, props);
}
window["voyagerStory"] = this.application;
this.dockView = null;
const system = this.application.system;
const taskProvider = system.components.get(CVTaskProvider);
taskProvider.ins.mode.on("value", this.onTaskMode, this);
const registry = this.registry = new Map();
const explorer = this.application.explorer;
registry.set("explorer", () => new ExplorerPanel(explorer));
registry.set("tour-editor", () => new TourPanel(system));
registry.set("task", () => new TaskPanel(system));
registry.set("notes", () => new NotesPanel(system));
registry.set("console", () => new ConsolePanel(system));
registry.set("navigator", () => new NavigatorPanel(system));
registry.set("hierarchy", () => new HierarchyTreeView(system));
registry.set("inspector", () => new InspectorPanel(system));
registry.set("assets", () => new AssetPanel(system));
registry.set("article-editor", () => new EditorPanel(system));
registry.set("collection", () => new CollectionPanel(system));
const reset = new URL(window.location.href).searchParams.get("reset") !== null;
const state = reset ? null : localStorage.get("voyager-story", MainView.stateKey);
this.state = state || {
regularLayout: MainView.regularLayout,
expertLayout: MainView.expertLayout,
};
}
protected firstConnected()
{
this.setStyle({
display: "flex",
flexDirection: "column"
});
this.appendElement(new TaskBar(this.application.system));
this.dockView = this.appendElement(DockView);
this.restoreLayout();
window.addEventListener("beforeunload", this.onUnload);
}
protected disconnected()
{
this.storeLayout();
localStorage.set("voyager-story", MainView.stateKey, this.state);
}
protected onUnload()
{
this.storeLayout();
localStorage.set("voyager-story", MainView.stateKey, this.state);
}
protected onTaskMode(mode: ETaskMode)
{
this.storeLayout();
this.restoreLayout();
}
protected storeLayout()
{
const state = this.state;
const expertMode = this.taskProvider.expertMode;
if (expertMode) {
state.expertLayout = this.dockView.getLayout();
}
else {
state.regularLayout = this.dockView.getLayout();
}
}
protected restoreLayout()
{
const state = this.state;
const expertMode = this.taskProvider.expertMode;
this.dockView.setLayout(expertMode ? state.expertLayout : state.regularLayout, this.registry);
this.dockView.setPanelsMovable(true)
}
protected static readonly regularLayout: IDockElementLayout = {
type: "strip",
direction: "horizontal",
size: 1,
elements: [{
type: "strip",
direction: "vertical",
size: 0.22,
elements: [{
type: "stack",
size: 0.35,
activePanelIndex: 0,
panels: [{
contentId: "navigator",
text: "Navigator"
}, {
contentId: "assets",
text: "Media"
}, {
contentId: "collection",
text: "Collection"
}]
}, {
type: "stack",
size: 0.65,
activePanelIndex: 0,
panels: [{
contentId: "task",
text: "Task"
}]
}]
}, {
type: "strip",
direction: "vertical",
size: 0.78,
elements: [{
type: "stack",
size: 0.75,
activePanelIndex: 0,
panels: [{
contentId: "explorer",
text: "Explorer"
}]
}, {
type: "stack",
size: 0.25,
activePanelIndex: 0,
panels: [{
contentId: "article-editor",
text: "Article Editor"
}, {
contentId: "tour-editor",
text: "Tour Editor"
}, {
contentId: "notes",
text: "Note Editor"
}]
}]
}]
};
protected static readonly expertLayout: IDockElementLayout = {
type: "strip",
direction: "horizontal",
size: 1,
elements: [{
type: "strip",
direction: "vertical",
size: 0.2,
elements: [{
type: "stack",
size: 0.3,
activePanelIndex: 0,
panels: [{
contentId: "navigator",
text: "Navigator"
}, {
contentId: "assets",
text: "Media"
}, {
contentId: "collection",
text: "Collection"
}]
}, {
type: "stack",
size: 0.7,
activePanelIndex: 0,
panels: [{
contentId: "task",
text: "Task"
}]
}]
},{
type: "strip",
direction: "vertical",
size: 0.6,
elements: [{
type: "stack",
size: 0.8,
activePanelIndex: 0,
panels: [{
contentId: "explorer",
text: "Explorer"
}]
}, {
type: "stack",
size: 0.2,
activePanelIndex: 0,
panels: [{
contentId: "article-editor",
text: "Article Editor"
}, {
contentId: "tour-editor",
text: "Tour Editor"
}, {
contentId: "notes",
text: "Note Editor"
}, {
contentId: "console",
text: "Console"
}]
}]
}, {
type: "strip",
direction: "vertical",
size: 0.2,
elements: [{
type: "stack",
size: 0.6,
activePanelIndex: 0,
panels: [{
contentId: "hierarchy",
text: "Hierarchy"
}]
}, {
type: "stack",
size: 0.4,
activePanelIndex: 0,
panels: [{
contentId: "inspector",
text: "Inspector"
}]
}]
}]
};
}