diff --git a/demo/proximity-maps.html b/demo/proximity-maps.html
index a8df19f..4c48921 100644
--- a/demo/proximity-maps.html
+++ b/demo/proximity-maps.html
@@ -59,7 +59,7 @@
-
+
@@ -67,11 +67,11 @@
-
+
-
+
@@ -81,12 +81,6 @@
import { FolkWeather } from '../src/folk-weather.ts';
import { FolkCluster, FolkProximity } from '../src/folk-proximity.ts';
- FolkGeometry.define();
- FolkMap.define();
- FolkCluster.define();
- FolkProximity.define();
- FolkWeather.define();
-
class GeoWiki extends HTMLElement {
static tagName = 'geo-wiki';
@@ -150,6 +144,50 @@
}
}
+ FolkCluster.registerElement({
+ constructor: FolkMap,
+ events: {
+ recenter: (e) => ({
+ lat: e.target.coordinates.lat,
+ lng: e.target.coordinates.lng,
+ }),
+ },
+ onAdd: (element) => ({
+ lat: element.coordinates.lat,
+ lng: element.coordinates.lng,
+ }),
+ });
+
+ FolkCluster.registerElement({
+ constructor: FolkWeather,
+ onAdd: console.log,
+ onUpdate(element, data, changes) {
+ const lat = data.get('lat');
+ const lng = data.get('lng');
+
+ if (lat && lng) {
+ element.coordinates = [lat, lng];
+ }
+ },
+ });
+
+ FolkCluster.registerElement({
+ constructor: GeoWiki,
+ onUpdate(element, data, changes) {
+ const lat = data.get('lat');
+ const lng = data.get('lng');
+
+ if (lat && lng) {
+ element.coordinates = [lat, lng];
+ }
+ },
+ });
+
+ FolkGeometry.define();
+ FolkMap.define();
+ FolkCluster.define();
+ FolkProximity.define();
+ FolkWeather.define();
GeoWiki.define();
diff --git a/src/folk-proximity.ts b/src/folk-proximity.ts
index 6ddb767..7170686 100644
--- a/src/folk-proximity.ts
+++ b/src/folk-proximity.ts
@@ -2,6 +2,18 @@ import { collisionDetection } from './collision';
import { FolkHull } from './folk-hull';
import { FolkGeometry } from './canvas/fc-geometry.ts';
+interface ElementConstructor {
+ new (): E;
+}
+
+export interface ElementConfig {
+ constructor: ElementConstructor;
+ events?: Record Record>;
+ onAdd?(element: E): void | Record;
+ onUpdate?(element: E, data: ReadonlyMap, updatedValues: Set): void;
+ onRemove?(element: E): void;
+}
+
// TODO don't hard code this
const PROXIMITY = 50;
@@ -15,6 +27,12 @@ declare global {
export class FolkCluster extends FolkHull {
static tagName = 'folk-cluster';
+ static #config = new Map();
+
+ static registerElement(config: ElementConfig) {
+ this.#config.set(config.constructor, config);
+ }
+
#data = new Map();
isElementInCluster(element: FolkGeometry) {
@@ -28,18 +46,85 @@ export class FolkCluster extends FolkHull {
return false;
}
- addElements(...elements) {
+ addElements(...elements: FolkGeometry[]) {
this.sources = this.sourceElements
.concat(elements)
.map((el) => `#${el.id}`)
.join(', ');
+
+ let data = {};
+
+ for (const geometry of elements) {
+ const element = geometry.firstElementChild;
+
+ if (element === null) continue;
+
+ const config = this.#getConfig(element);
+
+ if (config) {
+ for (const event of Object.keys(config.events || {})) {
+ element.addEventListener(event, this.#handleEvent);
+ }
+
+ const newData = config.onAdd?.(element);
+ data = Object.assign(data, newData);
+ }
+ }
+
+ this.#handleUpdate(data);
}
- removeElement(element) {
+ #handleEvent = (event: Event) => {
+ const config = this.#getConfig(event.currentTarget as Element);
+
+ if (config) {
+ const data = config.events?.[event.type]?.(event);
+ if (data === undefined) return;
+ this.#handleUpdate(data);
+ }
+ };
+
+ #handleUpdate(data: Record) {
+ const keys = new Set(Object.keys(data));
+ for (const key of keys) {
+ this.#data.set(key, data[key]);
+ }
+
+ for (const geometry of this.sourceElements) {
+ const element = geometry.firstElementChild;
+
+ if (element === null) continue;
+
+ const config = this.#getConfig(element);
+
+ config?.onUpdate?.(element, this.#data, keys);
+ }
+ }
+
+ removeElement(geometry: FolkGeometry) {
this.sources = this.sourceElements
- .filter((el) => el !== element)
+ .filter((el) => el !== geometry)
.map((el) => `#${el.id}`)
.join(', ');
+
+ const element = geometry.firstElementChild;
+
+ if (element === null) return;
+
+ const config = this.#getConfig(element);
+
+ if (config) {
+ for (const event of Object.keys(config.events || {})) {
+ element.removeEventListener(event, this.#handleEvent);
+ }
+
+ config.onRemove?.(element);
+ }
+ }
+
+ #getConfig(element: Element) {
+ const config = (this.constructor as typeof FolkCluster).#config;
+ return config.get(element.constructor as ElementConstructor);
}
}
@@ -58,15 +143,6 @@ export class FolkProximity extends HTMLElement {
this.addEventListener('move', this.#handleProximity);
this.addEventListener('resize', this.#handleProximity);
- // document.addEventListener('recenter', (e) => {
- // proximityMap.get(e.target.parentElement)?.forEach((el) => {
- // const content = el.firstElementChild;
- // if (content instanceof GeoWiki) {
- // const { lat, lng } = e.target.coordinates;
- // content.coordinates = [lat, lng];
- // }
- // });
- // });
}
#handleProximity = (e) => {
diff --git a/src/folk-set.ts b/src/folk-set.ts
index 389312a..e18607c 100644
--- a/src/folk-set.ts
+++ b/src/folk-set.ts
@@ -8,6 +8,8 @@ declare global {
}
}
+const defaultRect = DOMRectReadOnly.fromRect();
+
export class FolkSet extends HTMLElement {
static tagName = 'folk-set';
@@ -60,6 +62,7 @@ export class FolkSet extends HTMLElement {
this.unobserveSources(elementsToUnobserve);
for (const el of elementsToObserve) {
+ this.#sourcesMap.set(el, defaultRect);
clientRectObserver.observe(el, this.#sourcesCallback);
}