how to access react-konva “stage” or “layer” from event handler? - react-konva

How would one access the “stage” or “layer” from an event hanlde when using react-konva? (i.e. with JSX)
For example in this example: https://konvajs.org/docs/react/index.html, how would one access stage or layer in the event handler if one wanted to do a “find”, to search for a particular canvas node…

onClick={(e) => {
const stage = e.target.getStage();
const layer = e.target.getLayer();
}}

Related

Double Click Radzen Blazor Tree Items

I am using Radzen Blazor Tree component in a Blazor project. By default the component comes with an event listener for single click on RadzenTreeItem. I am hoping to add functionality such that a single click on a RadzenTreeItem and a double click on a RadzenTreeItem produces different changes to the app. I am thinking of accomplishing this by using a JSInterop for the dblclick event listener, but I know this would require having individual html ids for each RadzenTreeItem and I am not sure how to go about creating unique ids for a RadzenTreeItem since the tree has a dynamic size. My question is how to create unique ids for a RadzenTreeItem if each item is being created in a RenderFragment or is there a better way to allow double click functionality?
Below is an example of the C# RenderFragment that is used to display the RadzenTreeItem:
public static RenderFragment<RadzenTreeItem> TreeDesign = (RadzenTreeItem context) => builder =>
{
builder.OpenComponent<RadzenIcon>(0);
builder.AddAttribute(1, "Icon", "crop_16_9");
builder.CloseComponent();
builder.AddContent(4, context.Text);
};
Below is an example of the html code that is calling the RenderFragment and creating the tree:
<RadzenTree Data="#entries" Change="#OnChange">
<RadzenTreeLevel Template="#TreeDesign" Text="#GetTextForNode" />
</RadzenTree>
5 months old - but I'll give you a shot on how I handle double click years ago in Silverlight.... I created an Observable that would give me an event when there were two clicks within 250MS of each other... this is called a Hot Observable in which the events simply flow by, and when a criteria matches the events, it can raise another event... I can try and dig up the code, as researching for a a double-click event and protection for blazor events.
To add perhaps a bit more clarity - this would listen to the click event you refer to, and if there were two clicks, it would invoke method A, other wise would invoke B for a single click...
A triple click would then become 1 double click, and one single click - but that would depend on the time between the first and thrid event using the Observable...
Maybe it will help someone in the future:
builder =>
{
builder.OpenComponent<RadzenIcon>(0);
builder.AddAttribute(1, "Icon", "crop_16_9");
builder.CloseComponent();
builder.OpenElement(2, "div");
builder.AddAttribute<MouseEventArgs>(3, "ondblclick", RuntimeHelpers.TypeCheck<EventCallback<MouseEventArgs>>(EventCallback.Factory.Create<MouseEventArgs>(this,
(args) => OnDoubleClick(args, context)
)));
builder.AddContent(10, context.Text);
builder.CloseElement();
};
};
private void OnDoubleClick(MouseEventArgs args, RadzenTreeItem item)
{
//
}

How to close multiple dialog box using Angular

I have a running Angular 9 application and I have created custom dialog box. I have also used ComponentFactoryResolver to dynamically load the component.
My custom dialog box looks like:
So, when I click on close button, the dialog box closes.
As per the current implementation, if I open multiple dialog box on the screen, then I am able to close only last opened dialog box by clicking on close button.
My expected behavior is to close all the dialog box. Please help me on this
Stackblitz demo: https://stackblitz.com/edit/dialog-box-overlay
Note: In this stackblitz demo, one modal opens on the top of another modal as I have not modified the css. So, please focus on Modal name to get to know which modal is opened
Instead of assigning the created modal component to the services dcRef property you need to manage all of your modal components, i.e. in a list. Your service's open() method
open(component: Type<any>, modalName: string) {
const factory = this.componentFactoryResolver.resolveComponentFactory(DialogComponent);
this.dcRef = factory.create(this.injector);
...
return this.dcRef;
}
returns the component reference. You could manage this reference from the caller and pass it as an argument to your close() method. When all of the component refs are managed by the service you can also "batch close" all modals (see closeAll()):
#Injectable()
export class DialogService {
refs: ComponentRef<DialogComponent>[] = [];
constructor(private componentFactoryResolver: ComponentFactoryResolver,
private applicationRef: ApplicationRef,
private injector: Injector
) { }
open(component: Type<any>, modalName: string) {
const factory = this.componentFactoryResolver.resolveComponentFactory(DialogComponent);
var ref = factory.create(this.injector);
this.applicationRef.attachView(ref.hostView);
const domElement = (ref.hostView as EmbeddedViewRef<any>).rootNodes[0] as HTMLElement;
document.body.appendChild(domElement);
ref.changeDetectorRef.detectChanges();
ref.instance.open(component, modalName);
this.refs.push(ref);
return ref;
}
close(ref) {
this.applicationRef.detachView(ref.hostView);
ref.instance.close();
ref.destroy();
// Remove ref from a list managed by the service
var i = this.refs.indexOf(ref);
this.refs.splice(i, 1);
}
closeAll()
{
this.refs.forEach(r => this.close(r));
}
}
This is not tested and might need to be tuned, but you should get the idea. Instead of using the ComponentRef as a handle you can also create some custom object to prevent the caller of the modal to interact with the component directly.

