I have a console (div) absolutely positioned a top a leafletJS map. I have made it draggable using jquery-ui draggable plugin.
The problem is, when I drag the console the underlying map is also dragging. I am unsure as to which event on which element to stopImmediatePropagation on and when. Any ideas? I tried cancelling the click event on both start and stop handlers, but to no avail. (its wrapped as an Angular2+ Directive).
import {Directive, ElementRef, EventEmitter, Input, OnInit, Output} from '#angular/core';
declare var $: any;
#Directive({
selector: '[skinDraggable]'
})
export class DraggableDirective implements OnInit {
#Input('containmentSelector') containmentSelector: string;
constructor(private el: ElementRef) {}
ngOnInit() {
var me = this;
/** legacy since its available and ng has no decent equivalent */
$( this.el.nativeElement )
.draggable({
containment: this.containmentSelector,
scroll: false,
start: (event, ui) => {
// event.toElement is the element that was responsible
// for triggering this event. The handle, in case of a draggable.
$( event.originalEvent.target ).one('click', function(e){ e.stopImmediatePropagation(); } );
}
});
this.el.nativeElement.style.cursor = "move";
}
}
Made it, by disabling leaflet dragging on mousedown and then reenabling it on mouseup. The code shows some ngrx/store, but effectively leafletMap.dragging.enable() and disable() are being called, but only when the target is the overlay (not the map).
import {Directive, ElementRef, OnInit} from '#angular/core';
import {DraggableDirective} from "../../../widgets/draggable.directive";
import {Store} from "#ngrx/store";
import * as fromRoot from '../../../app.reducer';
import * as toolbarActions from '../../map-toolbar/map-toolbar.actions';
#Directive({
selector: '[skinPrintOverlayDraggable]'
})
export class PrintOverlayDraggableDirective extends DraggableDirective implements OnInit {
constructor(
el: ElementRef,
private store: Store<fromRoot.State>
) {
super(el);
}
ngOnInit()
{
super.ngOnInit();
//* preventing the map panning when the user drags the print overlay */
const mapContainer = document.querySelector('#map-container');
const map = document.querySelector('.mapboxgl-map');
mapContainer.addEventListener('mousedown', e => {
if (e.target === map) return true;
this.store.dispatch(new toolbarActions.SwitchDragPanActive(false));
});
mapContainer.addEventListener('mouseup', e => {
if (e.target === map) return true;
this.store.dispatch(new toolbarActions.SwitchDragPanActive(true));
});
}
}
Related
I want to react on events started by elements placed in the data-grid rows.
Vaading data-grid prevents events from bubbling up to the parent component containing the grid. Having buttons placed in the grid column rendered for each row I cannot catch the click or any other event from the component that hosts the grid.
The examples from https://vaadin.com/components/vaadin-grid/html-examples are relying on js hooks being attached in the html file. I am working with Lit-element and trying to do the same at firstUpdated() callback. The problem is that apparently at this point the table is not available.
<vaadin-grid id="test" .items=${this.data} theme="compact">
<vaadin-grid-column width="40px" flex-grow="0">
<template class="header">#</template>
<template>
<vaadin-button style="font-size:10px;" theme="icon" focus-target #click="${(e) => { console.log(e) }}">
<iron-icon icon="icons:create"></iron-icon>
</vaadin-button>
</template>
</vaadin-grid-column>
</vaadin-grid>
I expected to have the log and nothing happens as the grid component prevents event from bubbling up to my component.
The code that tries to implement renderer property for vaadin-grid-column:
import { LitElement, html, css } from 'lit-element'
import {render} from 'lit-html';
import '#vaadin/vaadin-grid/vaadin-grid.js'
import '#vaadin/vaadin-grid/vaadin-grid-filter-column.js'
import '#vaadin/vaadin-grid/vaadin-grid-sort-column.js';
import '#vaadin/vaadin-grid/vaadin-grid-filter.js';
import '#vaadin/vaadin-grid/vaadin-grid-sorter.js'
export default class MyClass extends LitElement {
static get properties () {
return {
data: {
type: Array,
hasChanged: () => true
},
}
}
get grid() {
return this.shadowRoot.querySelector('vaadin-grid');
}
constructor () {
super()
this.data = []//is being assigned from super as a property to a custom element
}
render () {
return html`
<vaadin-grid id="test" .items=${this.data}>
<vaadin-grid-column .renderer=${this.columnRenderer} header="some header text"></vaadin-grid-column>
</vaadin-grid>
`
}
columnRenderer(root, column, rowData) {
render(html`test string`, root);
}
}
window.customElements.define('my-elem', MyClass)
When using vaadin-grid inside LitElement-based components you should use renderers
Here's how your code would look using renderers
import {LitElement, html} from 'lit-element';
// you need lit-html's render function
import {render} from 'lit-html';
class MyElement extends LitElement {
// we'll just assume the data array is defined to keep the sample short
render() {
return html`
<vaadin-grid id="test" .items=${this.data} theme="compact">
<vaadin-grid-column width="40px" flex-grow="0" .renderer=${this.columnRenderer} .headerRenderer=${this.columnHeaderRenderer}></vaadin-grid-column>
<vaadin-grid>
`;
}
columnHeaderRenderer(root) {
render(html`#`, root);
// you could also just do this
// root.textContent = '#'
// or actually just use the column's header property would be easier tbh
}
columnRenderer(root, column, rowData) {
render(
html`
<vaadin-button style="font-size: 10px;" theme="icon" focus-target #click="${(e) => { console.log(e) }}">
<iron-icon icon="icons:create"></iron-icon>
</vaadin-button>
`, root);
}
}
You can see this and more of vaadin-grid's features in action in LitElement in this Glitch created by one of the vaadin team members
So i am new to react native and JS in general, i have a huge background in native mobile development and i want to know more about Web Apps. So i try to begin with react native to experience that.
I wanted to do a side menu using createDrawerNavigator, so i created the following component :
// code for the menu
import React from 'react';
import { Platform } from 'react-native';
import { createStackNavigator, createDrawerNavigator, createAppContainer, DrawerActions } from "react-navigation";
import HomeScreen from '../screens/HomeScreen';
import LinksScreen from '../screens/LinksScreen';
import SettingsScreen from '../screens/SettingsScreen';
const App = createDrawerNavigator({
HomeScreen: {
screen: HomeScreen
},
LinksScreen: {
screen: LinksScreen
},
SettingsScreen: {
screen: SettingsScreen
}
},
{
drawerWidth: 300
});
export default createAppContainer(App);
// HomeScreen.js
import React from 'react';
import {
Image,
Platform,
ScrollView,
StyleSheet,
Text,
TouchableOpacity,
View,
Dimensions,
} from 'react-native';
import { WebBrowser } from 'expo';
import { DrawerNavigator } from 'react-native-navigation';
import { MonoText } from '../components/StyledText';
import { createStackNavigator, createDrawerNavigator, createAppContainer, DrawerActions } from "react-navigation";
import Router from './MenuNavigator';
export default class HomeScreen extends React.Component {
static navigationOptions = {
header: 'Vapeself 2',
};
render() {
const App = createDrawerNavigator({
Home: {
screen: MyHomeScreen,
},
Notifications: {
screen: MyNotificationsScreen,
},
});
const MyApp = createAppContainer(App);
this.props.navigation.dispatch(DrawerActions.openDrawer());
return (
<Router/>
);
}
// LinkScreen.js
import React from 'react';
import { ScrollView, StyleSheet } from 'react-native';
import { ExpoLinksView } from '#expo/samples';
export default class LinksScreen extends React.Component {
static navigationOptions = {
title: 'Links',
};
render() {
return (
<ScrollView style={styles.container}>
{/* Go ahead and delete ExpoLinksView and replace it with your
* content, we just wanted to provide you with some helpful links */}
<ExpoLinksView />
</ScrollView>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 15,
backgroundColor: '#fff',
},
});
// SettingsScreen.js
import React from 'react';
import { ExpoConfigView } from '#expo/samples';
export default class SettingsScreen extends React.Component {
static navigationOptions = {
title: 'app.json',
};
render() {
/* Go ahead and delete ExpoConfigView and replace it with your
* content, we just wanted to give you a quick view of your config */
return <ExpoConfigView />;
}
}
I don't know what happened because the code doesn't work, and i have this kind of error The component for route 'HomeScreen' must be a React component . But the thing is that its actually work with a TabNavigator. So i really don't understand.
Thank you in advance for the help.
I tried to set following
Highcharts.setOptions({ lang: { thousandsSep: ',' } });
Trying to set thousand separator as default is space.
Error TS2686: 'Highcharts' refers to a UMD global, but the current
file is a module. Consider adding an import instead.
I am using "highcharts": "^6.1.0".
import {Component, OnInit, ViewEncapsulation,Inject, ViewChild,ElementRef,AfterContentInit, OnDestroy, Input} from '#angular/core';
import { chart } from 'highcharts';
import { Race } from '../../../race';
import { BaseComponent } from '../../../base/base.component';
#Component({
selector: 'nc-mobility',
templateUrl: './mobility.component.html',
styleUrls: ['./mobility.component.css']
})
export class MobilityComponent extends BaseComponent implements OnInit, AfterContentInit, OnDestroy {
#Input() mobility: Array<any>;
// highchart declarations
#ViewChild('mobilityDist') chartTarget: ElementRef;
chart: Highcharts.ChartObject;
constructor() { super(); }
ngOnInit() {
}
ngAfterContentInit() {
const options: Highcharts.Options = {
chart: {
type: 'column'
},
series: this.mobility.map(x => {
return { name: x.name, data: [x.value] };
}),
credits: {
enabled: false
}
};
this.chart = chart(this.chartTarget.nativeElement, options);
}
ngOnDestroy() {
this.chart = null;
}
}
Error occurs because you import only one function from Highcharts, by this:
import { chart } from 'highcharts';
In order to use setOptions, you need to also import this function or import whole Highcharts module, just like that:
import * as Highcharts from 'highcharts';
// import Highcharts
import Highcharts from "highcharts";
//set the options in your constructor
Highcharts.setOptions({
lang: {
thousandsSep: ","
}
});
I am trying to make a self nested component that uses Angular Material mat-menu. I have a flyoutcomponent that is a wrapper for flyout-menu-item component, that will have a button as a matMenuTrigger for the nested component that will appear as many levels as the FeatureInput.FeatureChoices dictates. FeatureInput is an object that has FeatureChoices that may or may not contain other featurechoices etc N levels deep. Below code does not compile but it should demonstrate what I am doing. Basically I have flyout menu component as a input to a form and I am trying to load a stored answer on a form rather than select new, which I can do easily using the nested component. The desired behavior is that if the user clicks top matMenuTrigger button to open the top menu that it would expand all child menus to the menu item that matches with the FeatureInput.FeatureValue and sets the menu item _highlighted to true. I am using the menuOpen input parameter and ngChanges successfully to find the match(with I a setTimeout which cannot be right). Basically when I console.log this.trigger it is undefined. Ideally in the ngOnChange to the openMenu I would go through all menus and call openMenu on all the triggers but I cannot get access to the matMenuTrigger with ViewChild as the docs say. I get undefined. *-( All help welcome please and thanks.
Here is flyout template component.
<div>
<buttonmat-button [matMenuTriggerFor]="menu.childMenu"
(onMenuOpen)="onMenuOpen()"
(onMenuClose)="onMenuClose()">
<span [innerHTML]="featureInput.Text"></span>
</button>
<app-flyout-menu-item #menu
[featureChoicesObject]="featureInput.FeatureChoices"></app-flyout-menu-item>
</div>
And here is its .ts
import { Component, OnInit, Input, ViewChild } from '#angular/core';
import { MatMenuTrigger } from '#angular/material';
#Component({
selector: 'app-flyout',
templateUrl: './flyout.component.html',
styleUrls: ['./flyout.component.scss']
})
export class FlyoutComponent implements OnInit {
#Input() featureInput: FeatureInput
constructor() { }
ngOnInit() {
}
onMenuOpen() {
this.menuOpen = true;
}
onMenuClose() {
this.menuOpen = false;
}
}
And here is flyout-menu-item template
<mat-menu #childMenu="matMenu" [overlapTrigger]="false">
<span *ngFor="let featureChoice of featureChoices">
<span>
<button mat-menu-item [matMenuTriggerFor]="menu.childMenu">
<span [innerHTML]="featureChoice.Text"></span>
</button>
<app-flyout-menu-item #menu
[menuOpen]="menuOpen"
[featureInput]="featureInput"
[featureChoicesObject]="featureChoice.FeatureChoices"
(onOptionSelected)="someService.SomeMethod($event)"></app-flyout-menu-item>
</span>
<span *ngIf="!featureChoice.FeatureChoices">
<button mat-menu-item (click)="selectOption(featureChoice.ID)" [innerHTML]="featureChoice.Text" value="{{featureChoice.ID}}"></button>
</span>
</span>
</mat-menu>
And here is its .ts
import { Component, OnInit, Input, Output, ViewChild, EventEmitter, OnChanges, SimpleChanges } from '#angular/core';
import { MatMenuTrigger } from '#angular/material';
import { FeatureChoice } from 'app/model/feature-choice';
import { FeatureInput } from 'app/model/feature-input';
#Component({
selector: 'app-flyout-menu-item',
templateUrl: './flyout-menu-item.component.html',
styleUrls: ['./flyout-menu-item.component.scss']
})
export class FlyoutMenuItemComponent implements OnInit{
#ViewChild('menu') public menu;
#ViewChild('childMenu') public childMenu;
#ViewChild(MatMenuTrigger) public trigger: MatMenuTrigger;
#Input() featureInput: FeatureInput;
#Input() featureChoicesObject: FeatureChoice;
#Output() onOptionSelected: EventEmitter<FeatureInput> = new EventEmitter<FeatureInput>();
constructor(public solutionDataService: SolutionDataService) { }
ngOnInit() {
console.log(this.trigger);
}
ngOnChanges(simpleChanges: SimpleChanges) {
if (simpleChanges.menuOpen && simpleChanges.menuOpen.currentValue) {
setTimeout(() => {
// console.log(this.menu);
const itemsArray = this.childMenu.items.toArray();
for (let x = 0; x < itemsArray.length; x++) {
const menuItem = itemsArray[x];
if (this.featureInput.FeatureValue !== '' && menuItem._elementRef.nativeElement.value === this.featureInput.FeatureValue) {
menuItem._highlighted = true;
}
}
}, 1);
}
}
}
this.menuOpen = true;
Perhaps add menuOpen: boolean = false as an attribute at the top of your FlyoutComponent. I don't know where the value of menuOpen is saved.
the menuOpen property relates to the matMenuTrigger.
here's an example:
<button [ngClass]="{'active-icon': trigger.menuOpen}" type="button" mat-
icon-button #trigger="matMenuTrigger" [matMenuTriggerFor]="help">
<mat-icon></mat-icon>
</button>
<mat-menu #help="matMenu">
<div> textId </div>
</mat-menu>
Disclaimer: React noob over here - I apologize if I'm bastardizing the way it's meant to be used.
I'm trying to use jQuery's draggable API in conjunction with React components. However, whenever I drag the list item, the object associated to the drag event is the same.
class CompanyListItem extends React.Component {
componentDidMount() {
that = this
$("[role='draggable']").draggable({
start: (e, ui)=> {
that.props.dragHandler(event, ui, this);
},
revert: true
});
}
render() {
return (
<li className="draggable company" id={this.props.company.id} role="draggable">
<span>{this.props.company.name}</span>
</li>
);
}
}
class CompanyList extends React.Component {
render() {
var rows = []
_.each(this.props.companies, (company) => {
rows.push(<CompanyListItem company={company} dragHandler={this.props.dragHandler} key={company.id}/>);
});
return (
<ul> Select
{rows}
</ul>
);
}
}
For example, the component rendered below:
The object tied to both list elements is the "HopShop" (the first rendered list item)
Everytime you call $("[role='draggable']").draggable(...) you reset the other components. You should call it on the id instead.
componentDidMount() {
that = this
$("#" + this.props.company.id).draggable({
start: (e, ui)=> {
that.props.dragHandler(event, ui, this);
},
revert: true
});
}