I can't understand how to make css animations to an element no sooner than it appears on page, like css3 transform rotate. when I set in attached(){performRotation()} when the element appears on page it's already rotated. I couldn't find in all documentation which event I need to subscribe to in order to start animation.
You can do that with the help of NeonAnimatableBehavior and NeonAnimationRunnerBehavior, you may configure your animations in animationConfig property and then play them with playAnimation('name') function which is implemented by NeonAnimationRunnerBehavior .
Please check lifecycle callbacks and neon-animation demos.
behaviors: [
Polymer.NeonAnimatableBehavior,
Polymer.NeonAnimationRunnerBehavior
],
properties: {
animationConfig: { ... },
}
...
ready: function() {
this.playAnimation('entry');
}
I've made you a simple example using the above elements to give you a quick start.
Update
You can modify each configured animation based on given properties in one of the lifecycle callbacks, e.g. ready callback:
var entryAnimation = this.animationConfig.entry[0];
entryAnimation.transformFrom = this.doSomethingTo(this.someProperty);
Please check the updated demo.
Related
I'm using react-leaflet with the Geoman plugin, and I notice that pm:create doesn't fire when I add new layers programmatically. This code runs on startup and again anytime the activeFeatureGroup is changed:
map.pm.setGlobalOptions({
...map.pm.getGlobalOptions(),
layerGroup: activeFeatureGroup,
hintlineStyle: { color },
templineStyle: { color },
})
This is my function to programmatically add new layers from GeoJSON:
const opts = { style: { color } }
geoJSON(newGeoJsonObject, opts).addTo(activeFeatureGroup)
How can I get pm:create to fire after this code runs? The main concern is that I'm adding event listeners to every layer that pm:create sees, so an alternative solution would be a way to set one listener on the map instance that fires anytime a layer is added in Geoman, but I don't see support for this in the Geoman docs.
How can I get pm:create to fire after this code runs?
You can achive this in 3 different ways:
Create a function and call it after running the geoJson part and in the pm:create listener
add a listener on the map layeradd (leaflet event) and run the same code as in pm:create
you can fire pm:create manually with map.fire('pm:create',{layer: XYZ}); (I don't recommand this!)
alternative solution would be a way to set one listener on the map instance that fires anytime a layer is added in Geoman
This is pm:create ... if you mean to fire an event for each layer that is intialized and is added to Geoman, you can achive this by listening on layeradd and then check if the Geoman property layer.pm exists.
I am working on an editable table in Angular application with ag-grid library. I would like to keep editing cells (in full row edit mode) until I finish with it and then close the editors manually via API. The problem is that the editor is closing on other cell click/focus (on some other line) as described here:
The grid will stop editing when any of the following happen:
Other Cell Focus: If focus in the grid goes to another cell, the editing will stop.
I cannot figure out how to disable this, if it is possible. Installing the onCellMouseDown() hook does not help, because the cellFocused event is fired before cellMouseDown. Therefore, the editing stops before I have a chance to intercept the mousedown event.
Here is my stackblitz little extract with related pieces of code.
The need for such scenario is that I want to validate the entry and not to allow a used to quit the editing if the form is not valid. The only workaround I found so far is that on any click outside of editing cells when the editor closing I reopen it right away in onRowEditingStopped() hook unless the editor has been closed via 'OK' button.
After all, I have managed to provide a custom solution that fits exactly into this problem which I was facing also.
First thing is to disable pointer events to non edited rows when a specific row is currently being edited. On Ag-grid's 'cellEditingStarted' callback I have added the following code:
public cellEditingStarted(event: any): void {
//not all rows are on dom ag-grid takes care of it
const nonSelectedGridRows = document.querySelectorAll('.ag-grid-custom-row:not(.ag-row-selected):not(.ag-row-editing):not(.pointer-events-none)');
forEach(nonSelectedGridRows, row => {
row.classList.add("pointer-events-none");
});
}
Because not all rows exist on dom (Ag-grid creates and destroys while you are scrolling )when a specific cell is being edited, I have also added a rowClassRule which is applied when rows are being created:
this.rowClassRules = {
'pointer-events-none': params => {
if (params.api.getEditingCells().length > 0) {
return true;
}
return false;
}
};
scss:
.pointer-events-none {
pointer-events: none
}
By disabling pointer events, when you click on a non edited cell the cell won't get focus and thus the currently edited cell will stil remain on edit mode. You can provide your own custom validation solution and close the editor manually through API. When you are done, you have to enable pointer events to all grid rows back again:
private enablePointerEvents(): void {
//not all rows are on dom ag-grid takes care of it
const nonSelectedGridRows = document.querySelectorAll('.ag-grid-custom-row.pointer-events-none');
forEach(nonSelectedGridRows, row => {
row.classList.remove("pointer-events-none");
});
}
I implemented the same above approach in Ag-Grid React.
I used getRowStyle callback for adding the css pointerEvents: none on dynemic basis.
It seems to be working for me fine.
Please refer the below code
const getRowStyle = (params) => {
// this is not initialized in read mode
// condition for me ==> currentEditRowIndex.current !== null && params.node.rowIndex !== currentEditRowIndex.current
if (someCondition for Row other than inline edit row) {
return { pointerEvents: "none" };
}
return null;
};
After adding this whenver you start the editing..You will need to call redrawRows so that css changes can be applied.
Hope this will help. Thank You!!
Thought I would share another solution that has been working out okay for me so far.
Using 'pointer-events-none' as suggested in the other answer is flawed because the Enter key can also close the editor.
In my case, I want to prevent the editor from closing when client side validation has failed and the data is invalid. When my conditions are met, I call stopPropagation() on the events to prevent the editor close from happening in the first place. It still has potential problems:
It cancels mousedown, dblclick, keydown, focusout and click for all elements that have a class name starting with ag- so if you happen to use this class prefix for other controls on the page, it could interfere. It also means any controls within the grid (sorting, resizing, etc.) don't work while the condition is met.
Calling stopPropagation() could potentially interfere with your own custom controls. So far I've been okay if I dont use the ag- prefix within the markup from my own custom cell editors and renderers
I hope they can add a proper API function to cancel the row/cell stopEditing function in the future.
["mousedown", "dblclick", "keydown", "focusout", "click"].forEach(function (eventName) {
document.addEventListener(eventName, function (e) {
if ( conditionForCancelingIsMet() ) {
// this appears to cancel some events in agGrid, it works for
// preventing editor closing on clicking other cells for example.
// It would be ideal if this worked for all ag-grid specific events
// and had a proper public API to use!
e["__ag_Grid_Stop_Propagation"] = true;
}
// convert element classList to normal js array so we can use some()
var classArray = [].slice.apply(e.target.classList);
if ( conditionForCancelingIsMet() && classArray.some(c => c.startsWith("ag-")) ) {
// unfortunately some events like pressing the 'enter' key still
// require stopPropagation() and could potentially interfere with unrelated controls
e.stopPropagation();
}
}, true);
});
I have a Detail view in an SAPUI5 app which contains a single input field with ID "bestellmenge_tu". Whenever this view is called, the focus should be on that input field. Unfortunately, when setting the focus on the field in the controller's onInit method, the focus will be set but some milliseconds later the UI5 takes it away and transfers it to the "navigation back" button of the detail view.
By putting a log.trace() on the input field's blur event, I found out that the focus is taken away by a method called sap.ui.define.NavContainer._afterTransitionCallback which is called asynchronously (some window.setTimeouts between trigger and execution). The function simply looks for the first focusable element in the view and brutally switches the focus on it.
My workaround was to redefine the method jQuery.fn.firstFocusableDomRef which is used to find this "first focusable element":
// DIESE KANONE FUNKTIONIERT
jQuery.fn.firstFocusableDomRef = (function() {
var _default = jQuery.fn.firstFocusableDomRef;
return function() {
var bestellmenge_tu = document.querySelector("input[id$='bestellmenge_tu-inner']");
if (bestellmenge_tu &&
bestellmenge_tu.style.display !="none" &&
bestellmenge_tu.style.visibility != "hidden") return bestellmenge_tu;
else return _default.apply(this);
}
})();
But this could be a performance issue (querySelector called during DOM transversal at any page load from there on), and it is too much coding for the desired effect.
Is there an easier method to achieve this?
I thought of something like
<mvc:View controllerName="zrt_dispo.view.Detail"...>
<Page id="detailPage" initialFocus="bestellmenge_tu"> <!-- ID of the element to carry the focus -->
</Page>
</mvc:view>
Here is a working example: https://embed.plnkr.co/wp6yes/
<App autoFocus="false" xmlns="sap.m"> <!-- AND/OR -->
<f:FlexibleColumnLayout autoFocus="false" xmlns:f="sap.f" /> <!-- autoFocus in FCL available since 1.76 -->
{ // Controller of the target view:
onInit: function() {
this.attachAfterShow(this.onAfterShow);
},
attachAfterShow: function(onAfterShow) {
this._afterShowDelegate = { onAfterShow };
this.getView().addEventDelegate(this._afterShowDelegate, this);
},
onAfterShow: function() {
this.byId("thatControl").focus();
},
onExit: function() { // detach delegates
this.getView().removeEventDelegate(this._afterShowDelegate);
this._afterShowDelegate = null;
}
}
If you have a sap.m.NavContainer (I.e. sap.m.App or in sap.f.FlexibleColumnLayout), its direct child, aka. NavContainerChild which is usually a View, can react to navigation related events. Add a delegate to the NavContainerChild event afterShow according to the API reference of NavContainer:
The afterShow event can be used to focus another element, only if autoFocus is set to false.
The event handler is fired after the animation is finished. And most importantly:
This event is fired every time (in contrast to onAfterRendering) when the NavContainer has made this child control visible.
Since sap.ui.core.Control extends sap.ui.core.Element, every control can receive the focus via focus().
Known Issues in Older UI5 Versions
SAP Fiori launchpad (FLP) running with older SAPUI5 versions might take the control of the initial focus for the first app launch: FLP: Setting Custom Initial Focus on App Launch Fails
However, it's no longer reproducible (Let me know if otherwise).
In UI5 1.62 and below, calling focus() in onAfterShow alone would still make the app set the focus on the first focusable element when the user navigates back, even with autoFocus="false" (See this GitHub issue #2306). In that case, an additional setTimeout with 0 ms (or requestAnimationFrame) is needed in order to let the browser know that the element should be focused at the end of the call stack.
onAfterShow: function() {
// Only if UI5 version < 1.63:
setTimeout(() => this.byId("thatControl").focus());
},
With commit:6d46cf0, the fix is available as of 1.63.
The NavContainer has a property autoFocus. App is a descendant of NavContainer so it has that property too.
The help (as linked above) states the following:
Determines whether the initial focus is set automatically on first rendering and after navigating to a new page. This is useful when on touch devices the keyboard pops out due to the focus being automatically set on an input field. If necessary the "afterShow" event can be used to focus another element.
Default value is true.
Using the jQuery Fancybox plugin with Isotope, I'm trying to figure out how to update the Fancybox gallery order in lightbox view after I change the Isotope sort by options.
When I re-sort the images I need to be able to tell Fancybox what the new order is, so that when I navigate between images in lightbox view it goes to the next image in the newly sorted order. Right now the next/previous buttons take you to the next/previous image in the original sort order.
Any help is much appreciated.
With reference to this page, the relevant part looks something like this...
$('.option-set').change(function() {
var $this = $(this);
var delay = 1100; // approx 1 second delay after last input
clearTimeout($this.data('timer'));
$this.data('timer', setTimeout(function(){
$this.removeData('timer');
$('a[rel^="lightbox"]').each(function() {
var opacity = $(this).parent().css("opacity");
$(this).attr('rel','lightbox['+opacity+']');
});
Shadowbox.clearCache();
Shadowbox.setup();
}, delay));
});
It's a hack of course. Whenever one of the checkboxes is changed, this routine waits a bit to let isotope do its thing and then updates all 'rels' to correspond to the opacity of their respective parents. So there will actually be two sets of rels (lightbox[0] and lightbox[1]). But because there is no visible thumbnail for lightbox[0], those images are in effect removed from the lightbox/shadowbox.
I came across the same problem and looked around to find a solution and stumbled upon this as well. As I couldn't find a solution, I thought I'd try it myself and the solution was simple. Not sure if this has an affect on performance of the browser, but simple DOM manipulation will give you the required behavior. I'm using isotope V2 and events in this version is a bit different to that in V1. Fancybox version shouldn't matter.
First you have to make sure you have set isInitLayout: false when initializing isotope.
var $container = $("#isotopeContainer");
$container.isotope({
//other options
isInitLayout: false
});
After that, you have to bind to the layoutComplete event on your isotope container.
$container.isotope('on', 'layoutComplete', function(isoInstance, laidOutItems) {
var $firstItem = laidOutItems[0].element;
$($firstItem).prependTo(isoInstance.element);
var $lastMovedItem = $firstItem,$nextItem;
for (var i = 0; i < laidOutItems.length; i++) {
$nextItem = laidOutItems[i].element;
if ($nextItem != $firstItem) {
$($nextItem).insertAfter($lastMovedItem);
$lastMovedItem = $nextItem;
}
}
});
As you set isInitLayout to false while initializing isotope, you have to call the arrange method manually to lay it all out properly.
$container.isotope('arrange');
I'm pretty sure there is room for improvement. But I'm happy with this solution.
Hope someone will find this useful.
Came across the exact same issue tonight!
I suggest re-ordering the order of your griditems in javascript, before you call the isotope function, that way, all your items will already be in the correct order and any lightbox plugin won't get confused :)
(like this for example: jquery sort list based on data attribute value)
Hope I helped someone ;-) Worked like a charm for me :-)
My code does a .pantolatlong then a .showinfobox
The info box does not appear, unless I remove the pantolatlong. I guess it is stopping it. I tried adding it to the endpan event but that did not work.
What is the simplest way to pan to a pushpin and display the infobox for it?
I was using setcenter, but I discovered that sometimes setcenter pans, and this breaks it.
After some insane googling, I came up with the solution, and I'll share it here so that others can hopefully not have the grief I went through.
I created and power my bing map using pure javascript, no sdk or iframe solutions. In my code, I generate the javascript to add all of the pins I want to the map, and inject it using an asp.net label.
If you call the setCenter() method on your Bing Map, it is supposed to instantly set the map, surprise surprise, to the coordinates you specify. And it does... most of the time. Occasionally though, it decides to pan between points. If you do a SetCenter, followed by a ShowInfoBox, it will work great, unless it decides to pan.
The solution? Being great programmers we are, we dive into the sdk, and it reveals there are events we can hook into to deal with these. There is an onendpan event, which is triggered after a pan is completed. There is also an onchangeview event, which triggers when the map jumps.
So we hook into these events, and try to display the infobox for our pushpin shape... but nothing happens. Why not?
You have to give it a few milliseconds to catch its breath, for unknown reasons, when the event is called. Using a setTimeout with 10 milliseconds seems to be fine. Your box will appear great after this.
The next problem is, you only want it to appear when it pans via whatever you used to make it flick between your pushpins (in my case, a table with onclick methods). I create/destroy the event handlers on the fly, although there are other options such as using a global variable to track if the user is panning, or if the system is panning in response to a click.
Finally, you have the one bug that comes from this. If you click a place in your list, and it jumps/pans to that location, the infobox will display fine. If the user dismisses it though, then clicks again on the list item, the map does not move, and therefore no events are triggered.
My solution to this is to detect if the map moved or not, by recording its long/lat, and using another setTimeout method, detecting if they changed 100ms later. If they did not, display the infobox.
There are other things you need to keep track of, as there is no way I can see to pass parameters to the eventhandlers so I use global javascript variables for this - you have to know which pushpin shape you are displaying, and also keep track of the previous mapcoordinates before checking to see if they changed.
It took me a while to piece all this together, but it seems to work. Here is my code, some sections are removed:
// An array of our pins to allow panning to them
var myPushPins = [];
// Used by the eventhandler
var eventPinIndex;
var oldMapCenter;
// Zoom in and center on a pin, then show its information box
function ShowPushPin(pinIndex) {
eventPinIndex = pinIndex;
oldMapCenter = map.GetCenter();
map.AttachEvent("onendpan", EndPanHandler);
map.AttachEvent("onchangeview", ChangeViewHandler);
setTimeout("DetectNoMapChange();", 200);
map.SetZoomLevel(9);
map.SetCenter(myPushPins[pinIndex].GetPoints()[0]);
}
function EndPanHandler(e) {
map.DetachEvent("onendpan", EndPanHandler);
setTimeout("map.ShowInfoBox(myPushPins[eventPinIndex]);", 10);
}
function ChangeViewHandler(e) {
map.DetachEvent("onchangeview", ChangeViewHandler);
setTimeout("map.ShowInfoBox(myPushPins[eventPinIndex]);", 10);
}
function DetectNoMapChange(centerofmap) {
if (map.GetCenter().Latitude == oldMapCenter.Latitude && map.GetCenter().Longitude == oldMapCenter.Longitude) {
map.ShowInfoBox(myPushPins[eventPinIndex]);
}
}
Here is another way:
function addPushpin(lat,lon,pinNumber) {
var pinLocation = new Microsoft.Maps.Location(lat, lon);
var pin = new Microsoft.Maps.Pushpin(map.getCenter(), { text: pinNumber.toString() });
pinInfobox = new Microsoft.Maps.Infobox(pinLocation,
{ title: 'Details',
description: 'Latitude: ' + lat.toString() + ' Longitude: ' + lon.toString(),
offset: new Microsoft.Maps.Point(0, 15)
});
map.entities.push(pinInfobox);
map.entities.push(pin);
pin.setLocation(pinLocation);
map.setView({ center: pinLocation});
}