Forge Viewer: Autodesk.BoxSelection extension bug - javascript

On the project where I work (React, TS), we use the viewer and added the Box Selection extension for it.
The first time you activate it with a button in the toolbar, the extension works, the elements are highlighted. Then you can switch to another mode, for example, the orbit mode. And after that, when you click on the button that activates the "box Selection extension", the extension no longer works. The orbit mode remains working.
At the same time, the button is clicked (console.log() is fired) and the loadExtension('Autodesk.Box Selection') method works.
What could be the problem?
I will give some code snippets
This is the extension code:
export default function RectangleSelectionExtension(
this,
viewer,
options,
) {
window.Autodesk.Viewing.Extension.call(this, viewer, options);
}
RectangleSelectionExtension.prototype = (
Object.create(window.Autodesk.Viewing.Extension.prototype)
);
RectangleSelectionExtension.prototype.constructor = RectangleSelectionExtension;
RectangleSelectionExtension.prototype.load = () => true;
RectangleSelectionExtension.prototype.unload = () => true;
RectangleSelectionExtension.prototype.onToolbarCreated = function onToolbarCreated() {
this.group = this.viewer.toolbar.getControl('allExtensionsToolbar');
if (!this.group) {
this.group = new window.Autodesk.Viewing.UI.ControlGroup('allExtensionsToolbar');
this.viewer.toolbar.addControl(this.group);
}
// Add a new button to the toolbar group
this.button = new window.Autodesk.Viewing.UI.Button('RectangleSelectionExtension');
this.button.onClick = async () => {
const boxSelectionExtension = await this.viewer.loadExtension('Autodesk.BoxSelection');
this.viewer.toolController.activateTool(boxSelectionExtension.boxSelectionTool.getName());
boxSelectionExtension.addToolbarButton(this.viewer);
};
this.button.setToolTip('Select within a rectangle area');
this.button.addClass('RectangleSelectionExtension');
this.group.addControl(this.button);
};
window.Autodesk.Viewing.theExtensionManager.registerExtension('BoxSelection', RectangleSelectionExtension);
Next, in the Viewer component, we import and register the extension:
window.Autodesk.Viewing.theExtensionManager.registerExtension('RectangleSelectionExtension', RectangleSelectionExtension);
And this is how we initialize the viewer:
window.Autodesk.Viewing.Initializer(options, () => {
const container = document.getElementById('forgeViewer');
if (container) {
viewer = new window.Autodesk.Viewing.GuiViewer3D(
container,
{
token,
extensions: [
/* ...some extensions */
'RectangleSelectionExtension',
],
},
);
const startedCode = viewer.start();
if (startedCode > 0) {
return;
}
/* ...some eventListeners */
}

I'm not sure I understand the purpose of your RectangleSelectionExtension. From the code it looks like it just adds a button in the toolbar, and clicking that button repeatedly loads another extension (Autodesk.BoxSelection), repeatedly activates the box selection tool, and repeatedly adds the box selection button to the toolbar. That doesn't seem right.
If you're simply interested in the box selection, you can load it (and include it in the toolbar) like so:
// ...
viewer = new window.Autodesk.Viewing.GuiViewer3D(
container,
{
token,
extensions: [
/* ...some extensions */
'Autodesk.BoxSelection',
]
}
);
// and later ...
const boxSelectionExt = viewer.getExtension('Autodesk.BoxSelection');
boxSelectionExt.addToolbarButton(true); // Add the button to the toolbar
boxSelectionExt.addToolbarButton(false); // Remove the button from the toolbar
// ...

Related

Material Dialog - update configuration (hasBackdrop) after open

It is possible to update the MatDialog configuration after open? I am trying to set the backdrop to true when the Dialog is opened and to false after some operations.
openDialog() {
const config = new MatDialogConfig();
config.hasBackdrop = true;
config.date = 'Hello';
this.dialog = this.matDialog.open(MaterialDialogComponent, config);
}
updateDialog() {
this.dialog.updateSize('100px', '100px');
this.dialog.updatePosition({right:'10%', bottom:'10%'});
// how to update the hasBackdrop to false?
}
The documentation do not show any method to do this, also I do not see any other way to achieve this result.
You can get reference of backdrop element from private api
const backdropElement = this.dialogRef['_ref'].overlayRef._backdropElement;
and then change the style of this element. or you can use OverlayContainer service and query the backdrop element
const backdropElement = this.overlayContainer.getContainerElement().querySelector('.cdk-overlay-backdrop')

class toggle is adding the same class instead of toggling

I am trying to switch between a leaflet map and a list. It is working fine untill the code is compiled by webpack. After clicking on an item the map shows up as it should. But when I close the map with the close button, I have map.remove() which removes the map but the class toggles do not toggle but instead they add the already existing class.
The function below runs to open the map and toggles correctly. And it runs when closing the map, but then the same toggles do not run correctly and adds the same class, as you can see in the photo below. The menu class is also duplicated which it should not.
Before I compile with webpack it does run correctly. Thanks!
const mapHandler = async id => {
let brewery;
if (id) {
brewery = await fetchBreweries.get('/' + id);
if (!brewery.latitude || !brewery.longitude) {
alert('Sorry, this entry has no coordinates.');
return;
};
};
if (!id) {
//no id means we are clicking the closehandler, so enable buttons in footer and remove the map
footerBtns.map(btn => btn.disabled = false);
//remove it before toggling classes or it will throw the can't find it error
if (map) map.remove();
};
mapEl.classList.toggle('not-visible');
list.classList.toggle('not-visible');
mapEl.classList.toggle('visible');
list.classList.toggle('visible');
if (!id) return;
//there is an id, so we are going to build the map to show it
//disable buttons in the footer so we cannot click on them when the map is shown
footerBtns.map(btn => btn.disabled = true);
if (aroundMyLocation) {
//if above flag is true than we want to build the map, show own location and then fly to brewery location
buildMap(myLocation);
setTimeout(() => {
map.flyTo([brewery.latitude, brewery.longitude], 12, {duration: 3})
}, 800);
setTimeout(() => {
L.marker([brewery.latitude, brewery.longitude])
.addTo(map)
.bindPopup(brewery.name)
.openPopup();
}, 1200);
} else {
//flag is false so we only want to show the brewery location
buildMap({lat: brewery.latitude, lng: brewery.longitude});
L.marker([brewery.latitude, brewery.longitude])
.addTo(map)
.bindPopup(brewery.name)
.openPopup();
};
//show brewery details in bottom right box
details.innerHTML = `
<h3>${brewery.name}</h3>
<p>${brewery.phone || 'number unknown'}</p>
<p>${brewery.street || 'street unknown'}</p>
<p>${brewery.city || 'city unknown'}</p>
`;
};
My script was being added twice with webpack. After adding inject: false to HtmlWebpackPlugin in my config file it was running as it should. I was trying to do a simple project so I could focus on learning webpack, serviceworkers and testing. A bit too much all at once probably...

Link two elements together so changing contents of one automatically changes contents of the other Javascript/Angular

Is it possible to link two elements together so changing contents of one automatically changes contents of the other using jquery or angular?
I have an app where I want the title of an element, which appears a few times throughout the app, to be the same. So for example, when i click on one button called "open window 1", it opens window 1 and when i click on another button also called "open window 1" it also opens window 1.
What im trying to achieve is if i change the title of 1 button, to try to make it change the title of the other button in real time. Remember i have around 20 different buttons on the app, where each has a twin that should be linked together.
In basic terms i just want to link the two buttons together so they are like a mirror image of each other. Is this some feature/function Angular JS has?
The last time I used Angular was a while ago, and it was mostly with Angularjs rather than Angular, so this is just a brief gist to give you an idea of what I mean, which you'd have to tailor to your needs
You could do something like creating a global module somewhere, such as:
buttons.ts:
interface ButtonInfo {
title: string,
onClick: Function
}
const button1: ButtonInfo = {
title: 'Open Window 1',
onClick: openWindow1()
};
const updateButton1 = update => {
if (update.title) button1.title = update.title;
if (update.onClick) button1.onClick = update.onClick;
};
const button2: ButtonInfo = {
title: 'Open Window 2',
onClick: openWindow2()
};
const updateButton2 = update => {
if (update.title) button2.title = update.title;
if (update.onClick) button2.onClick = update.onClick;
};
export {
button1,
updateButton1,
button2,
updateButton2,
ButtonInfo
};
Then you could import this wherever your pairs of buttons are:
button1.component.ts:
import { button1, updateButton1 } from './buttons';
import type { ButtonInfo } from './buttons';
export class Button1Component {
button1: ButtonInfo = button1;
...
updateButton(update) {
updateButton1(update);
}
}
button1.component.html:
<div>
...
<button (click)="button1.onClick()">
{{button1.title}}
</button>
</div>

NgbModal opens very slowly for *some* modal windows

I'm using ng-bootstrap#6.0.0. Some modal dialogs are opening very quickly, while others are opening extremely slowly (sometimes taking 15 seconds to open).
The slow behavior is: The modal window shows with a maximum width regardless of the size selected, but has not modal-body content (yet). Approximately 15 seconds later, the modal window resizes to the requested size, and then populates with the modal-body as expected.
I have compared the modal components that work fine with the modal components that don't, and see very few differences aside from functionality differences like you would expect.
I've tried changing the "backdrop", I've tried setting the "container", I've tried converting the modal between an in-line ng-template and a separate component. Nothing is changing the behavior of the slow modals.
Does anyone have any ideas?
I am using ngx-simplemde, and launching a modal from a custom toolbar item in the markdown editor. For some reason, when launching a modal, popover, or tooltip from within the markdown editor, it behaves very slowly. I had to abstract my logic out of the custom ngx-simplemde toolbar button and trigger the modal being opened from outside the ngx-simplemde custom toolbar item. This was fairly difficult to figure out, as I had to create a hidden button, and then trigger the hidden button using the DOM's .click() method, and the ngClick of the button was setup to initiate the modal popup. Not ideal, fairly hacky, but it works now.
component.html
<simplemde [(ngModel)]="value" [options]="{ toolbar: toolbar }" (ngModelChange)="valueChange.emit($event)" #smde></simplemde>
<button type="button" class="btn btn-primary" (click)="insertImage()" style="display: none" #buttonElement>Test</button>
component.ts
createImageListToolbar() {
return {
name: 'image-list',
className: 'fas fa-images',
title: this.imageListButtonTitle,
// This action is called when the user clicks the button
// It will open the imageListModal that is embedded in the HTML of this component
// When the modal closes, the user will have selected the image they want inserted
action: async () => {
this.buttonEle.nativeElement.click();
}
};
}
async insertImage() {
const image = await this.modalService.getMediaSelection(this.implementationGuideId, this.mediaReferences);
const doc = this.simplemde.Instance.codemirror.getDoc();
const cursor = doc.getCursor();
const altTag = image.title ? `alt="${image.title}" ` : '';
const replaceText = `<table><tr><td><img src="${image.name}" ${altTag}/></td></tr></table>`;
doc.replaceRange(replaceText, cursor);
}
modal.service.ts:
async getMediaSelection(implementationGuideId?: string, mediaReferences?: MediaReference[]): Promise<ImageItem> {
const modalRef = this.modalService.open(MediaSelectionModalComponent, { container: 'body', backdrop: 'static' });
modalRef.componentInstance.implementationGuideId = implementationGuideId;
modalRef.componentInstance.mediaReferences = mediaReferences;
return await modalRef.result;
}
This could be caused by change detection issues.
Try to open modal in change detection zone.
Inject Zone:
constructor(
private modalService: NgbModal,
private zone: NgZone,
) { }
and wrap modalService.open with zone.run:
confirmDelete(user: DSUser): void {
this.zone.run(() => {
const modalRef = this.modalService.open(ConfirmModalComponent, {
backdrop: true,
centered: true,
}).componentInstance;
modalRef.message = `<strong>Are you sure you want to delete <span class="text-primary">"${user.name.firstName} ${user.name.lastName}"</span> profile?</strong>`;
modalRef.confirmCallback = () => this.deleteUser.emit(user);
});
}

ARCGIS Javascript vertex custom right click event possible?

i am using the ARCGIS Javascript API and trying to override the default right click behavior of the vertex points of a shape.
in ESRI's help it does list the onVertexClick event however from here it seems there is no way to determine if this is a right or left click event so i cannot override just the rightclick.
https://developers.arcgis.com/javascript/jsapi/edit.html
I am trying to set the right click behavour to just delete the current node/vertex instead of showing a menu with the option Delete.
EDIT
Here is the current event that exists within the ARCGIS api.
this.eventsList.push(dojo.connect(this._editToolbar, 'onVertexClick', $.proxy(this.addCustomVertexClickEvent, this)));
this event is already in the api however it does not return any way for me to determine left/right click.
your comment "listen for the click event then test the button attribute of the MouseEvent object" would work however i cant actually add a click event to the vertex points directly as these are inside the ARCGIS api code.
For anyone else who is looking for a way to do this without hacking around. You can listen to "contextmenu" (right click) events on the body, set a flag in the "contextmenu" handler to let the application know the current state. Simulate a click event to the "vertex handle" with a "mousedown", "mouseup" combination. In the "vertex-click" handler check for the right click flag set in the "contextmenu" handler
var editToolbar = new Edit(map, options);
var rightClick;
$('body').on('contextmenu', function(e) {
var target = e.target;
if(target.tagName === 'circle') {
// We only care about this event if it targeted a vertex
// which is visualized with an SVG circle element
// Set flag for right click
rightClick = true;
// Simulate click on vertex to allow esri vertex-click
// to fill in the data for us
var mouseDownEvt = new MouseEvent('mousedown', e.originalEvent);
target.dispatchEvent(mouseDownEvt);
var mouseUpEvt = new MouseEvent('mouseup', e.originalEvent);
target.dispatchEvent(mouseUpEvt);
// Since this event will be handled by us lets prevent default
// and stop propagation so the browser context menu doesnt appear
e.preventDefault();
e.stopPropagation();
}
});
editToolbar.on('vertex-click', function(e) {
if(rightClick) {
// Handle the right click on a vertex
rightClick = null;
}
});
after hearing back from ESRI it seems they do not provide this detail in their API so this is not possible yet.
I ended up doing this differently. I wanted to add a UI so the user could enter the XY of the point
// setup to allow editing
this.editToolbar = new EditToolbar(this.map, { allowDeleteVertices: false });
const rcMenuForGraphics = new RightClickVertexContextMenu();
const menu = rcMenuForGraphics.createMenu();
// bind to the map graphics as this is where the vertex editor is
this.map.graphics.on("mouse-over", (evt)=> {
// bind to the graphic underneath the mouse cursor
menu.bindDomNode(evt.graphic.getDojoShape().getNode());
});
this.map.graphics.on("mouse-out", (evt)=> {
menu.unBindDomNode(evt.graphic.getDojoShape().getNode());
});
this.editToolbar.on("vertex-click", (evt2) => {
rcMenuForGraphics.setCurrentTarget(evt2);
// evt2.vertexinfo.graphic.geometry.setX(evt2.vertexinfo.graphic.geometry.x - 1000);
})
// when the graphics layer is clicked start editing
gl.on("click", (evt: any) => {
this.map.setInfoWindowOnClick(false);
// tslint:disable-next-line: no-bitwise
const t: any = EditToolbar.MOVE | EditToolbar.EDIT_VERTICES;
this.editToolbar.deactivate();
this.editToolbar.activate(t, evt.graphic);
})
The code for the menu uses esri's vertex editor to grab the point, change its XY and then manually call the events to refresh the geometry. Only tested with polygon
import Menu = require("dijit/Menu");
import MenuItem = require("dijit/MenuItem");
import Graphic = require("esri/graphic");
import Edit = require("esri/toolbars/edit");
import Point = require("esri/geometry/Point");
class RightClickVertexContextMenu {
private curentTarget: { graphic: Graphic; vertexinfo: any; target: Edit; };
public createMenu() {
const menuForGraphics = new Menu({});
menuForGraphics.addChild(new MenuItem({
label: "Edit",
onClick: () => {
// this is a bit hooky. We grab the verx mover, change the x/y and then call the _moveStopHandler
console.log(this.curentTarget.vertexinfo);
const e: any = this.curentTarget.target;
const mover = e._vertexEditor._findMover(this.curentTarget.vertexinfo.graphic);
const g: Graphic = mover.graphic;
// add in a UI here to allow the user to set the new value. This just shifts the point to the left
g.setGeometry(new Point(mover.point.x - 1000, mover.point.y ))
e._vertexEditor._moveStopHandler(mover, {dx: 15});
this.curentTarget.target.refresh();
}
}));
menuForGraphics.addChild(new MenuItem({
label: "Delete",
onClick: () => {
// call the vertex delete handler
const ct: any = this.curentTarget.target;
ct._vertexEditor._deleteHandler(this.curentTarget.graphic)
}
}));
return menuForGraphics;
}
public setCurrentTarget(evt: { graphic: Graphic; vertexinfo: any; target: Edit; }) {
this.curentTarget = evt;
}
}
export = RightClickVertexContextMenu;

Categories

Resources