Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 72 additions & 3 deletions modules/detour/src/lib/detour.factories.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,74 @@
import { Param, ParamEvaluatorService } from "@rollthecloudinc/dparam";
import { InteractionEventPlugin } from "./models/interaction-event.models";
import { InteractionHandlerPlugin } from "./models/interaction-handler.models";
import { map } from "rxjs/operators";
import { Renderer2 } from "@angular/core";
import { Observable } from "rxjs";

export const interactionEventDomFactory = () => {
return new InteractionEventPlugin<string>({ title: 'DOM', id: 'dom' });
};
export const interactionEventDomFactory = (paramEvaluatorService: ParamEvaluatorService) => {
return new InteractionEventPlugin<string>({
title: 'DOM',
id: 'dom',
connect: ({ filteredListeners, listenerParams, renderer, callback }) => new Observable(obs => {
const mapTypes = new Map<string, Array<number>>();
const len = filteredListeners.length;
for (let i = 0; i < len; i++) {
const type = (listenerParams[i] as any).type;
if (mapTypes.has(type)) {
const targets = mapTypes.get(type);
targets.push(i);
mapTypes.set(type, targets);
} else {
mapTypes.set(type, [i]);
}
}
const eventDelegtionHandler = (m => e => {
if (m.has(e.type)) {
const targets = m.get(e.type);
const len = targets.length;
targets.forEach((__, i) => {
const expectedTarget = (listenerParams[targets[i]] as any).target;
if (e.target.matches(expectedTarget)) {
console.log(`delegated target match ${expectedTarget}`);
if(filteredListeners[i].handler.settings.params) {
const paramNames = filteredListeners[i].handler.settings.paramsString ? filteredListeners[i].handler.settings.paramsString.split('&').filter(v => v.indexOf('=:') !== -1).map(v => v.split('=', 2)[1].substr(1)) : [];
paramEvaluatorService.paramValues(
filteredListeners[i].handler.settings.params.reduce((p, c, i) => new Map<string, Param>([ ...p, [ paramNames[i], c ] ]), new Map<string, Param>())
).pipe(
map(params => Array.from(params).reduce((p, [k, v]) => ({ ...p, [k]: v }), {}))
).subscribe((handlerParams) => {
// plugin call and pass params
// console.log('handler original event and params', e, filteredListeners[i].handler.plugin, handlerParams);
callback({ handlerParams, plugin: filteredListeners[i].handler.plugin, index: i, evt: e });
})
} else {
// plugin call and pass params
// console.log('handler original event and params', filteredListeners[i].handler.plugin, e);
callback({ handlerParams: {}, plugin: filteredListeners[i].handler.plugin, index: i, evt: e });
}
}
});
}
})(mapTypes)
const keys = Array.from(mapTypes);
for (let i = 0; i < keys.length; i++) {
const type = keys[i][0];
renderer.listen('document', type, e => {
eventDelegtionHandler(e);
});
}
obs.next({});
obs.complete();
})
});
};

export const interactionHandlerHelloWorldFactory = () => {
return new InteractionHandlerPlugin<string>({
title: 'Hello World',
id: 'hello_world',
handle: ({}) => {
console.log("Hello World");
}
})
}
12 changes: 8 additions & 4 deletions modules/detour/src/lib/detour.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MaterialModule } from '@rollthecloudinc/material';
import { DparamModule } from '@rollthecloudinc/dparam';
import { DparamModule, ParamEvaluatorService } from '@rollthecloudinc/dparam';
import { InteractionListenerComponent } from './components/interaction-listener/interaction-listener.component';
import { InteractionsDialogComponent } from './components/interactions-dialog/interactions-dialog.component';
import { InteractionsFormComponent } from './components/interactions-form/interactions-form.component';
import { InteractionEventPluginManager } from './services/interaction-event-plugin-manager.service';
import { interactionEventDomFactory } from './detour.factories';
import { interactionEventDomFactory, interactionHandlerHelloWorldFactory } from './detour.factories';
import { InteractionHandlerPluginManager } from './services/interaction-handler-plugin-manager.service';

