Added item selection and a WIP version of the edition form
This commit is contained in:
parent
8047bf2c87
commit
3120058c36
|
@ -124,19 +124,57 @@ function createVisualConsole(
|
|||
visualConsole = new VisualConsole(container, props, items);
|
||||
// VC Item clicked.
|
||||
visualConsole.onItemClick(function(e) {
|
||||
// Override the link to another VC if it isn't on remote console.
|
||||
if (
|
||||
e.data &&
|
||||
e.data.linkedLayoutId != null &&
|
||||
e.data.linkedLayoutId > 0 &&
|
||||
e.data.link != null &&
|
||||
e.data.link.length > 0 &&
|
||||
(e.data.linkedLayoutAgentId == null || e.data.linkedLayoutAgentId === 0)
|
||||
var data = e.item.props || {};
|
||||
var meta = e.item.meta || {};
|
||||
|
||||
if (meta.editMode) {
|
||||
// Item selection.
|
||||
if (meta.isSelected) {
|
||||
visualConsole.unselectItem(data.id);
|
||||
} else {
|
||||
// Unselect the rest of the elements if the
|
||||
visualConsole.selectItem(data.id, !e.nativeEvent.metaKey);
|
||||
}
|
||||
} else if (
|
||||
!meta.editMode &&
|
||||
data.linkedLayoutId != null &&
|
||||
data.linkedLayoutId > 0 &&
|
||||
data.link != null &&
|
||||
data.link.length > 0 &&
|
||||
(data.linkedLayoutAgentId == null || data.linkedLayoutAgentId === 0)
|
||||
) {
|
||||
// Override the link to another VC if it isn't on remote console.
|
||||
// Stop the current link behavior.
|
||||
e.nativeEvent.preventDefault();
|
||||
// Fetch and update the old VC with the new.
|
||||
updateVisualConsole(e.data.linkedLayoutId, updateInterval);
|
||||
updateVisualConsole(data.linkedLayoutId, updateInterval);
|
||||
}
|
||||
});
|
||||
// VC Item double clicked.
|
||||
visualConsole.onItemDblClick(function(e) {
|
||||
e.nativeEvent.preventDefault();
|
||||
e.nativeEvent.stopPropagation();
|
||||
|
||||
var item = e.item || {};
|
||||
var props = item.props || {};
|
||||
var meta = item.meta || {};
|
||||
|
||||
if (meta.editMode) {
|
||||
// Item selection.
|
||||
visualConsole.selectItem(props.id, true);
|
||||
|
||||
var formContainer = item.getFormContainer();
|
||||
var formElement = formContainer.getFormElement();
|
||||
formContainer.onSubmit(function(e) {
|
||||
// TODO: Send the update.
|
||||
console.log("Form submit", e.data);
|
||||
$(formElement).dialog("close");
|
||||
});
|
||||
|
||||
$(formElement).dialog({
|
||||
title: formContainer.title
|
||||
});
|
||||
// TODO: Add submit and reset button.
|
||||
}
|
||||
});
|
||||
// VC Item moved.
|
||||
|
|
|
@ -0,0 +1,205 @@
|
|||
import TypedEvent, { Listener, Disposable } from "./lib/TypedEvent";
|
||||
import { AnyObject } from "./lib/types";
|
||||
|
||||
// TODO: Document
|
||||
export abstract class InputGroup<Data extends {} = {}> {
|
||||
private _name: string = "";
|
||||
private _element?: HTMLElement;
|
||||
public initialData: Data;
|
||||
protected currentData: Partial<Data> = {};
|
||||
|
||||
public constructor(name: string, initialData: Data) {
|
||||
this.name = name;
|
||||
this.initialData = initialData;
|
||||
}
|
||||
|
||||
public set name(name: string) {
|
||||
if (name.length === 0) throw new RangeError("empty name");
|
||||
this._name = name;
|
||||
}
|
||||
|
||||
public get name(): string {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
public get data(): Partial<Data> {
|
||||
return { ...this.currentData };
|
||||
}
|
||||
|
||||
public get element(): HTMLElement {
|
||||
if (this._element == null) {
|
||||
const element = document.createElement("div");
|
||||
element.className = `input-group input-group-${this.name}`;
|
||||
|
||||
const content = this.createContent();
|
||||
|
||||
if (content instanceof Array) {
|
||||
content.forEach(element.appendChild);
|
||||
} else {
|
||||
element.appendChild(content);
|
||||
}
|
||||
|
||||
this._element = element;
|
||||
}
|
||||
|
||||
return this._element;
|
||||
}
|
||||
|
||||
public reset(): void {
|
||||
this.currentData = {};
|
||||
}
|
||||
|
||||
protected updateData(data: Partial<Data>): void {
|
||||
this.currentData = {
|
||||
...this.currentData,
|
||||
...data
|
||||
};
|
||||
// TODO: Update item.
|
||||
}
|
||||
|
||||
protected abstract createContent(): HTMLElement | HTMLElement[];
|
||||
|
||||
// public abstract get isValid(): boolean;
|
||||
}
|
||||
|
||||
export interface SubmitFormEvent {
|
||||
nativeEvent: Event;
|
||||
data: AnyObject;
|
||||
}
|
||||
|
||||
// TODO: Document
|
||||
export class FormContainer {
|
||||
public readonly title: string;
|
||||
private inputGroupsByName: { [name: string]: InputGroup } = {};
|
||||
private enabledInputGroupNames: string[] = [];
|
||||
// Event manager for submit events.
|
||||
private readonly submitEventManager = new TypedEvent<SubmitFormEvent>();
|
||||
|
||||
public constructor(
|
||||
title: string,
|
||||
inputGroups: InputGroup[] = [],
|
||||
enabledInputGroups: string[] = []
|
||||
) {
|
||||
this.title = title;
|
||||
|
||||
if (inputGroups.length > 0) {
|
||||
this.inputGroupsByName = inputGroups.reduce((prevVal, inputGroup) => {
|
||||
prevVal[inputGroup.name] = inputGroup;
|
||||
return prevVal;
|
||||
}, this.inputGroupsByName);
|
||||
}
|
||||
|
||||
if (enabledInputGroups.length > 0) {
|
||||
this.enabledInputGroupNames = [
|
||||
...this.enabledInputGroupNames,
|
||||
...enabledInputGroups.filter(
|
||||
name => this.inputGroupsByName[name] != null
|
||||
)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
public getInputGroup(inputGroupName: string): InputGroup | null {
|
||||
return this.inputGroupsByName[inputGroupName] || null;
|
||||
}
|
||||
|
||||
public addInputGroup(
|
||||
inputGroup: InputGroup,
|
||||
index: number | null = null
|
||||
): FormContainer {
|
||||
this.inputGroupsByName[inputGroup.name] = inputGroup;
|
||||
|
||||
// Remove the current stored name if exist.
|
||||
this.enabledInputGroupNames = this.enabledInputGroupNames.filter(
|
||||
name => name === inputGroup.name
|
||||
);
|
||||
|
||||
if (index !== null) {
|
||||
if (index <= 0) {
|
||||
this.enabledInputGroupNames = [
|
||||
inputGroup.name,
|
||||
...this.enabledInputGroupNames
|
||||
];
|
||||
} else if (index >= this.enabledInputGroupNames.length) {
|
||||
this.enabledInputGroupNames = [
|
||||
...this.enabledInputGroupNames,
|
||||
inputGroup.name
|
||||
];
|
||||
} else {
|
||||
this.enabledInputGroupNames = [
|
||||
// part of the array before the specified index
|
||||
...this.enabledInputGroupNames.slice(0, index),
|
||||
// inserted item
|
||||
inputGroup.name,
|
||||
// part of the array after the specified index
|
||||
...this.enabledInputGroupNames.slice(index)
|
||||
];
|
||||
}
|
||||
} else {
|
||||
this.enabledInputGroupNames = [
|
||||
...this.enabledInputGroupNames,
|
||||
inputGroup.name
|
||||
];
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public removeInputGroup(inputGroupName: string): FormContainer {
|
||||
// delete this.inputGroupsByName[inputGroupName];
|
||||
// Remove the current stored name.
|
||||
this.enabledInputGroupNames = this.enabledInputGroupNames.filter(
|
||||
name => name === inputGroupName
|
||||
);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public getFormElement(): HTMLFormElement {
|
||||
const form = document.createElement("form");
|
||||
form.addEventListener("submit", e => {
|
||||
e.preventDefault();
|
||||
this.submitEventManager.emit({
|
||||
nativeEvent: e,
|
||||
data: this.enabledInputGroupNames.reduce((data, name) => {
|
||||
if (this.inputGroupsByName[name]) {
|
||||
data = {
|
||||
...data,
|
||||
...this.inputGroupsByName[name].data
|
||||
};
|
||||
}
|
||||
return data;
|
||||
}, {})
|
||||
});
|
||||
});
|
||||
|
||||
this.enabledInputGroupNames.forEach(name => {
|
||||
if (this.inputGroupsByName[name]) {
|
||||
form.appendChild(this.inputGroupsByName[name].element);
|
||||
}
|
||||
});
|
||||
|
||||
return form;
|
||||
}
|
||||
|
||||
public reset(): void {
|
||||
this.enabledInputGroupNames.forEach(name => {
|
||||
if (this.inputGroupsByName[name]) {
|
||||
this.inputGroupsByName[name].reset();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// public get isValid(): boolean {
|
||||
// for (let i = 0; i < this.enabledInputGroupNames.length; i++) {
|
||||
// const inputGroup = this.inputGroupsByName[this.enabledInputGroupNames[i]];
|
||||
// if (inputGroup && !inputGroup.isValid) return false;
|
||||
// }
|
||||
|
||||
// return true;
|
||||
// }
|
||||
|
||||
public onSubmit(listener: Listener<SubmitFormEvent>): Disposable {
|
||||
return this.submitEventManager.on(listener);
|
||||
}
|
||||
}
|
|
@ -16,9 +16,11 @@ import {
|
|||
humanTime,
|
||||
addMovementListener,
|
||||
debounce,
|
||||
addResizementListener
|
||||
addResizementListener,
|
||||
t
|
||||
} from "./lib";
|
||||
import TypedEvent, { Listener, Disposable } from "./lib/TypedEvent";
|
||||
import { FormContainer, InputGroup } from "./Form";
|
||||
|
||||
// Enum: https://www.typescriptlang.org/docs/handbook/enums.html.
|
||||
export const enum ItemType {
|
||||
|
@ -58,10 +60,8 @@ export interface ItemProps extends Position, Size {
|
|||
aclGroupId: number | null;
|
||||
}
|
||||
|
||||
// FIXME: Fix type compatibility.
|
||||
export interface ItemClickEvent<Props extends ItemProps> {
|
||||
// data: Props;
|
||||
data: AnyObject;
|
||||
export interface ItemClickEvent {
|
||||
item: VisualConsoleItem<ItemProps>;
|
||||
nativeEvent: Event;
|
||||
}
|
||||
|
||||
|
@ -83,6 +83,41 @@ export interface ItemResizedEvent {
|
|||
newSize: Size;
|
||||
}
|
||||
|
||||
// TODO: Document
|
||||
class PositionInputGroup extends InputGroup<ItemProps> {
|
||||
protected createContent(): HTMLElement | HTMLElement[] {
|
||||
const positionLabel = document.createElement("label");
|
||||
positionLabel.textContent = t("Position");
|
||||
|
||||
const positionInputX = document.createElement("input");
|
||||
positionInputX.type = "number";
|
||||
positionInputX.min = "0";
|
||||
positionInputX.required = true;
|
||||
positionInputX.value = `${this.currentData.x || this.initialData.x || 0}`;
|
||||
positionInputX.addEventListener("change", e =>
|
||||
this.updateData({
|
||||
x: parseIntOr((e.target as HTMLInputElement).value, 0)
|
||||
})
|
||||
);
|
||||
|
||||
const positionInputY = document.createElement("input");
|
||||
positionInputY.type = "number";
|
||||
positionInputY.min = "0";
|
||||
positionInputY.required = true;
|
||||
positionInputY.value = `${this.currentData.y || this.initialData.y || 0}`;
|
||||
positionInputY.addEventListener("change", e =>
|
||||
this.updateData({
|
||||
y: parseIntOr((e.target as HTMLInputElement).value, 0)
|
||||
})
|
||||
);
|
||||
|
||||
positionLabel.appendChild(positionInputX);
|
||||
positionLabel.appendChild(positionInputY);
|
||||
|
||||
return positionLabel;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a valid enum value from a raw label positi9on value.
|
||||
* @param labelPosition Raw value.
|
||||
|
@ -147,7 +182,9 @@ abstract class VisualConsoleItem<Props extends ItemProps> {
|
|||
// Reference to the DOM element which will contain the view of the item which extends this class.
|
||||
protected childElementRef: HTMLElement = document.createElement("div");
|
||||
// Event manager for click events.
|
||||
private readonly clickEventManager = new TypedEvent<ItemClickEvent<Props>>();
|
||||
private readonly clickEventManager = new TypedEvent<ItemClickEvent>();
|
||||
// Event manager for double click events.
|
||||
private readonly dblClickEventManager = new TypedEvent<ItemClickEvent>();
|
||||
// Event manager for moved events.
|
||||
private readonly movedEventManager = new TypedEvent<ItemMovedEvent>();
|
||||
// Event manager for resized events.
|
||||
|
@ -164,6 +201,10 @@ abstract class VisualConsoleItem<Props extends ItemProps> {
|
|||
private debouncedMovementSave = debounce(
|
||||
500, // ms.
|
||||
(x: Position["x"], y: Position["y"]) => {
|
||||
// Update the metadata information.
|
||||
// Don't use the .meta property cause we don't need DOM updates.
|
||||
this._metadata.isBeingMoved = false;
|
||||
|
||||
const prevPosition = {
|
||||
x: this.props.x,
|
||||
y: this.props.y
|
||||
|
@ -197,6 +238,9 @@ abstract class VisualConsoleItem<Props extends ItemProps> {
|
|||
this.removeMovement = addMovementListener(
|
||||
element,
|
||||
(x: Position["x"], y: Position["y"]) => {
|
||||
// Update the metadata information.
|
||||
// Don't use the .meta property cause we don't need DOM updates.
|
||||
this._metadata.isBeingMoved = true;
|
||||
// Move the DOM element.
|
||||
this.moveElement(x, y);
|
||||
// Run the save function.
|
||||
|
@ -219,6 +263,10 @@ abstract class VisualConsoleItem<Props extends ItemProps> {
|
|||
private debouncedResizementSave = debounce(
|
||||
500, // ms.
|
||||
(width: Size["width"], height: Size["height"]) => {
|
||||
// Update the metadata information.
|
||||
// Don't use the .meta property cause we don't need DOM updates.
|
||||
this._metadata.isBeingResized = false;
|
||||
|
||||
const prevSize = {
|
||||
width: this.props.width,
|
||||
height: this.props.height
|
||||
|
@ -252,6 +300,10 @@ abstract class VisualConsoleItem<Props extends ItemProps> {
|
|||
this.removeResizement = addResizementListener(
|
||||
element,
|
||||
(width: Size["width"], height: Size["height"]) => {
|
||||
// Update the metadata information.
|
||||
// Don't use the .meta property cause we don't need DOM updates.
|
||||
this._metadata.isBeingResized = true;
|
||||
|
||||
// The label it's outside the item's size, so we need
|
||||
// to get rid of its size to get the real size of the
|
||||
// item's content.
|
||||
|
@ -353,13 +405,27 @@ abstract class VisualConsoleItem<Props extends ItemProps> {
|
|||
box.style.zIndex = this.props.isOnTop ? "2" : "1";
|
||||
box.style.left = `${this.props.x}px`;
|
||||
box.style.top = `${this.props.y}px`;
|
||||
// Init the click listener.
|
||||
|
||||
// Init the click listeners.
|
||||
box.addEventListener("dblclick", e => {
|
||||
if (!this.meta.isBeingMoved && !this.meta.isBeingResized) {
|
||||
this.dblClickEventManager.emit({
|
||||
item: this,
|
||||
nativeEvent: e
|
||||
});
|
||||
}
|
||||
});
|
||||
box.addEventListener("click", e => {
|
||||
if (this.meta.editMode) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
} else {
|
||||
this.clickEventManager.emit({ data: this.props, nativeEvent: e });
|
||||
}
|
||||
|
||||
if (!this.meta.isBeingMoved && !this.meta.isBeingResized) {
|
||||
this.clickEventManager.emit({
|
||||
item: this,
|
||||
nativeEvent: e
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -377,6 +443,9 @@ abstract class VisualConsoleItem<Props extends ItemProps> {
|
|||
if (this.meta.isUpdating) {
|
||||
box.classList.add("is-updating");
|
||||
}
|
||||
if (this.meta.isSelected) {
|
||||
box.classList.add("is-selected");
|
||||
}
|
||||
|
||||
return box;
|
||||
}
|
||||
|
@ -643,6 +712,13 @@ abstract class VisualConsoleItem<Props extends ItemProps> {
|
|||
this.elementRef.classList.remove("is-updating");
|
||||
}
|
||||
}
|
||||
if (!prevMeta || prevMeta.isSelected !== this.meta.isSelected) {
|
||||
if (this.meta.isSelected) {
|
||||
this.elementRef.classList.add("is-selected");
|
||||
} else {
|
||||
this.elementRef.classList.remove("is-selected");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -801,11 +877,20 @@ abstract class VisualConsoleItem<Props extends ItemProps> {
|
|||
};
|
||||
}
|
||||
|
||||
// TODO: Document
|
||||
public getFormContainer(): FormContainer {
|
||||
return new FormContainer(
|
||||
t("Item"),
|
||||
[new PositionInputGroup("position", this.props)],
|
||||
["position"]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* To add an event handler to the click of the linked visual console elements.
|
||||
* @param listener Function which is going to be executed when a linked console is clicked.
|
||||
*/
|
||||
public onClick(listener: Listener<ItemClickEvent<Props>>): Disposable {
|
||||
public onClick(listener: Listener<ItemClickEvent>): Disposable {
|
||||
/*
|
||||
* The '.on' function returns a function which will clean the event
|
||||
* listener when executed. We store all the 'dispose' functions to
|
||||
|
@ -817,6 +902,22 @@ abstract class VisualConsoleItem<Props extends ItemProps> {
|
|||
return disposable;
|
||||
}
|
||||
|
||||
/**
|
||||
* To add an event handler to the double click of the linked visual console elements.
|
||||
* @param listener Function which is going to be executed when a linked console is double clicked.
|
||||
*/
|
||||
public onDblClick(listener: Listener<ItemClickEvent>): Disposable {
|
||||
/*
|
||||
* The '.on' function returns a function which will clean the event
|
||||
* listener when executed. We store all the 'dispose' functions to
|
||||
* call them when the item should be cleared.
|
||||
*/
|
||||
const disposable = this.dblClickEventManager.on(listener);
|
||||
this.disposables.push(disposable);
|
||||
|
||||
return disposable;
|
||||
}
|
||||
|
||||
/**
|
||||
* To add an event handler to the movement of visual console elements.
|
||||
* @param listener Function which is going to be executed when a linked console is moved.
|
||||
|
|
|
@ -203,9 +203,9 @@ export default class VisualConsole {
|
|||
[key: string]: Line;
|
||||
} = {};
|
||||
// Event manager for click events.
|
||||
private readonly clickEventManager = new TypedEvent<
|
||||
ItemClickEvent<ItemProps>
|
||||
>();
|
||||
private readonly clickEventManager = new TypedEvent<ItemClickEvent>();
|
||||
// Event manager for double click events.
|
||||
private readonly dblClickEventManager = new TypedEvent<ItemClickEvent>();
|
||||
// Event manager for move events.
|
||||
private readonly movedEventManager = new TypedEvent<ItemMovedEvent>();
|
||||
// Event manager for resize events.
|
||||
|
@ -217,11 +217,20 @@ export default class VisualConsole {
|
|||
* React to a click on an element.
|
||||
* @param e Event object.
|
||||
*/
|
||||
private handleElementClick: (e: ItemClickEvent<ItemProps>) => void = e => {
|
||||
private handleElementClick: (e: ItemClickEvent) => void = e => {
|
||||
this.clickEventManager.emit(e);
|
||||
// console.log(`Clicked element #${e.data.id}`, e);
|
||||
};
|
||||
|
||||
/**
|
||||
* React to a double click on an element.
|
||||
* @param e Event object.
|
||||
*/
|
||||
private handleElementDblClick: (e: ItemClickEvent) => void = e => {
|
||||
this.dblClickEventManager.emit(e);
|
||||
// console.log(`Double clicked element #${e.data.id}`, e);
|
||||
};
|
||||
|
||||
/**
|
||||
* React to a movement on an element.
|
||||
* @param e Event object.
|
||||
|
@ -308,6 +317,7 @@ export default class VisualConsole {
|
|||
this.elementIds.push(itemInstance.props.id);
|
||||
// Item event handlers.
|
||||
itemInstance.onClick(this.handleElementClick);
|
||||
itemInstance.onDblClick(this.handleElementDblClick);
|
||||
itemInstance.onMoved(this.handleElementMovement);
|
||||
itemInstance.onResized(this.handleElementResizement);
|
||||
itemInstance.onRemove(this.handleElementRemove);
|
||||
|
@ -685,9 +695,7 @@ export default class VisualConsole {
|
|||
* Add an event handler to the click of the linked visual console elements.
|
||||
* @param listener Function which is going to be executed when a linked console is clicked.
|
||||
*/
|
||||
public onItemClick(
|
||||
listener: Listener<ItemClickEvent<ItemProps>>
|
||||
): Disposable {
|
||||
public onItemClick(listener: Listener<ItemClickEvent>): Disposable {
|
||||
/*
|
||||
* The '.on' function returns a function which will clean the event
|
||||
* listener when executed. We store all the 'dispose' functions to
|
||||
|
@ -699,6 +707,22 @@ export default class VisualConsole {
|
|||
return disposable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an event handler to the double click of the linked visual console elements.
|
||||
* @param listener Function which is going to be executed when a linked console is double clicked.
|
||||
*/
|
||||
public onItemDblClick(listener: Listener<ItemClickEvent>): Disposable {
|
||||
/*
|
||||
* The '.on' function returns a function which will clean the event
|
||||
* listener when executed. We store all the 'dispose' functions to
|
||||
* call them when the item should be cleared.
|
||||
*/
|
||||
const disposable = this.dblClickEventManager.on(listener);
|
||||
this.disposables.push(disposable);
|
||||
|
||||
return disposable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an event handler to the movement of the visual console elements.
|
||||
* @param listener Function which is going to be executed when a linked console is moved.
|
||||
|
@ -750,4 +774,69 @@ export default class VisualConsole {
|
|||
});
|
||||
this.containerRef.classList.remove("is-editing");
|
||||
}
|
||||
|
||||
/**
|
||||
* Select an item.
|
||||
* @param itemId Item Id.
|
||||
* @param unique To remove the selection of other items or not.
|
||||
*/
|
||||
public selectItem(itemId: number, unique: boolean = false): void {
|
||||
if (unique) {
|
||||
this.elementIds.forEach(currentItemId => {
|
||||
const meta = this.elementsById[currentItemId].meta;
|
||||
|
||||
if (currentItemId !== itemId && meta.isSelected) {
|
||||
this.elementsById[currentItemId].meta = {
|
||||
...meta,
|
||||
isSelected: false
|
||||
};
|
||||
} else if (currentItemId === itemId && !meta.isSelected) {
|
||||
this.elementsById[currentItemId].meta = {
|
||||
...meta,
|
||||
isSelected: true
|
||||
};
|
||||
}
|
||||
});
|
||||
} else if (this.elementsById[itemId]) {
|
||||
this.elementsById[itemId].meta = {
|
||||
...this.elementsById[itemId].meta,
|
||||
isSelected: true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unselect an item.
|
||||
* @param itemId Item Id.
|
||||
*/
|
||||
public unselectItem(itemId: number): void {
|
||||
if (this.elementsById[itemId]) {
|
||||
const meta = this.elementsById[itemId].meta;
|
||||
|
||||
if (meta.isSelected) {
|
||||
this.elementsById[itemId].meta = {
|
||||
...meta,
|
||||
isSelected: false
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unselect all items.
|
||||
*/
|
||||
public unselectItems(): void {
|
||||
this.elementIds.forEach(itemId => {
|
||||
if (this.elementsById[itemId]) {
|
||||
const meta = this.elementsById[itemId].meta;
|
||||
|
||||
if (meta.isSelected) {
|
||||
this.elementsById[itemId].meta = {
|
||||
...meta,
|
||||
isSelected: false
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,12 +7,17 @@
|
|||
|
||||
import "./main.css"; // CSS import.
|
||||
import VisualConsole from "./VisualConsole";
|
||||
import * as Form from "./Form";
|
||||
import AsyncTaskManager from "./lib/AsyncTaskManager";
|
||||
|
||||
// Export the VisualConsole class to the global object.
|
||||
// eslint-disable-next-line
|
||||
(window as any).VisualConsole = VisualConsole;
|
||||
|
||||
// Export the VisualConsole's Form classes to the global object.
|
||||
// eslint-disable-next-line
|
||||
(window as any).VisualConsole.Form = Form;
|
||||
|
||||
// Export the AsyncTaskManager class to the global object.
|
||||
// eslint-disable-next-line
|
||||
(window as any).AsyncTaskManager = AsyncTaskManager;
|
||||
|
|
|
@ -284,7 +284,10 @@ export function itemMetaDecoder(data: UnknownObject): ItemMeta | never {
|
|||
editMode: parseBoolean(data.editMode),
|
||||
isFromCache: parseBoolean(data.isFromCache),
|
||||
isFetching: false,
|
||||
isUpdating: false
|
||||
isUpdating: false,
|
||||
isBeingMoved: false,
|
||||
isBeingResized: false,
|
||||
isSelected: false
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -428,14 +431,17 @@ function getOffset(el: HTMLElement | null) {
|
|||
*
|
||||
* @param element Element to move.
|
||||
* @param onMoved Function to execute when the element moves.
|
||||
* @param altContainer Alternative element to contain the moved element.
|
||||
*
|
||||
* @return A function which will clean the event handlers when executed.
|
||||
*/
|
||||
export function addMovementListener(
|
||||
element: HTMLElement,
|
||||
onMoved: (x: Position["x"], y: Position["y"]) => void
|
||||
onMoved: (x: Position["x"], y: Position["y"]) => void,
|
||||
altContainer?: HTMLElement
|
||||
): Function {
|
||||
const container = element.parentElement as HTMLElement;
|
||||
const container = altContainer || (element.parentElement as HTMLElement);
|
||||
|
||||
// Store the initial draggable state.
|
||||
const isDraggable = element.draggable;
|
||||
// Init the coordinates.
|
||||
|
@ -747,3 +753,7 @@ export function addResizementListener(
|
|||
handleEnd();
|
||||
};
|
||||
}
|
||||
|
||||
export function t(text: string): string {
|
||||
return text;
|
||||
}
|
||||
|
|
|
@ -56,5 +56,8 @@ export interface ItemMeta {
|
|||
isFromCache: boolean;
|
||||
isFetching: boolean;
|
||||
isUpdating: boolean;
|
||||
isSelected: boolean;
|
||||
isBeingMoved: boolean;
|
||||
isBeingResized: boolean;
|
||||
editMode: boolean;
|
||||
}
|
||||
|
|
|
@ -32,3 +32,10 @@
|
|||
background: url(./resize-handle.svg);
|
||||
cursor: se-resize;
|
||||
}
|
||||
|
||||
.visual-console-item.is-editing.is-selected {
|
||||
border: 2px dashed #2b2b2b;
|
||||
transform: translateX(-2px) translateY(-2px);
|
||||
cursor: move;
|
||||
user-select: none;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue