From ff60aafb4249fb497dedb11cf6707cb3df7a3d76 Mon Sep 17 00:00:00 2001 From: Sebastien DUMETZ Date: Fri, 26 Jul 2024 16:34:23 +0200 Subject: [PATCH 1/9] add back ff-color-edit popup. Fix #257 --- source/client/ui/PropertyColor.ts | 68 ++++++++++++++++++++++++--- source/client/ui/explorer/styles.scss | 31 +++++++----- 2 files changed, 81 insertions(+), 18 deletions(-) diff --git a/source/client/ui/PropertyColor.ts b/source/client/ui/PropertyColor.ts index 3891bee8..b9b6f712 100644 --- a/source/client/ui/PropertyColor.ts +++ b/source/client/ui/PropertyColor.ts @@ -21,12 +21,10 @@ import Property from "@ff/graph/Property"; import CustomElement, { customElement, property, PropertyValues, html } from "@ff/ui/CustomElement"; import "@ff/ui/Button"; -import { IButtonClickEvent } from "@ff/ui/Button"; - import "@ff/ui/ColorEdit"; -import { IColorEditChangeEvent } from "@ff/ui/ColorEdit"; -import {getFocusableElements, focusTrap} from "../utils/focusHelpers"; +import type { IColorEditChangeEvent } from "@ff/ui/ColorEdit"; +import { focusTrap, getFocusableElements } from "client/utils/focusHelpers"; //////////////////////////////////////////////////////////////////////////////// @@ -39,8 +37,12 @@ export default class PropertyColor extends CustomElement @property({ type: String }) name = ""; + @property({attribute: false, type: Boolean}) + pickerActive :boolean = false; + protected color: Color = new Color(); + constructor() { super(); @@ -52,6 +54,11 @@ export default class PropertyColor extends CustomElement this.classList.add("sv-property", "sv-property-color"); } + protected disconnected() + { + this.pickerActive = false; + } + protected update(changedProperties: PropertyValues): void { if (!this.property) { @@ -73,6 +80,15 @@ export default class PropertyColor extends CustomElement } } + if(changedProperties.has("pickerActive")){ + if(this.pickerActive){ + this.setPickerFocus(); + document.addEventListener("pointerdown", this.onPointerDown, { capture: true, passive: true }); + }else{ + document.removeEventListener("pointerdown", this.onPointerDown, {capture: true}); + } + } + super.update(changedProperties); } @@ -83,13 +99,25 @@ export default class PropertyColor extends CustomElement const color = this.color.toString(); return html` - + + ${this.pickerActive ? html`this.onKeyDown(e)} @change=${this.onColorChange}>` : null} `; } - protected onColorChange(event: Event) + protected async setPickerFocus() + { + await this.updateComplete; + const container = this.getElementsByTagName("ff-color-edit").item(0) as HTMLElement; + (getFocusableElements(container)[0] as HTMLElement).focus(); + } + + protected onButtonClick(event: Event) + { + this.pickerActive = !this.pickerActive; + } + + protected onColorChange(event: IColorEditChangeEvent) { - this.color = new Color((event.target as HTMLInputElement).value); this.property.setValue(this.color.toRGBArray()); } @@ -98,4 +126,30 @@ export default class PropertyColor extends CustomElement this.color.fromArray(value); this.requestUpdate(); } + // if color picker is active and user clicks outside, close picker + protected onPointerDown = (event: PointerEvent) => { + if (!this.pickerActive) { + return; + } + + if (event.composedPath()[0] instanceof Node && this.contains(event.composedPath()[0] as Node)) { + return; + } + this.pickerActive = false; + } + + protected onKeyDown(e: KeyboardEvent) + { + if (e.code === "Escape" || e.code === "Enter") { + e.preventDefault(); + e.stopPropagation(); + this.pickerActive = false; + + (this.getElementsByTagName("ff-button")[0] as HTMLElement).focus(); + } + else if(e.code === "Tab") { + const element = this.getElementsByTagName("ff-color-edit")[0] as HTMLElement; + focusTrap(getFocusableElements(element) as HTMLElement[], e); + } + } } \ No newline at end of file diff --git a/source/client/ui/explorer/styles.scss b/source/client/ui/explorer/styles.scss index 52cb1b6e..489cde4f 100644 --- a/source/client/ui/explorer/styles.scss +++ b/source/client/ui/explorer/styles.scss @@ -1248,22 +1248,31 @@ $tour-entry-indent: 12px; } &.sv-property-color { - input[type="color"] { - cursor: pointer; - border: none; - margin: 2px; + position: relative; + display: block; + + & > .ff-button { + width: 26px; + max-width: 20ch; height: 26px; inline-size: 26px; + box-sizing: border-box; padding: 1px; background: $color-background; border-radius: 2px; - &::-webkit-color-swatch-wrapper { - padding: 0; - } - &::-webkit-color-swatch, &::-moz-color-swatch { - border: none; - border-radius: 2px; - } + border: none; + margin: 2px; + } + + .ff-color-edit { + position: absolute; + width: 200px; + height: 180px; + right: 0px; + top: -188px; + background: $color-background-dark; + padding: 8px; + border-radius: 2px; } } From ca62abf48e1ae890ee3b7866fdaf4c406a4b1f2f Mon Sep 17 00:00:00 2001 From: Jamie Cope Date: Mon, 29 Jul 2024 10:06:06 -0400 Subject: [PATCH 2/9] Fix include order regression for Packrat use case --- source/client/ui/story/MainView.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/client/ui/story/MainView.ts b/source/client/ui/story/MainView.ts index 547a6be5..e5cd95de 100644 --- a/source/client/ui/story/MainView.ts +++ b/source/client/ui/story/MainView.ts @@ -15,6 +15,8 @@ * limitations under the License. */ +import ExplorerPanel from "./ExplorerPanel"; + import localStorage from "@ff/browser/localStorage"; import StoryApplication, { IStoryApplicationProps } from "../../applications/StoryApplication"; @@ -38,7 +40,6 @@ import ConsolePanel from "./ConsolePanel"; import InspectorPanel from "./InspectorPanel"; import AssetPanel from "./AssetPanel"; import CollectionPanel from "./CollectionPanel"; -import ExplorerPanel from "./ExplorerPanel"; import "./styles.scss"; From 857281db6c1266ad0eaf9bfc60e311f45c3b6d1e Mon Sep 17 00:00:00 2001 From: Jamie Cope Date: Tue, 30 Jul 2024 11:15:01 -0400 Subject: [PATCH 3/9] Bug fixes for focus-visible and anno overlay transform issues --- source/client/annotations/CircleSprite.ts | 4 ++-- source/client/ui/explorer/styles.scss | 21 ++++++++++++++++++--- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/source/client/annotations/CircleSprite.ts b/source/client/annotations/CircleSprite.ts index 0cede5cc..155f6e2a 100755 --- a/source/client/annotations/CircleSprite.ts +++ b/source/client/annotations/CircleSprite.ts @@ -303,10 +303,10 @@ class CircleAnnotation extends AnnotationElement // Handle shifting annotation body when out-of-bounds if (this.isExpanded) { this.contentElement.style.removeProperty("transform"); - if (this.classList.contains("sv-align-right")) { + if (this.classList.contains("sv-align-right") && !this.overlayed) { this.contentElement.style.transform = `translateX(-${this.offsetWidth}px)`; } - if (this.classList.contains("sv-align-bottom")) { + if (this.classList.contains("sv-align-bottom") && !this.overlayed) { this.contentElement.style.transform = `translateY(-${this.offsetHeight-this.markerElement.offsetHeight}px)`; } } diff --git a/source/client/ui/explorer/styles.scss b/source/client/ui/explorer/styles.scss index aaf8795d..a8b26ba8 100644 --- a/source/client/ui/explorer/styles.scss +++ b/source/client/ui/explorer/styles.scss @@ -258,9 +258,6 @@ $pad: $canvas-border-width + $main-menu-button-size + 8px; } &:has(:focus-visible) { - outline: none; - background-color: $menu-color-background-dark; - box-shadow: 0 0 3pt 2pt lighten($color-primary, 50%); opacity: 1 !important; } @@ -293,6 +290,12 @@ $pad: $canvas-border-width + $main-menu-button-size + 8px; font-size: 0.9em; color: #fff; -webkit-tap-highlight-color: transparent; + + &:focus-visible { + outline: none; + background-color: $menu-color-background-dark; + box-shadow: 0 0 3pt 2pt lighten($color-primary, 50%); + } } .sv-annotation-body { @@ -302,6 +305,12 @@ $pad: $canvas-border-width + $main-menu-button-size + 8px; margin: 0px 1px; width: 100%; + &:has(:focus-visible) { + outline: none; + background-color: $menu-color-background-dark; + box-shadow: 0 0 3pt 2pt lighten($color-primary, 50%); + } + .sv-title { padding: 0 0 2px 0; font-weight: bold; @@ -360,6 +369,12 @@ $pad: $canvas-border-width + $main-menu-button-size + 8px; pointer-events: auto; overflow-wrap: normal; + &:has(:focus-visible) { + outline: none; + background-color: $menu-color-background-dark; + box-shadow: 0 0 3pt 2pt lighten($color-primary, 50%); + } + &.sv-expanded { width: 20%; min-width: 180px; From 160a19f2d2ede3ee606a388528123e521d8e0069 Mon Sep 17 00:00:00 2001 From: Jamie Cope Date: Mon, 12 Aug 2024 11:41:51 -0400 Subject: [PATCH 4/9] Fix for #297 due to type change of query param parsing return --- source/client/models/DerivativeList.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/client/models/DerivativeList.ts b/source/client/models/DerivativeList.ts index 92024ea3..cf6ab32e 100644 --- a/source/client/models/DerivativeList.ts +++ b/source/client/models/DerivativeList.ts @@ -159,7 +159,7 @@ export default class DerivativeList createModelAsset(assetPath: string, quality: EDerivativeQuality | string): Derivative { quality = (typeof quality === "string" ? EDerivativeQuality[quality] : quality) as EDerivativeQuality; - quality = isFinite(quality) ? quality : EDerivativeQuality.Medium; + quality = quality != null ? quality : EDerivativeQuality.Medium; const derivative = this.getOrCreate(EDerivativeUsage.Web3D, quality); @@ -173,7 +173,7 @@ export default class DerivativeList createMeshAsset(geoPath: string, colorMapPath?: string, occlusionMapPath?: string, normalMapPath?: string, quality?: EDerivativeQuality | string): Derivative { quality = (typeof quality === "string" ? EDerivativeQuality[quality] : quality) as EDerivativeQuality; - quality = isFinite(quality) ? quality : EDerivativeQuality.Medium; + quality = quality != null ? quality : EDerivativeQuality.Medium; const derivative = this.getOrCreate(EDerivativeUsage.Web3D, quality); From a9fdddfd3ccc0ff03bfb849d89928e088d1b670c Mon Sep 17 00:00:00 2001 From: Jamie Cope Date: Wed, 14 Aug 2024 14:54:28 -0400 Subject: [PATCH 5/9] Bug fix PropertyOptions value being set as string instead of number --- source/client/ui/PropertyOptions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/client/ui/PropertyOptions.ts b/source/client/ui/PropertyOptions.ts index eeba45f3..f345800f 100644 --- a/source/client/ui/PropertyOptions.ts +++ b/source/client/ui/PropertyOptions.ts @@ -106,7 +106,7 @@ export default class PropertyOptions extends CustomElement return html` From 3b4f64ec9d7ab8fc058957115384446455d2c4a1 Mon Sep 17 00:00:00 2001 From: Jamie Cope Date: Wed, 14 Aug 2024 15:24:24 -0400 Subject: [PATCH 6/9] Fix for regression from 69b59c8 that throws an error when leaving tasks using LanguagManager --- source/client/ui/story/AnnotationsTaskView.ts | 4 ++++ source/client/ui/story/ArticlesTaskView.ts | 4 ++++ source/client/ui/story/AudioTaskView.ts | 2 +- source/client/ui/story/ToursTaskView.ts | 4 ++++ 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/source/client/ui/story/AnnotationsTaskView.ts b/source/client/ui/story/AnnotationsTaskView.ts index 044c67e0..3141ec06 100644 --- a/source/client/ui/story/AnnotationsTaskView.ts +++ b/source/client/ui/story/AnnotationsTaskView.ts @@ -74,6 +74,10 @@ export default class AnnotationsTaskView extends TaskView protected render() { + if(!this.activeDocument) { + return; + } + const node = this.activeNode; const annotations = node && node.getComponent(CVAnnotationView, true); const languageManager = this.activeDocument.setup.language; diff --git a/source/client/ui/story/ArticlesTaskView.ts b/source/client/ui/story/ArticlesTaskView.ts index 4de3887b..33ce55e4 100644 --- a/source/client/ui/story/ArticlesTaskView.ts +++ b/source/client/ui/story/ArticlesTaskView.ts @@ -46,6 +46,10 @@ export default class ArticlesTaskView extends TaskView protected render() { + if(!this.activeDocument) { + return; + } + const task = this.task; const articles = task.articles; const activeArticle = task.activeArticle; diff --git a/source/client/ui/story/AudioTaskView.ts b/source/client/ui/story/AudioTaskView.ts index 450d7984..78389255 100644 --- a/source/client/ui/story/AudioTaskView.ts +++ b/source/client/ui/story/AudioTaskView.ts @@ -57,7 +57,7 @@ export default class AudioTaskView extends TaskView protected render() { - if(!this.task.audioManager) { + if(!this.task.audioManager || !this.activeDocument) { return; } diff --git a/source/client/ui/story/ToursTaskView.ts b/source/client/ui/story/ToursTaskView.ts index 34890ddd..e7f17f99 100644 --- a/source/client/ui/story/ToursTaskView.ts +++ b/source/client/ui/story/ToursTaskView.ts @@ -61,6 +61,10 @@ export default class ToursTaskView extends TaskView { //console.log("TourTaskView.render"); + if(!this.activeDocument) { + return; + } + const task = this.task; const tours = task.tours; From d412af41cb49930a9300d61ae6abee9c56ec11c2 Mon Sep 17 00:00:00 2001 From: Jamie Cope Date: Fri, 23 Aug 2024 11:17:56 -0400 Subject: [PATCH 7/9] Improved audio error checking --- source/client/components/CVAudioManager.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/source/client/components/CVAudioManager.ts b/source/client/components/CVAudioManager.ts index 3393bc2e..32df2909 100644 --- a/source/client/components/CVAudioManager.ts +++ b/source/client/components/CVAudioManager.ts @@ -289,6 +289,12 @@ export default class CVAudioManager extends Component play(id: string) { const { outs } = this; + const uri = this.getAudioClipUri(id); + + if(!uri) { + Notification.show("Failed to play audio clip - no uri", "warning"); + return; + } // handle currently playing track if(outs.isPlaying.value) { @@ -309,7 +315,7 @@ export default class CVAudioManager extends Component this.activeId = id; this.isPlaying = true; Object.keys(this.audioViews).forEach((key) => this.audioViews[key].requestUpdate()); - this.analytics.sendProperty("Audio_Play", this.getAudioClipUri(id)); + this.analytics.sendProperty("Audio_Play", uri); }) .catch(error => Notification.show(`Failed to play audio at '${this.audioPlayer.getAttribute("src")}':${error}`, "warning")); } From e19f849394daa22786bd916f095e8ecc42875b51 Mon Sep 17 00:00:00 2001 From: Jamie Cope Date: Fri, 23 Aug 2024 14:59:16 -0400 Subject: [PATCH 8/9] Fix for measure/grid label not scaling correctly with unit change --- .../components/CVStaticAnnotationView.ts | 32 +++++++------------ source/client/components/CVTape.ts | 8 ++++- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/source/client/components/CVStaticAnnotationView.ts b/source/client/components/CVStaticAnnotationView.ts index 82aeff90..9a679546 100644 --- a/source/client/components/CVStaticAnnotationView.ts +++ b/source/client/components/CVStaticAnnotationView.ts @@ -22,29 +22,16 @@ import { ITypedEvent, Node, types } from "@ff/graph/Component"; import Viewport, { IViewportDisposeEvent } from "@ff/three/Viewport"; import HTMLSpriteGroup, { HTMLSprite } from "@ff/three/HTMLSpriteGroup"; -import CObject3D, { IPointerEvent, IRenderContext } from "@ff/scene/components/CObject3D"; -import CRenderer from "@ff/scene/components/CRenderer"; +import CObject3D, { IRenderContext } from "@ff/scene/components/CObject3D"; -import CVModel2 from "./CVModel2"; -import CVMeta from "./CVMeta"; -import CVReader from "./CVReader"; -import unitScaleFactor from "../utils/unitScaleFactor"; - -import { IAnnotation } from "client/schema/model"; import Annotation from "../models/Annotation"; -import AnnotationSprite, { IAnnotationClickEvent, IAnnotationLinkEvent } from "../annotations/AnnotationSprite"; +import { IAnnotationClickEvent } from "../annotations/AnnotationSprite"; import AnnotationFactory from "../annotations/AnnotationFactory"; import "../annotations/StandardSprite"; import "../annotations/ExtendedSprite"; import "../annotations/CircleSprite"; -import CVARManager from "./CVARManager"; -import CVLanguageManager from "./CVLanguageManager"; -import { ELanguageType, EUnitType } from "client/schema/common"; -import CVAssetReader from "./CVAssetReader"; -import CVAudioManager from "./CVAudioManager"; -import CVAssetManager from "./CVAssetManager"; //////////////////////////////////////////////////////////////////////////////// @@ -63,9 +50,9 @@ export default class CVStaticAnnotationView extends CObject3D { static readonly typeName: string = "CVStaticAnnotationView"; - /*static readonly ins = { - unitScale: types.Number("Transform.UnitScale", { preset: 1, precision: 5 }), - activeTags: types.String("Tags.Active"), + static readonly ins = { + unitScale: types.Number("Transform.UnitScale", { preset: 1, precision: 5 }) + /*activeTags: types.String("Tags.Active"), title: types.String("Annotation.Title"), lead: types.String("Annotation.Lead"), marker: types.String("Annotation.Marker"), @@ -80,10 +67,10 @@ export default class CVStaticAnnotationView extends CObject3D audioId: types.String("Annotation.AudioID"), tilt: types.Number("Annotation.Tilt"), azimuth: types.Number("Annotation.Azimuth"), - color: types.ColorRGB("Annotation.Color"), + color: types.ColorRGB("Annotation.Color"),*/ }; - ins = this.addInputs(CVStaticAnnotationView.ins);*/ + ins = this.addInputs(CVStaticAnnotationView.ins); //private _activeAnnotation: Annotation = null; private _annotations: Dictionary = {}; @@ -106,6 +93,11 @@ export default class CVStaticAnnotationView extends CObject3D const ins = this.ins; const object3D = this.object3D; + + if (ins.unitScale.changed) { + object3D.scale.setScalar(ins.unitScale.value); + object3D.updateMatrix(); + } if (ins.visible.changed) { (object3D as HTMLSpriteGroup).setVisible(ins.visible.value); diff --git a/source/client/components/CVTape.ts b/source/client/components/CVTape.ts index 1b5a949c..cec83a51 100644 --- a/source/client/components/CVTape.ts +++ b/source/client/components/CVTape.ts @@ -112,6 +112,7 @@ export default class CVTape extends CObject3D lineMaterial.transparent = true; this.line = new Line(lineGeometry, lineMaterial); this.line.visible = false; + this.line.frustumCulled = false; // add distance label this.annotationView = this.node.createComponent(CVStaticAnnotationView); @@ -164,6 +165,10 @@ export default class CVTape extends CObject3D endPin.scale.setScalar(radius * 0.003); endPin.updateMatrix(); + + const defaultScale = radius * 0.05; + this.annotationView.ins.unitScale.setValue(defaultScale); + ins.endPosition.set(); // always trigger recalculation } // if tape is enabled, listen for pointer events to set tape start/end @@ -234,7 +239,8 @@ export default class CVTape extends CObject3D // update distance label const data = this.label.data; - data.position = [(positions[0]+positions[3])/2.0,(positions[1]+positions[4])/2.0,(positions[2]+positions[5])/2.0]; + const scaleFactor = 1/this.annotationView.ins.unitScale.value; + data.position = [scaleFactor*(positions[0]+positions[3])/2.0,scaleFactor*(positions[1]+positions[4])/2.0,scaleFactor*(positions[2]+positions[5])/2.0]; const units = this.ins.globalUnits.getOptionText(); this.label.title = tapeLength.toFixed(2) + " " + units; this.annotationView.updateAnnotation(this.label, true); From 338d2a98844eb073b9417e8675aaafa63486c649 Mon Sep 17 00:00:00 2001 From: Jamie Cope Date: Wed, 28 Aug 2024 12:01:38 -0400 Subject: [PATCH 9/9] Update version number --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a7879ca1..ebf74d03 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "voyager", - "version": "0.42.0", + "version": "0.44.0", "description": "Smithsonian DPO Voyager - 3D Explorer and Tool Suite", "scripts": { "start": "npm run server",