@NgModule({
declarations: [
Expand All @@ -30,8 +31,11 @@ import { interactionEventDomFactory } from './detour.factories';
})
export class DetourModule {
constructor(
iepm: InteractionEventPluginManager
iepm: InteractionEventPluginManager,
ihpm: InteractionHandlerPluginManager,
paramEvaluatorService: ParamEvaluatorService
) {
iepm.register(interactionEventDomFactory());
iepm.register(interactionEventDomFactory(paramEvaluatorService));
ihpm.register(interactionHandlerHelloWorldFactory());
}
}
12 changes: 11 additions & 1 deletion modules/detour/src/lib/models/interaction-event.models.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
import { Type } from '@angular/core';
import { Renderer2, Type } from '@angular/core';
import { Plugin } from '@rollthecloudinc/plugin';
import { Observable } from 'rxjs';
import { InteractionListener } from './interaction.models';

export type InteractionEventCallbackInput = { handlerParams: {}, plugin: string, index: number, evt: any };
export type InteractionEventCallback = ({ handlerParams, plugin, index, evt }) => void
export type InteractionEventOutput = { };
export type InteractionEventInput = { filteredListeners: Array<InteractionListener>, listenerParams: {}, renderer: Renderer2, callback: InteractionEventCallback };

export class InteractionEventPlugin<T = string> extends Plugin<T> {
// editor: Type<any>;
// errorMessage: string;
// builder: ({ v, serialized }: { v: ValidationValidator, serialized: boolean }) => Observable<AsyncValidatorFn>;
connect: ({ filteredListeners, listenerParams, renderer, callback }: InteractionEventInput) => Observable<InteractionEventOutput>;
constructor(data?: InteractionEventPlugin<T>) {
super(data)
if(data) {
// Probably should be required to have a connection.
this.connect = data.connect;
// this.editor = data.editor;
// this.errorMessage = data.errorMessage;
// this.builder = data.builder;
Expand Down
7 changes: 6 additions & 1 deletion modules/detour/src/lib/models/interaction-handler.models.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
import { Type } from '@angular/core';
import { Type, Renderer2 } from '@angular/core';
import { Plugin } from '@rollthecloudinc/plugin';
import { InteractionListener } from './interaction.models';

export type InteractionHandlerInput = { handlerParams: {}, plugin: string, index: number, evt: any, listener: InteractionListener, renderer: Renderer2 }

export class InteractionHandlerPlugin<T = string> extends Plugin<T> {
// editor: Type<any>;
// editor: Type<any>;
// errorMessage: string;
// builder: ({ v, serialized }: { v: ValidationValidator, serialized: boolean }) => Observable<AsyncValidatorFn>;
handle: ({ handlerParams, plugin, index, evt, listener, renderer } : InteractionHandlerInput) => void;
constructor(data?: InteractionHandlerPlugin<T>) {
super(data)
if(data) {
this.handle = data.handle;
//this.editor = data.editor;
// this.editor = data.editor;
// this.errorMessage = data.errorMessage;
Expand Down
185 changes: 184 additions & 1 deletion modules/render/src/lib/components/panel-page/panel-page.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { CONTENT_PLUGIN, ContentPlugin, ContentPluginManager } from '@rolltheclo
import { GridLayoutComponent, LayoutPluginManager } from '@rollthecloudinc/layout';
import { AsyncApiCallHelperService, StyleLoaderService } from '@rollthecloudinc/utils';
import { FilesService, MediaSettings, MEDIA_SETTINGS } from '@rollthecloudinc/media';
import { InteractionListener } from '@rollthecloudinc/detour';
import { InteractionEventPluginManager, InteractionHandlerPluginManager, InteractionListener } from '@rollthecloudinc/detour';
import { /*ContextManagerService, */ InlineContext, ContextPluginManager, InlineContextResolverService } from '@rollthecloudinc/context';
import { PanelPage, Pane, LayoutSetting, CssHelperService, PanelsContextService, PageBuilderFacade, FormService, PanelPageForm, PanelPageState, PanelContentHandler, PaneStateService, Panel, StylePlugin, PanelResolverService, StylePluginManager, StyleResolverService } from '@rollthecloudinc/panels';
import { DisplayGrid, GridsterConfig, GridType, GridsterItem } from 'angular-gridster2';
Expand All @@ -29,6 +29,7 @@ import { camelize } from 'inflected';
import merge from 'deepmerge-json';
import { DOCUMENT } from '@angular/common';
import { AuthFacade } from '@rollthecloudinc/auth';
import { Param, ParamEvaluatorService } from '@rollthecloudinc/dparam';

@Component({
selector: 'classifieds-ui-panel-page',
Expand Down Expand Up @@ -377,6 +378,140 @@ export class PanelPageComponent implements OnInit, AfterViewInit, AfterContentIn
tap(() => this.isStable = false)
).subscribe();

readonly wireListenersSub = combineLatest([
this.listeners$,
this.renderLayout$,
this.afterContentInit$,
]).pipe(
delay(1),
switchMap(() => forkJoin(this.filteredListeners.map(l => of({}).pipe(
map(() => ({ paramNames: l.event.settings.paramsString ? l.event.settings.paramsString.split('&').filter(v => v.indexOf('=:') !== -1).map(v => v.split('=', 2)[1].substr(1)) : [] })),
switchMap(({ paramNames }) => this.paramEvaluatorService.paramValues(l.event.settings.params.reduce((p, c, i) => new Map<string, Param>([ ...p, [ paramNames[i], c ] ]), new Map<string, Param>())).pipe(
map(params => Array.from(params).reduce((p, [k, v]) => ({ ...p, [k]: v }), {}))
)),
defaultIfEmpty([])
)
))),
switchMap(listenerParams => this.iepm.getPlugin('dom').pipe(
map(p => ({ p, listenerParams }))
)),
switchMap(({ p, listenerParams }) => p.connect({
filteredListeners: this.filteredListeners,
listenerParams,
renderer: this.renderer,
callback: ({ handlerParams, plugin, index, evt }) => {
// console.log(`The handler was called`, handlerParams, plugin, index, this.filteredListeners[index], evt );
this.ihpm.getPlugin(plugin).pipe(
tap(p => {
p.handle({
handlerParams,
plugin,
index,
listener: this.filteredListeners[index],
evt,
renderer: this.renderer });
})
).subscribe();
}
})),
tap((listenerParams) => {
console.log('listener info', this.filteredListeners, listenerParams);

/*this.iepm.getPlugin('dom').subscribe(p => {
p.connect({
filteredListeners: this.filteredListeners,
listenerParams,
renderer: this.renderer,
callback: ({ handlerParams, plugin, index, evt }) => {
console.log(`The handler was called`, handlerParams, plugin, index, this.filteredListeners[index], evt );
}
}).subscribe();
});*/

// The hard way to handle events using our own delegation algorithm
// since nodes are constantly changing underneath and simple way
// doesn't seem to work.

// This is all going to be part of the plugin function anyway.

/*const mapTypes = new Map<string, Array<number>>();
const len = this.filteredListeners.length;
for (let i = 0; i < len; i++) {
const type = (listenerParams[i] as any).type;
if (mapTypes.has(type)) {
const targets = mapTypes.get(type);
targets.push(i);
mapTypes.set(type, targets);
} else {
mapTypes.set(type, [i]);
}
}
const eventDelegtionHandler = (m => e => {
if (m.has(e.type)) {
const targets = m.get(e.type);
const len = targets.length;
targets.forEach((__, i) => {
const expectedTarget = (listenerParams[targets[i]] as any).target;
if (e.target.matches(expectedTarget)) {
console.log(`delegated target match ${expectedTarget}`);
if(this.filteredListeners[i].handler.settings.params) {
const paramNames = this.filteredListeners[i].handler.settings.paramsString ? this.filteredListeners[i].handler.settings.paramsString.split('&').filter(v => v.indexOf('=:') !== -1).map(v => v.split('=', 2)[1].substr(1)) : [];
this.paramEvaluatorService.paramValues(
this.filteredListeners[i].handler.settings.params.reduce((p, c, i) => new Map<string, Param>([ ...p, [ paramNames[i], c ] ]), new Map<string, Param>())
).pipe(
map(params => Array.from(params).reduce((p, [k, v]) => ({ ...p, [k]: v }), {}))
).subscribe((handlerParams) => {
// plugin call and pass params
console.log('handler original event and params', e, this.filteredListeners[i].handler.plugin, handlerParams);
})
} else {
// plugin call and pass params
console.log('handler original event and params', this.filteredListeners[i].handler.plugin, e);
}
}
});
}
})(mapTypes)
const keys = Array.from(mapTypes);
for (let i = 0; i < keys.length; i++) {
const type = keys[i][0];
this.renderer.listen('document', type, e => {
eventDelegtionHandler(e);
});
}*/

/*this.renderer.listen('document', 'click', e => {
console.log('delegated target');
if (e.target.matches('.open-dialog')) {
console.log('delegated target match');
}
});*/

/*const listenerLen = this.filteredListeners.length;
for (let i = 0; i < listenerLen; i++) {
// Assumption is made herre that would be responsibility of plugin instead ie. target is required for DOM event.
// For now though just to get things spinning again hard code expectation.
const targets =(this.el.nativeElement as Element).querySelectorAll((listenerParams[i] as any).target);
console.log('listener target', targets);
targets.forEach(t => this.renderer.listen(t, (listenerParams[i] as any).type, e => {
console.log('listener fired');
if(this.filteredListeners[i].handler.settings.params) {
const paramNames = this.filteredListeners[i].handler.settings.paramsString ? this.filteredListeners[i].handler.settings.paramsString.split('&').filter(v => v.indexOf('=:') !== -1).map(v => v.split('=', 2)[1].substr(1)) : [];
this.paramEvaluatorService.paramValues(
this.filteredListeners[i].handler.settings.params.reduce((p, c, i) => new Map<string, Param>([ ...p, [ paramNames[i], c ] ]), new Map<string, Param>())
).pipe(
map(params => Array.from(params).reduce((p, [k, v]) => ({ ...p, [k]: v }), {}))
).subscribe((handlerParams) => {
console.log('handler original event and params',e, handlerParams);
});
} else {
console.log('handler original event and params', e);
}
}));
}*/
})
).subscribe()

get panelsArray(): UntypedFormArray {
return this.pageForm.get('panels') as UntypedFormArray;
}
Expand Down Expand Up @@ -412,6 +547,10 @@ export class PanelPageComponent implements OnInit, AfterViewInit, AfterContentIn
private classifyService: ClassifyService,
private fileService: FilesService,
private authFacade: AuthFacade,
private paramEvaluatorService: ParamEvaluatorService,
private renderer: Renderer2,
private iepm: InteractionEventPluginManager,
private ihpm: InteractionHandlerPluginManager,
es: EntityServices,
) {
this.panelPageService = es.getEntityCollectionService('PanelPage');
Expand Down Expand Up @@ -761,10 +900,52 @@ export class RenderPaneComponent implements OnInit, OnChanges, ControlValueAcces
]).pipe(
map(([l]) => l),
tap(listeners => {
this.filteredListeners = listeners
console.log('pane listeners', listeners);
})
).subscribe();

readonly wireListenersSub = combineLatest([
this.listeners$,
this.afterContentInit$,
]).pipe(
delay(1),
switchMap(() => forkJoin(this.filteredListeners.map(l => of({}).pipe(
map(() => ({ paramNames: l.event.settings.paramsString ? l.event.settings.paramsString.split('&').filter(v => v.indexOf('=:') !== -1).map(v => v.split('=', 2)[1].substr(1)) : [] })),
switchMap(({ paramNames }) => this.paramEvaluatorService.paramValues(l.event.settings.params.reduce((p, c, i) => new Map<string, Param>([ ...p, [ paramNames[i], c ] ]), new Map<string, Param>())).pipe(
map(params => Array.from(params).reduce((p, [k, v]) => ({ ...p, [k]: v }), {}))
)),
defaultIfEmpty([])
)
))),
tap((listenerParams) => {
console.log('listener info', this.filteredListeners, listenerParams);
const listenerLen = this.filteredListeners.length;
for (let i = 0; i < listenerLen; i++) {
// Assumption is made herre that would be responsibility of plugin instead ie. target is required for DOM event.
// For now though just to get things spinning again hard code expectation.
const targets =(this.el.nativeElement as Element).querySelectorAll((listenerParams[i] as any).target);
console.log('listener target', targets);
targets.forEach(t => this.renderer.listen(t, (listenerParams[i] as any).type, e => {
console.log('listener fired');
if(this.filteredListeners[i].handler.settings.params) {
const paramNames = this.filteredListeners[i].handler.settings.paramsString ? this.filteredListeners[i].handler.settings.paramsString.split('&').filter(v => v.indexOf('=:') !== -1).map(v => v.split('=', 2)[1].substr(1)) : [];
this.paramEvaluatorService.paramValues(
this.filteredListeners[i].handler.settings.params.reduce((p, c, i) => new Map<string, Param>([ ...p, [ paramNames[i], c ] ]), new Map<string, Param>())
).pipe(
map(params => Array.from(params).reduce((p, [k, v]) => ({ ...p, [k]: v }), {}))
).subscribe((handlerParams) => {
console.log('handler original event and params',e, handlerParams);
});
} else {
console.log('handler original event and params', e);
}
}));

}
})
).subscribe()

paneForm = this.fb.group({
contentPlugin: this.fb.control('', Validators.required),
name: this.fb.control(''),
Expand Down Expand Up @@ -832,6 +1013,8 @@ export class RenderPaneComponent implements OnInit, OnChanges, ControlValueAcces
private cpm: ContentPluginManager,
private cssHelper: CssHelperService,
private paneStateService: PaneStateService,
private paramEvaluatorService: ParamEvaluatorService,
private renderer: Renderer2,
es: EntityServices
) {
this.panelPageStateService = es.getEntityCollectionService('PanelPageState');
Expand Down
4 changes: 2 additions & 2 deletions modules/render/src/lib/render.factories.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { InteractionHandlerPlugin } from '@rollthecloudinc/detour';

export const interationHandlerFormSubmit = () => {
return new InteractionHandlerPlugin<string>({ id: 'panels_form_submit', title: 'Submit Panels Form' });
return new InteractionHandlerPlugin<string>({ id: 'panels_form_submit', title: 'Submit Panels Form', handle: () => {} });
};

export const interationHandlerDialog = () => {
return new InteractionHandlerPlugin<string>({ id: 'panels_dialog', title: 'Open Panels Dialog' });
return new InteractionHandlerPlugin<string>({ id: 'panels_dialog', title: 'Open Panels Dialog', handle: () => {} });
};