Skip to content

Commit

Permalink
Basic implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
mlhaufe committed Dec 20, 2020
1 parent 602a56d commit 56a1806
Show file tree
Hide file tree
Showing 9 changed files with 562 additions and 1 deletion.
16 changes: 16 additions & 0 deletions src/Agent/Abstraction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*!
* @license
* Copyright (C) 2020 Michael L Haufe
* SPDX-License-Identifier: AGPL-3.0-only
* @see <https://spdx.org/licenses/AGPL-3.0-only.html>
*/

import Contracts from '@final-hill/decorator-contracts';

const {invariant} = new Contracts(true);

/**
* An Abstraction is the business domain functionality associated with an Agent
*/
@invariant
export default class Abstraction {}
13 changes: 13 additions & 0 deletions src/Agent/LayoutOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*!
* @license
* Copyright (C) 2020 Michael L Haufe
* SPDX-License-Identifier: AGPL-3.0-only
* @see <https://spdx.org/licenses/AGPL-3.0-only.html>
*/

enum LayoutOptions {
VERTICAL = 'vertical',
HORIZONTAL = 'horizontal'
}

export default LayoutOptions;
129 changes: 129 additions & 0 deletions src/Agent/Presentation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*!
* @license
* Copyright (C) 2020 Michael L Haufe
* SPDX-License-Identifier: AGPL-3.0-only
* @see <https://spdx.org/licenses/AGPL-3.0-only.html>
*/

import './style.css';

import Contracts from '@final-hill/decorator-contracts';
import LayoutOptions from './LayoutOptions';

const {invariant, demands} = new Contracts(true);

export interface PresentationOptions {
layout?: LayoutOptions;
}

/**
* A Presentation is the visual representation of an Agent
*/
@invariant
export default class Presentation {
declare rootElementType: Element;
#rootElement: this['rootElementType'];

declare containerType: Element;
#containerElement: this['containerType'];

declare childType: Presentation;
#children: this['childType'][] = [];

#handlers: Map<string,EventListener> = new Map();

constructor(options: PresentationOptions = {
layout: LayoutOptions.VERTICAL
}) {
this.#rootElement = this._initRootElement();
this.#rootElement.classList.add('agent');

// TODO: reference import. dynamic class name
this.#containerElement = this._initContainerElement();
this.#containerElement.classList.add('agent-content');
this.setLayout(
options.layout != undefined ? options.layout : LayoutOptions.VERTICAL
);
}

/**
* Attaches the provided presentation as a child.
*
* @param {Presenation} presentation - The presentation to attach
* @returns {Presentation} - Returns the attached presentation
* @throws - Throws an exception if the provided presentation is already attached
*/
@demands(function(this: Presentation, presentation: Presentation['childType']){
return !this.children().includes(presentation);
})
attachChild(presentation: this['childType']): this['childType'] {
this.#children.push(presentation);

return presentation;
}

/**
* Detaches the provided presentation from the container
*
* @param {Presentation} presentation - The presentation to detach
* @returns {Presenation} - Returns the detached presentation
* @throws - Throws an exception if the provided presentation is not a child of this container
*/
@demands(function(this: Presentation, presentation: Presentation['childType']){
return this.children().includes(presentation);
})
detachChild(presentation: this['childType']): this['childType'] {
this.#children = this.#children.filter(child => child !== presentation);

return presentation;
}

/**
* Returns a list of the child presentations
* @returns {Presenation[]} -
*/
children(): this['childType'][] {
return this.#children.slice();
}

/**
* Captures all DOM Events and routes them to the appropriate method name
*
* @param {Event} e The raised event
*/
handleEvent(e: Event): void {
const name = `on${e.type[0].toUpperCase()}${e.type}`;
if(typeof (this as any)[name] == 'function') {
(this as any)[name](e);
}
if(this.#handlers.has(name)) {
this.#handlers.get(e.type)!(e);
}
}

setLayout(name: LayoutOptions): void {
const cls = this._rootElement.classList;
cls.forEach(cl => {
if (cl.startsWith('layout-')) {
cls.remove(cl);
}
});
cls.add(`layout-${name}`);
}

protected _initContainerElement(): this['containerType'] {
return this._rootElement;
}

protected _initRootElement(): this['rootElementType'] {
return document.createElement('div');
}

protected get _rootElement(): this['rootElementType'] {
return this.#rootElement;
}

protected get _containerElement(): this['containerType'] {
return this.#containerElement;
}
}
Loading

0 comments on commit 56a1806

Please sign in to comment.