Clarity Datagrid column input filter losing focus on first keypress after moving to next page in paginated grid

Using a clarity datagrid version 2.3
Seeing an issue where if the user starts typing into the input field of datagrid column filter, the filter input focuses out automatically as soon as a key is pressed.
Since the datagrid is paginated and server driven, this causes the API to get fired as soon as a
key is pressed after the debounce time.
The automatic focus out of the input field cause the filter to only have a single character and the API gets triggered since the debouce is only 800.
Have looked at clarity github for any reported issues, doesn't look like its reported or anyone having similar issue.
Expected behavior should be the input focus out should not happend until the user moves the cursor away or presses enter, which is when the debounce should kickin after which the api should be called.
HTML:
<clr-datagrid
(clrDgRefresh)= refreshDataGrid($event)>
...
</clr-datagrid>
TS Component:
debouncer = new Subject<any>();
ngOnInit() {
this.debouncer.asObservable().pipe(
debounceTime(800)
).subscribe(state => {
// do something here.. like call an API to filter the grid.
})
}
refreshDataGrid(state) {
this.debouncer.next(state);
}
Any help is appreciated.
Currently I'm hacking my component, to make sure the focus is not lost on the input field until done so by the user.
refreshDataGrid(state) {
const isClrFilterInputField = document.querySelector('.datagrid-filter .clr-input');
if (isClrFilterInputField instanceof HTMLElement) {
isClrFilterInputField.focus();
}
this.debouncer.next(state);
}
This is still not a clean answer, but as far as I have searched, this seems like an issue with clarity datagrid itself, until I hear from someone with a cleaner answer.
Most likely the upgrade version might have this fixed.
Yet to check that.
Unfortunately I think we designed the datagrid to emit the changes on each filter value change with debouncing intended to be done on the app side as consumers see fit.
That said, it is possible to accomplish what you describe. I've implmented a quick and dirty guard based on events but there may be better ways. I'll add code snippets here and a link to the working stackblitz at the end.
You are on the right track with the debouncer. But we don't need to debounce with time, we only need to 'debounce' on certain events.
Instead of debouncing with time, what if we debounce with an #HostListener for clicks on the filter input? (I'll leave it as an exercise for you to implement a HostListener for the focusin event since focusin bubble's up and blur does not). To do that we need:
A Hostlistener that can hear keydown.enter event on the filter input
A guard to prevent requests
A property to store the datagrid state as user enters text
In general the code needs to:
Fetch data when component inits but not after unless directed
Keep track of state events that get emitted from the datagrid
listen to keydown.enter events (and any other events like the filter input focusout - becuase it bubbles up, unlike blur)
Check that the event was generated on a datagrid filter input
dismiss the guard
make the request
re-enlist the guard
Here is a rough attempt that does that:
export class DatagridFullDemo {
refreshGuard = true; // init to true to get first run data
debouncer = new Subject<any>(); // this is now an enter key debouncer
datagridState: ClrDatagridStateInterface; // a place to store datagrid state as it is emitted
ngOnInit() {
// subscribe to the debouncer and pass the state to the doRefresh function
this.debouncer.asObservable().subscribe(state => {
this.doRefresh(state);
});
}
// a private function that takes a datagrid state
private doRefresh(state: ClrDatagridStateInterface) {
// Guard against refreshes ad only run them when true
if (this.refreshGuard) {
this.loading = true;
const filters: { [prop: string]: any[] } = {};
console.log("refresh called");
if (state.filters) {
for (const filter of state.filters) {
const { property, value } = <{ property: string; value: string }>(
filter
);
filters[property] = [value];
}
}
this.inventory
.filter(filters)
.sort(<{ by: string; reverse: boolean }>state.sort)
.fetch(state.page.from, state.page.size)
.then((result: FetchResult) => {
this.users = result.users;
this.total = result.length;
this.loading = false;
this.selectedUser = this.users[1];
// Set the guard back to false to prevent requests
this.refreshGuard = false;
});
}
}
// Listen to keydown.enter events
#HostListener("document:keydown.enter", ["$event"]) enterKeydownHandler(
event: KeyboardEvent
) {
// Use a host listener that checks the event element parent to make sure its a datagrid filter
const eventSource: HTMLElement = event.srcElement as HTMLElement;
const parentElement = eventSource.parentElement as HTMLElement;
if (parentElement.classList.contains("datagrid-filter")) {
// tell our guard its ok to refresh
this.refreshGuard = true;
// pass the latest state to the debouncer to make the request
this.debouncer.next(this.datagridState);
}
}
refresh(state: ClrDatagridStateInterface) {
this.datagridState = state;
this.debouncer.next(state);
}
}
Here is a working stackblitz: https://stackblitz.com/edit/so-60980488

