/** * 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 Node from "@ff/graph/Node"; import Component from "@ff/graph/Component"; import Property from "@ff/graph/Property"; import "@ff/scene/ui/PropertyView"; import { customElement, html, property } from "@ff/ui/CustomElement"; import Tree from "@ff/ui/Tree"; import CVSettingsTask from "../../components/CVSettingsTask"; import { TaskView } from "../../components/CVTask"; import NVNode from "../../nodes/NVNode"; //////////////////////////////////////////////////////////////////////////////// @customElement("sv-settings-task-view") export default class SettingsTaskView extends TaskView { protected render() { if(!this.activeDocument) { return; } const languageManager = this.activeDocument.setup.language; const node = this.activeNode; if (!node) { return html`
${languageManager.getUILocalizedString("Please select a node to display its properties.")}
`; } return html`
`; } protected onActiveNode(previous: NVNode, next: NVNode) { this.requestUpdate(); } } //////////////////////////////////////////////////////////////////////////////// interface ITreeNode { id: string; children: ITreeNode[]; text: string; classes: string; property?: Property; } @customElement("sv-settings-tree") export class SettingsTree extends Tree { @property({ attribute: false }) node: NVNode = null; protected firstConnected() { super.firstConnected(); this.classList.add("ff-property-tree", "sv-settings-tree"); } protected getClasses(treeNode: ITreeNode) { return treeNode.classes; } protected update(changedProperties: Map) { if (changedProperties.has("node")) { this.root = this.createNodeTreeNode(this.node); } super.update(changedProperties); } protected renderNodeHeader(node: ITreeNode) { if (!node.property) { return html`
${node.text}
`; } const component = node.property.group?.linkable; const nonEditableProperties: string[] = component["nonEditableProperties"] || []; const disabled = nonEditableProperties.includes(node.property.path); return html``; } protected createNodeTreeNode(node: Node): ITreeNode { const components = node.components.getArray().filter(component => component["settingProperties"] && !component.tags.has("no_settings")); return { id: node.id, text: node.displayName, classes: "ff-node", children: components.map(component => ({ id: component.id, text: component.displayName, classes: "ff-component", property: null, children: this.createPropertyNodes(component["settingProperties"]), })), }; } protected createPropertyNodes(properties: Property[]): ITreeNode[] { const root: Partial = { children: [] }; properties.forEach(property => { const fragments = property.path.split("."); let node = root; const count = fragments.length; const last = count - 1; for (let i = 0; i < count; ++i) { const fragment = fragments[i]; let child = node.children.find(node => node.text === fragment); if (!child) { const id = i === last ? property.key : fragment; child = { id, text: fragment, classes: "", children: [], property: i === last ? property : null }; node.children.push(child); } node = child; } }); return root.children; } }