Skip to content

Commit

Permalink
Las flechas se actualizan cuando cambia el valor de las propiedades d…
Browse files Browse the repository at this point in the history
…el objeto
  • Loading branch information
JavierGelatti committed Dec 30, 2024
1 parent 762c73b commit b235de1
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 13 deletions.
34 changes: 30 additions & 4 deletions src/association.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,28 @@ import {Outliner} from "./outliner.ts";
import {Arrow, drawNewArrowToBox} from "./arrows.ts";
import {ObjectOutliner} from "./objectOutliner.ts";
import {boundingPageBoxOf} from "./dom.ts";
import {World} from "./world.ts";

export class Association {
private _property: Property;
private _ownerOutliner: ObjectOutliner;
private readonly _property: Property;
private readonly _ownerOutliner: ObjectOutliner;
private _valueOutliner: Outliner<unknown>;
private readonly _arrow: Arrow;
private readonly _world: World;

constructor(property: Property, ownerOutliner: ObjectOutliner, valueOutliner: Outliner<unknown>) {
constructor(property: Property, ownerOutliner: ObjectOutliner, valueOutliner: Outliner<unknown>, world: World) {
this._property = property;
this._ownerOutliner = ownerOutliner;
this._valueOutliner = valueOutliner;
this._world = world;
this._arrow = drawNewArrowToBox(this._arrowStartPosition(), this._arrowEndBox());
}

arrow() {
return this._arrow;
}

update() {
updatePosition() {
this._arrow.updateStart(this._arrowStartPosition());
this._arrow.attachEndToBox(this._arrowEndBox());
}
Expand All @@ -47,4 +50,27 @@ export class Association {
selector() {
return this._property.name();
}

update() {
const propertyValue = this._property.currentValue();

if (this._targetValueAlreadyIs(propertyValue)) return;

if (this._world.hasOutlinerFor(propertyValue)) {
this._redirectTo(this._world.openOutliner(propertyValue));
this.updatePosition();
} else {
this.remove();
}
}

private _redirectTo(newTarget: Outliner<unknown>) {
this._valueOutliner.removeAssociationEnd(this);
this._valueOutliner = newTarget;
this._valueOutliner.registerAssociationEnd(this);
}

private _targetValueAlreadyIs(propertyValue: unknown) {
return this._valueOutliner.inspectedValue() === propertyValue;
}
}
9 changes: 7 additions & 2 deletions src/objectOutliner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,12 @@ export class ObjectOutliner extends Outliner<InspectableObject> {
for (const [key, property] of this._properties.entries()) {
property.update();

if (!currentKeys.includes(key)) this._properties.delete(key);
if (currentKeys.includes(key)) {
this.associationFor(key)?.update();
} else {
this._properties.delete(key);
this.associationFor(key)?.remove();
}
}

newKeys.forEach((newPropertyName) => {
Expand All @@ -128,7 +133,7 @@ export class ObjectOutliner extends Outliner<InspectableObject> {
return !!this.associationFor(propertyToInspect.name());
}

associationFor(propertyName: Selector): Association | undefined {
associationFor(propertyName: Selector) {
return this._associationStarts.get(propertyName);
}

Expand Down
2 changes: 1 addition & 1 deletion src/outliner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export abstract class Outliner<V> {

private _updateAssociationsPositions() {
this._associations().forEach(association => {
association.update();
association.updatePosition();
});
}

Expand Down
8 changes: 6 additions & 2 deletions src/world.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export class World {
}

openOutliner(anObject: unknown, position: Position = point(0, 0)) {
if (this._outliners.has(anObject)) {
if (this.hasOutlinerFor(anObject)) {
const existingOutliner = this._outliners.get(anObject)!;
existingOutliner.shake();
return existingOutliner;
Expand Down Expand Up @@ -55,7 +55,7 @@ export class World {
currentPosition.plus(point(50, 0))
);

const association = new Association(propertyToInspect, ownerOutliner, valueOutliner);
const association = new Association(propertyToInspect, ownerOutliner, valueOutliner, this);
this._domElement.append(association.domElement());

ownerOutliner.registerAssociationStart(association);
Expand All @@ -69,4 +69,8 @@ export class World {
return outliner.associationFor(propertyName);
}
}

hasOutlinerFor(anObject: unknown) {
return this._outliners.has(anObject);
}
}
6 changes: 5 additions & 1 deletion tests/outlinerFromDomElement.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {fireEvent, within} from "@testing-library/dom";
import {vi} from "vitest";
import {positionOfDomElement} from "../src/dom.ts";
import {boundingPageBoxOf, positionOfDomElement} from "../src/dom.ts";
import {fireMousePointerEventOver} from "./dom_event_simulation.ts";
import {point, Position} from "../src/position.ts";

Expand Down Expand Up @@ -135,4 +135,8 @@ export class OutlinerFromDomElement {
fireMousePointerEventOver(this.header(), "pointerMove", point(1, 1).plus(positionDelta));
fireMousePointerEventOver(this.header(), "pointerUp", point(1, 1));
}

boundingBox() {
return boundingPageBoxOf(this._domElement);
}
}
69 changes: 66 additions & 3 deletions tests/outliners_in_dom.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {point, Position, sumOf} from "../src/position";

import "../styles.css";
import {InspectableObject} from "../src/objectOutliner";
import {asClientLocation, boundingPageBoxOf, getElementAt, positionOfDomElement} from "../src/dom.ts";
import {asClientLocation, getElementAt, positionOfDomElement} from "../src/dom.ts";
import {OutlinerFromDomElement} from "./outlinerFromDomElement.ts";
import {Selector} from "../src/property.ts";
import {svgDefinitions} from "../src/arrows.ts";
Expand Down Expand Up @@ -191,6 +191,69 @@ describe("The outliners in the world", () => {
expect(arrow.svgElement()).not.toBeInTheDocument();
expect(visibleArrowElements().length).toEqual(0);
});

test("when the value of an inspected property changes and there is no outliner for the new value, the arrow is removed", () => {
const inspectedObject = { x: 1, y: 2 };
const sourceOutliner = openOutlinerFor(inspectedObject);

sourceOutliner.inspectProperty("x");
sourceOutliner.doIt("this.x = 3");

const association = world().associationFor(inspectedObject, "x");
expect(association).not.toBeDefined();
expect(visibleArrowElements().length).toEqual(0);
});

test("when the value of an inspected property changes but there is an outliner for the new value, the arrow is updated", () => {
const inspectedObject = { x: 1, y: 2 };
const sourceOutliner = openOutlinerFor(inspectedObject);
const newValueOutliner = openOutlinerFor(3);

sourceOutliner.inspectProperty("x");
sourceOutliner.doIt("this.x = 3");

expect(world().associationFor(inspectedObject, "x")).toBeDefined();
expect(visibleArrowElements().length).toEqual(1);
expect(arrowForAssociation(inspectedObject, "x").end()).toEqual(newValueOutliner.boundingBox());
expect(newValueOutliner.domElement()).toHaveClass("shaking");
});

test("when an inspected property is updated to the same value, the arrow is maintained", () => {
const inspectedObject = { x: 1, y: 2 };
const sourceOutliner = openOutlinerFor(inspectedObject);

sourceOutliner.inspectProperty("x");
sourceOutliner.doIt("this.x = 1");

expect(world().associationFor(inspectedObject, "x")).toBeDefined();
expect(visibleArrowElements().length).toEqual(1);
expect(lastOutliner().domElement()).not.toHaveClass("shaking");
});

test("when an arrow is redirected, updates its position when the new destination is moved", () => {
const inspectedObject = { x: 1, y: 2 };
const sourceOutliner = openOutlinerFor(inspectedObject);
const newValueOutliner = openOutlinerFor(3);
sourceOutliner.inspectProperty("x");

sourceOutliner.doIt("this.x = 3");
newValueOutliner.move(point(5, 5));

expect(arrowForAssociation(inspectedObject, "x").end()).toEqual(newValueOutliner.boundingBox());
});

test("when an inspected property is removed, the arrow is removed (:. not pointed to undefined)", () => {
const inspectedObject = { x: 1, y: 2 };
const sourceOutliner = openOutlinerFor(inspectedObject);
sourceOutliner.inspectProperty("x");
openOutlinerFor(undefined);

sourceOutliner.doIt("delete this.x");

const association = world().associationFor(inspectedObject, "x");
expect(association).not.toBeDefined();
expect(visibleArrowElements().length).toEqual(0);
});
});
});

Expand Down Expand Up @@ -462,7 +525,7 @@ describe("The outliners in the world", () => {
expect(getElementAt(associationArrow.start()))
.toEqual(outlinerElement.buttonToInspectProperty("x"));
expect(associationArrow.end())
.toEqual(boundingPageBoxOf(lastOutliner().domElement()));
.toEqual(lastOutliner().boundingBox());
});

test("when the source outliner is moved, the arrow is updated", () => {
Expand All @@ -487,7 +550,7 @@ describe("The outliners in the world", () => {
targetOutliner.move(point(10, 20));

expect(associationArrow.end())
.toEqual(boundingPageBoxOf(targetOutliner.domElement()));
.toEqual(targetOutliner.boundingBox());
});

test("when the source outliner is moved after scrolling, the arrow is updated", () => {
Expand Down

0 comments on commit b235de1

Please sign in to comment.