Make ajax call after state change

I want to implement checkbox-based filtering on my page. When I click on checkbox I want to change state and make ajax call based on checked items.
I have created toggleCheckbox(id) action and reducer that adds/removes id from selected items, but I have no idea where should I place ajax call.
Any ideas?
Your component should dispatch the async action, to avoid duplicating the logic for toggling the selected items move it to the async action and change the normal action from toggle to set. Like this:
const toggleCheckbox = id => (dispatch, getState) => {
const items = toggle(id, getState().selectedItems);
dispatch(setSelectedItems(items));
// call API with "items"
};
Network calls are asynchronous, and actions are by default synchronous. They are just plain objets. To handle asynchronous behaviour you need a side-effect library like redux-sagas or redux-thunks. Take a look at https://redux.js.org/docs/advanced/AsyncActions.html and https://redux-saga.js.org

Flex Event flows for built-in event and custom event

I hava a custom component and it contains a child icon. If I add a mouse-click event listener to both component(click-listener1) and icon(click-listener2), the event dispatched sequence is click-listener2, then click-listener1. I can understand it. But if I add a custom event to component (listener1), and mouse-click event to icon(listener2), when icon is clicked, the component will dispatch the custom event. In my test, the event dispatched sequence is listener1, then listener2. It doesn't match with event-bubbles rule.
In my opinion The custom event is dispatched in listener2, which triggers listener1. Why event flow sequence is not listener2, listener1?
In component.
icon.addEventListener(MouseEvent.CLICK, iconClickHandler);
private function iconClickHandler(event:MouseEvent):void
{
trace ("Listener2");
var customEvent:CustomEvent= new CustomEvent(CustomEvent.CUSTOM_EVENT, true, true);
dispatchEvent(customEvent)
trace ("Listener3");
}
In Application, which contains component
component.addEventListener(CustomEvent.CUSTOM_EVENT, customEventHandler);
private function customEventHandler(event:CustomEvent):void {
trace ("Listener1");
}
UPD
You've got:
private function iconClickHandler(event:MouseEvent):void
{
trace("listener2");
var customEvent:CustomEvent= new CustomEvent(CustomEvent.CUSTOM_EVENT, true, true);
dispatchEvent(customEvent);
trace("listener3");
}
private function customEventHandler(event:CustomEvent):void
{
trace("listener1");
}
When MouseEvent.MOUSE_CLICK is dispatched, it triggers first lucky listener - it is your component function iconClickHandler. Here we trace "listener2" and dispatch custom event.
Due to syncronious nature of events, CUSTOM_EVENT listeners are triggered immediatly, that means that dispatching an event is similar to calling listener functions. Events are not stored anywhere, they are not delayed: listeners to events fires immediatly, in the same control flow, in the same thread.
CUSTOM_EVENT was dispatched, its listeners were triggered - we've got a call to customEventHandler and "listener1" in console.
When all the listeners were triggered, control returns to iconClickHandler and "listener3" is traced to console.
That's why we've got output:
listener2
listener1
listener3

Resources