Initial commit
This commit is contained in:
583
Frontend/pages/home/ts/ActionEditor.ts
Normal file
583
Frontend/pages/home/ts/ActionEditor.ts
Normal file
@@ -0,0 +1,583 @@
|
||||
var ActionEditor: ActionEditor = {
|
||||
elements: {
|
||||
up: {
|
||||
container: document.querySelector('.event.up'),
|
||||
selector: document.querySelector('.event.up').querySelector('.actionselector'),
|
||||
actions: document.querySelector('.event.up').querySelector('.actions')
|
||||
},
|
||||
down: {
|
||||
container: document.querySelector('.event.down'),
|
||||
selector: document.querySelector('.event.down').querySelector('.actionselector'),
|
||||
actions: document.querySelector('.event.down').querySelector('.actions')
|
||||
},
|
||||
latch: {
|
||||
container: document.querySelector('.event.toggle'),
|
||||
selector: document.querySelector('.event.toggle').querySelector('.tlatch').querySelector('.actionselector'),
|
||||
actions: document.querySelector('.event.toggle').querySelector('.tlatch').querySelector('.actions')
|
||||
},
|
||||
unlatch: {
|
||||
container: document.querySelector('.event.toggle'),
|
||||
selector: document
|
||||
.querySelector('.event.toggle')
|
||||
.querySelector('.tunlatch')
|
||||
.querySelector('.actionselector'),
|
||||
actions: document.querySelector('.event.toggle').querySelector('.tunlatch').querySelector('.actions')
|
||||
}
|
||||
},
|
||||
|
||||
openEditors: { up: {}, down: {}, latch: {}, unlatch: {} },
|
||||
|
||||
listeners() {
|
||||
if (ActionSelector) {
|
||||
ActionSelector.register(ActionEditor.elements.up.selector, (integrationID: string, actionID: string) =>
|
||||
handleNewAction(integrationID, actionID, 'up')
|
||||
);
|
||||
ActionSelector.register(ActionEditor.elements.down.selector, (integrationID: string, actionID: string) =>
|
||||
handleNewAction(integrationID, actionID, 'down')
|
||||
);
|
||||
ActionSelector.register(ActionEditor.elements.latch.selector, (integrationID: string, actionID: string) =>
|
||||
handleNewAction(integrationID, actionID, 'latch')
|
||||
);
|
||||
ActionSelector.register(ActionEditor.elements.unlatch.selector, (integrationID: string, actionID: string) =>
|
||||
handleNewAction(integrationID, actionID, 'unlatch')
|
||||
);
|
||||
|
||||
function handleNewAction(
|
||||
integrationID: string,
|
||||
actionID: string,
|
||||
type: 'up' | 'down' | 'latch' | 'unlatch'
|
||||
) {
|
||||
socket.emit(
|
||||
'actioneditor',
|
||||
'create',
|
||||
PageHandler.currentPageID,
|
||||
Editor.currentX,
|
||||
Editor.currentY,
|
||||
type,
|
||||
integrationID,
|
||||
actionID,
|
||||
(actionInstance: Page_Key_Action) => {
|
||||
ActionEditor.openAction(actionInstance, type);
|
||||
}
|
||||
);
|
||||
}
|
||||
} else setTimeout(ActionEditor.listeners, 100);
|
||||
},
|
||||
|
||||
open(actions: Page_Key_ActionTypes, x: string, y: string, key: Page_Key, pageID: string) {
|
||||
for (var type in actions) {
|
||||
ActionEditor.elements[type].actions.innerHTML = '';
|
||||
for (var actionInstanceID in actions[type]) {
|
||||
ActionEditor.openAction(actions[type][actionInstanceID], type);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
openAction(actionInstance: Page_Key_Action, type: string) {
|
||||
var actionInstanceID = actionInstance.actionInstanceID;
|
||||
var container = <HTMLDivElement>ce('div', 'actioncontainer', { actionInstanceID });
|
||||
|
||||
var header = ce('div', 'header');
|
||||
header.appendChild(
|
||||
ce('div', 'integration', null, ActionSelector.maps.integrationNames[actionInstance.integrationID])
|
||||
);
|
||||
header.appendChild(ce('div', 'action', null, ActionSelector.maps.actionNames[actionInstance.actionID]));
|
||||
|
||||
var buttons = ce('div', 'buttons');
|
||||
var actionLogs = ce(
|
||||
'div',
|
||||
[
|
||||
'btn',
|
||||
'logs'
|
||||
],
|
||||
null,
|
||||
'Logs'
|
||||
);
|
||||
actionLogs.onclick = () => UndeckedNotification('Not implented yet', 'error');
|
||||
var actionRemove = ce(
|
||||
'div',
|
||||
[
|
||||
'btn',
|
||||
'remove'
|
||||
],
|
||||
null,
|
||||
'Remove'
|
||||
);
|
||||
actionRemove.onclick = () => {
|
||||
if (container.parentElement) container.parentElement.removeChild(container);
|
||||
if (
|
||||
ActionEditor.openEditors[type] != undefined &&
|
||||
ActionEditor.openEditors[type][actionInstanceID] != undefined
|
||||
) {
|
||||
ActionEditor.openEditors[type][actionInstanceID].destroy();
|
||||
}
|
||||
socket.emit(
|
||||
'actioneditor',
|
||||
'remove',
|
||||
PageHandler.currentPageID,
|
||||
Editor.currentX,
|
||||
Editor.currentY,
|
||||
actionInstanceID,
|
||||
type
|
||||
);
|
||||
Editor.registerChange();
|
||||
};
|
||||
buttons.appendChild(actionLogs);
|
||||
buttons.appendChild(actionRemove);
|
||||
header.appendChild(buttons);
|
||||
|
||||
container.appendChild(header);
|
||||
|
||||
var fields = <HTMLDivElement>ce('div', 'fields');
|
||||
container.appendChild(fields);
|
||||
|
||||
ActionEditor.elements[type].actions.appendChild(container);
|
||||
|
||||
ActionEditor.openEditors[type][actionInstanceID] = new Action(
|
||||
{
|
||||
integrationID: actionInstance.integrationID,
|
||||
actionID: actionInstance.actionID,
|
||||
actionType: <any>type,
|
||||
keyX: Editor.currentX,
|
||||
keyY: Editor.currentY,
|
||||
actionInstanceID,
|
||||
pageID: PageHandler.currentPageID
|
||||
},
|
||||
fields
|
||||
);
|
||||
},
|
||||
|
||||
close() {
|
||||
for (var type in ActionEditor.openEditors) {
|
||||
for (var actionInstanceID in ActionEditor.openEditors[type]) {
|
||||
ActionEditor.openEditors[type][actionInstanceID].destroy();
|
||||
}
|
||||
}
|
||||
|
||||
ActionEditor.elements.up.actions.innerHTML = '';
|
||||
ActionEditor.elements.down.actions.innerHTML = '';
|
||||
}
|
||||
};
|
||||
ActionEditor.listeners();
|
||||
|
||||
var Action = class Action {
|
||||
settings: Action_Settings;
|
||||
container: HTMLDivElement;
|
||||
|
||||
actionEditorID: string;
|
||||
|
||||
lastFields: EditorAPI_Field[];
|
||||
|
||||
constructor(settings: Action_Settings, container: HTMLDivElement) {
|
||||
this.settings = settings;
|
||||
this.container = container;
|
||||
|
||||
socket.emit(
|
||||
'actioneditor',
|
||||
'start',
|
||||
this.settings,
|
||||
(error: string, actionEditorID: string, ready: () => void) => {
|
||||
if (error) return UndeckedNotification(error, 'error', 5000);
|
||||
this.actionEditorID = actionEditorID;
|
||||
this.listeners();
|
||||
this.emit('ready');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
emit(query: string, ...args: any[]) {
|
||||
socket.emit('actioneditor', 'instance', this.actionEditorID, query, ...args);
|
||||
}
|
||||
|
||||
listeners() {
|
||||
socket.on(`AE_${this.actionEditorID}`, (query: string, ...args: any[]) => {
|
||||
switch (query) {
|
||||
case 'fields':
|
||||
var fields: EditorAPI_Field[] = args[0];
|
||||
this.render(fields);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
destroy() {
|
||||
if (this.container && this.container.parentElement) this.container.parentElement.removeChild(this.container);
|
||||
this.emit('close');
|
||||
}
|
||||
|
||||
render(fields: EditorAPI_Field[]) {
|
||||
var valueNameMap = {};
|
||||
|
||||
var updateMulti = (selected: string[], input: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement) => {
|
||||
var text: string =
|
||||
selected != undefined && selected.length > 0
|
||||
? selected
|
||||
.map((value) => {
|
||||
return valueNameMap[value];
|
||||
})
|
||||
.join(', ')
|
||||
: 'None';
|
||||
|
||||
input.innerHTML = '';
|
||||
input.appendChild(ce('option', 'mutli', { value: selected.join(',') }, text));
|
||||
};
|
||||
|
||||
var getMultiValues = (dropdowninner: HTMLDivElement): string[] => {
|
||||
var values: string[] = [];
|
||||
dropdowninner.querySelectorAll('.option').forEach((option: HTMLDivElement) => {
|
||||
var checkbox: HTMLInputElement = option.querySelector('input');
|
||||
var optionID = option.getAttribute('optionID');
|
||||
|
||||
if (checkbox.checked) values.push(optionID);
|
||||
});
|
||||
return values;
|
||||
};
|
||||
|
||||
fields.forEach((field) => {
|
||||
var fieldcontainer: HTMLDivElement = this.container.querySelector(`.field_${field.id}`);
|
||||
if (fieldcontainer == null) {
|
||||
fieldcontainer = <HTMLDivElement>ce('div', [
|
||||
'field',
|
||||
`field_${field.id}`
|
||||
]);
|
||||
this.container.appendChild(fieldcontainer);
|
||||
}
|
||||
|
||||
var label: HTMLDivElement = fieldcontainer.querySelector('.fieldlabel');
|
||||
if (label == null) {
|
||||
label = <HTMLDivElement>ce('div', 'fieldlabel', null, `${field.name}${field.required ? ' *' : ''}`);
|
||||
fieldcontainer.appendChild(label);
|
||||
} else label.innerText = `${field.name}${field.required ? ' *' : ''}`;
|
||||
|
||||
var input: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement = fieldcontainer.querySelector(
|
||||
'.input'
|
||||
);
|
||||
|
||||
if (input == null) {
|
||||
switch (field.type) {
|
||||
case 'number':
|
||||
case 'text':
|
||||
case 'color':
|
||||
input = <HTMLInputElement>ce('input', 'input', { type: field.type, value: field.value });
|
||||
fieldcontainer.appendChild(input);
|
||||
break;
|
||||
case 'select':
|
||||
case 'connection':
|
||||
if (field.multi != undefined && field.multi == true) {
|
||||
var multiSelect = ce('div', [
|
||||
'multiselect'
|
||||
]);
|
||||
input = <HTMLInputElement>ce('select', [
|
||||
'input',
|
||||
'multiinput'
|
||||
]);
|
||||
|
||||
input.onmouseup = (e: MouseEvent) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
dropdown.style.display = 'block';
|
||||
input.classList.add('open');
|
||||
|
||||
var handleWindowClick = (e: MouseEvent) => {
|
||||
if (e.target) {
|
||||
var target = <HTMLElement>e.target;
|
||||
|
||||
if (
|
||||
!target.classList.contains('msdp') &&
|
||||
!target.classList.contains('multiinput')
|
||||
) {
|
||||
window.removeEventListener('click', handleWindowClick);
|
||||
dropdown.style.display = 'none';
|
||||
input.classList.remove('open');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('click', handleWindowClick);
|
||||
};
|
||||
|
||||
input.onmousedown = (e: MouseEvent) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
multiSelect.appendChild(input);
|
||||
|
||||
var dropdown = ce('div', [
|
||||
'dropdown',
|
||||
'msdp'
|
||||
]);
|
||||
var dropdowninner = <HTMLDivElement>ce('div', [
|
||||
'inner',
|
||||
'msdp'
|
||||
]);
|
||||
|
||||
dropdown.appendChild(dropdowninner);
|
||||
multiSelect.appendChild(dropdown);
|
||||
|
||||
if (field.values != undefined && field.values.length > 0)
|
||||
for (let i = 0; i < field.values.length; i++) {
|
||||
((value) => {
|
||||
valueNameMap[value.id] = field.values[i].text;
|
||||
|
||||
var item = ce(
|
||||
'div',
|
||||
[
|
||||
'option',
|
||||
'msdp'
|
||||
],
|
||||
{ optionID: value.id }
|
||||
);
|
||||
var checkbox = <HTMLInputElement>ce('input', 'msdp', {
|
||||
type: 'checkbox'
|
||||
});
|
||||
|
||||
checkbox.checked = field.value != undefined && field.value.includes(value.id);
|
||||
|
||||
item.onclick = () => {
|
||||
checkbox.checked = !checkbox.checked;
|
||||
updateMulti(getMultiValues(dropdowninner), input);
|
||||
this.emit('fields', this.export());
|
||||
};
|
||||
|
||||
item.appendChild(checkbox);
|
||||
item.appendChild(
|
||||
ce(
|
||||
'div',
|
||||
[
|
||||
'text',
|
||||
'msdp'
|
||||
],
|
||||
null,
|
||||
value.text
|
||||
)
|
||||
);
|
||||
dropdowninner.appendChild(item);
|
||||
})(field.values[i]);
|
||||
}
|
||||
|
||||
updateMulti(field.value, input);
|
||||
|
||||
fieldcontainer.appendChild(multiSelect);
|
||||
} else {
|
||||
input = <HTMLInputElement>ce('select', 'input');
|
||||
if (field.values != undefined && field.values.length > 0)
|
||||
for (let i = 0; i < field.values.length; i++) {
|
||||
var option = ce(
|
||||
'option',
|
||||
null,
|
||||
{ value: field.values[i].id },
|
||||
field.values[i].text
|
||||
);
|
||||
if (field.value == field.values[i].id) option.setAttribute('selected', '');
|
||||
input.appendChild(option);
|
||||
}
|
||||
|
||||
fieldcontainer.appendChild(input);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (input) input.oninput = () => this.emit('fields', this.export());
|
||||
} else {
|
||||
input.value = field.value;
|
||||
|
||||
if (field.type == 'select' || field.type == 'connection') {
|
||||
var parsedOptionValues = [];
|
||||
if (field.multi != undefined && field.multi == true) {
|
||||
var dropdown_inner = <HTMLDivElement>input.parentElement.querySelector('.inner');
|
||||
|
||||
if (field.values != undefined && field.values.length > 0)
|
||||
for (let i = 0; i < field.values.length; i++) {
|
||||
var selectValue = field.values[i];
|
||||
var existingOption: HTMLDivElement = dropdown_inner.querySelector(
|
||||
`.option[optionid="${selectValue.id}"]`
|
||||
);
|
||||
|
||||
parsedOptionValues.push(selectValue.id);
|
||||
|
||||
if (existingOption) {
|
||||
var text: HTMLDivElement = existingOption.querySelector('.text');
|
||||
text.innerText = selectValue.text;
|
||||
} else {
|
||||
((value) => {
|
||||
valueNameMap[value.id] = field.values[i].text;
|
||||
|
||||
var item = ce(
|
||||
'div',
|
||||
[
|
||||
'option',
|
||||
'msdp'
|
||||
],
|
||||
{ optionID: value.id }
|
||||
);
|
||||
var checkbox = <HTMLInputElement>ce('input', 'msdp', {
|
||||
type: 'checkbox'
|
||||
});
|
||||
|
||||
checkbox.checked = field.value != undefined && field.value.includes(value.id);
|
||||
|
||||
item.onclick = () => {
|
||||
checkbox.checked = !checkbox.checked;
|
||||
updateMulti(getMultiValues(dropdown_inner), input);
|
||||
this.emit('fields', this.export());
|
||||
};
|
||||
|
||||
item.appendChild(checkbox);
|
||||
item.appendChild(
|
||||
ce(
|
||||
'div',
|
||||
[
|
||||
'text',
|
||||
'msdp'
|
||||
],
|
||||
null,
|
||||
value.text
|
||||
)
|
||||
);
|
||||
dropdown_inner.appendChild(item);
|
||||
})(selectValue);
|
||||
}
|
||||
}
|
||||
else dropdown_inner.innerHTML = '';
|
||||
|
||||
dropdown_inner.querySelectorAll('.option').forEach((option: HTMLOptionElement) => {
|
||||
var optionValue = option.getAttribute('optionID');
|
||||
var checkbox: HTMLInputElement = option.querySelector('input');
|
||||
|
||||
if (!parsedOptionValues.includes(optionValue)) option.parentElement.removeChild(option);
|
||||
else checkbox.checked = field.value.includes(optionValue);
|
||||
});
|
||||
} else {
|
||||
if (field.values != undefined && field.values.length > 0)
|
||||
for (let i = 0; i < field.values.length; i++) {
|
||||
var selectValue = field.values[i];
|
||||
var existing: HTMLOptionElement = input.querySelector(
|
||||
`option[value="${selectValue.id}"]`
|
||||
);
|
||||
|
||||
if (existing) existing.innerText = selectValue.text;
|
||||
else {
|
||||
existing = <HTMLOptionElement>ce(
|
||||
'option',
|
||||
null,
|
||||
{ value: selectValue.id },
|
||||
selectValue.text
|
||||
);
|
||||
input.appendChild(existing);
|
||||
}
|
||||
parsedOptionValues.push(selectValue.id);
|
||||
}
|
||||
else input.innerHTML = '';
|
||||
|
||||
input.querySelectorAll('option').forEach((option: HTMLOptionElement) => {
|
||||
var optionValue = option.getAttribute('value');
|
||||
option.removeAttribute('selected');
|
||||
|
||||
if (!parsedOptionValues.includes(optionValue)) option.parentElement.removeChild(option);
|
||||
else if (field.value == option) option.setAttribute('selected', '');
|
||||
});
|
||||
}
|
||||
|
||||
// if (field.value != undefined && input.value.length > 0) input.value = field.value;
|
||||
}
|
||||
}
|
||||
});
|
||||
this.lastFields = fields;
|
||||
}
|
||||
|
||||
export(): EditorAPI_Field[] {
|
||||
var copyOfLast = JSON.parse(JSON.stringify(this.lastFields));
|
||||
for (let i = 0; i < copyOfLast.length; i++) {
|
||||
var field = copyOfLast[i];
|
||||
var fieldcontainer: HTMLDivElement = this.container.querySelector(`.field_${field.id}`);
|
||||
if (fieldcontainer) {
|
||||
var input: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement = fieldcontainer.querySelector(
|
||||
'.input'
|
||||
);
|
||||
if (input) {
|
||||
if (!input.classList.contains('multiinput')) copyOfLast[i].value = input.value;
|
||||
else copyOfLast[i].value = input.value.split(',');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return copyOfLast;
|
||||
}
|
||||
};
|
||||
|
||||
interface Action_Settings {
|
||||
integrationID: string;
|
||||
actionID: string;
|
||||
actionInstanceID: string;
|
||||
pageID: string;
|
||||
actionType: 'up' | 'down';
|
||||
keyX: number | string;
|
||||
keyY: number | string;
|
||||
}
|
||||
|
||||
interface ActionEditor {
|
||||
elements: {
|
||||
up: {
|
||||
container: HTMLDivElement;
|
||||
selector: HTMLInputElement;
|
||||
actions: HTMLDivElement;
|
||||
};
|
||||
down: {
|
||||
container: HTMLDivElement;
|
||||
selector: HTMLInputElement;
|
||||
actions: HTMLDivElement;
|
||||
};
|
||||
latch: {
|
||||
container: HTMLDivElement;
|
||||
selector: HTMLInputElement;
|
||||
actions: HTMLDivElement;
|
||||
};
|
||||
unlatch: {
|
||||
container: HTMLDivElement;
|
||||
selector: HTMLInputElement;
|
||||
actions: HTMLDivElement;
|
||||
};
|
||||
};
|
||||
|
||||
openEditors: {
|
||||
up: { [actionInstanceID: string]: typeof Action };
|
||||
down: { [actionInstanceID: string]: typeof Action };
|
||||
latch: { [actionInstanceID: string]: typeof Action };
|
||||
unlatch: { [actionInstanceID: string]: typeof Action };
|
||||
};
|
||||
|
||||
listeners: () => void;
|
||||
|
||||
open: (actions: Page_Key_ActionTypes, x: string, y: string, key: Page_Key, pageID: string) => void;
|
||||
openAction: (actionInstance: Page_Key_Action, type: string) => void;
|
||||
close: () => void;
|
||||
}
|
||||
|
||||
interface Page_Key_ActionTypes {
|
||||
up: Page_Key_Actions;
|
||||
down: Page_Key_Actions;
|
||||
}
|
||||
interface Page_Key_Actions {
|
||||
[actionInstanceID: string]: Page_Key_Action;
|
||||
}
|
||||
|
||||
interface Page_Key_Action {
|
||||
integrationID: string;
|
||||
actionID: string;
|
||||
actionInstanceID: string;
|
||||
properties: { [property: string]: any };
|
||||
logs: { timestamp: number; type: 'error' | 'info' | 'warning'; text: string }[];
|
||||
}
|
||||
|
||||
interface EditorAPI_Field {
|
||||
id: string;
|
||||
name: string;
|
||||
type: 'text' | 'number' | 'select' | 'connection' | 'color';
|
||||
value: any;
|
||||
values?: { id: string; text: string }[];
|
||||
required?: boolean;
|
||||
multi?: boolean;
|
||||
}
|
||||
Reference in New Issue
Block a user