working proximity

This commit is contained in:
“chrisshank” 2024-11-30 21:14:53 -08:00
parent 17450f64b9
commit aa87c39a28
3 changed files with 138 additions and 21 deletions

View File

@ -59,7 +59,7 @@
<body> <body>
<folk-proximity> <folk-proximity>
<fc-geometry id="g1" x="25" y="100" width="400" height="200"> <fc-geometry id="g1" x="25" y="100" width="400" height="200">
<folk-map coordinates="52.09, 5.12" zoom="13"></folk-map> <folk-map coordinates="40.76845173617708, -73.97983074188234" zoom="15"></folk-map>
</fc-geometry> </fc-geometry>
<fc-geometry id="g2" x="50" y="600" width="400" height="250"> <fc-geometry id="g2" x="50" y="600" width="400" height="250">
@ -67,11 +67,11 @@
</fc-geometry> </fc-geometry>
<fc-geometry id="g3" x="500" y="500" width="500" height="300"> <fc-geometry id="g3" x="500" y="500" width="500" height="300">
<geo-wiki coordinates="51.50404120260676, -0.14007568359375003"></geo-wiki> <geo-wiki></geo-wiki>
</fc-geometry> </fc-geometry>
<fc-geometry id="g4" x="450" y="100"> <fc-geometry id="g4" x="450" y="100">
<folk-weather coordinates="51.50404120260676, -0.14007568359375003"></folk-weather> <folk-weather></folk-weather>
</fc-geometry> </fc-geometry>
</folk-proximity> </folk-proximity>
@ -81,12 +81,6 @@
import { FolkWeather } from '../src/folk-weather.ts'; import { FolkWeather } from '../src/folk-weather.ts';
import { FolkCluster, FolkProximity } from '../src/folk-proximity.ts'; import { FolkCluster, FolkProximity } from '../src/folk-proximity.ts';
FolkGeometry.define();
FolkMap.define();
FolkCluster.define();
FolkProximity.define();
FolkWeather.define();
class GeoWiki extends HTMLElement { class GeoWiki extends HTMLElement {
static tagName = 'geo-wiki'; 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(); GeoWiki.define();
</script> </script>
</body> </body>

View File

@ -2,6 +2,18 @@ import { collisionDetection } from './collision';
import { FolkHull } from './folk-hull'; import { FolkHull } from './folk-hull';
import { FolkGeometry } from './canvas/fc-geometry.ts'; import { FolkGeometry } from './canvas/fc-geometry.ts';
interface ElementConstructor<E extends Element = Element> {
new (): E;
}
export interface ElementConfig<E extends Element = Element> {
constructor: ElementConstructor<E>;
events?: Record<string, (event: Event) => Record<string, any>>;
onAdd?(element: E): void | Record<string, any>;
onUpdate?(element: E, data: ReadonlyMap<string, any>, updatedValues: Set<string>): void;
onRemove?(element: E): void;
}
// TODO don't hard code this // TODO don't hard code this
const PROXIMITY = 50; const PROXIMITY = 50;
@ -15,6 +27,12 @@ declare global {
export class FolkCluster extends FolkHull { export class FolkCluster extends FolkHull {
static tagName = 'folk-cluster'; static tagName = 'folk-cluster';
static #config = new Map<ElementConstructor, ElementConfig>();
static registerElement<E extends Element>(config: ElementConfig<E>) {
this.#config.set(config.constructor, config);
}
#data = new Map(); #data = new Map();
isElementInCluster(element: FolkGeometry) { isElementInCluster(element: FolkGeometry) {
@ -28,18 +46,85 @@ export class FolkCluster extends FolkHull {
return false; return false;
} }
addElements(...elements) { addElements(...elements: FolkGeometry[]) {
this.sources = this.sourceElements this.sources = this.sourceElements
.concat(elements) .concat(elements)
.map((el) => `#${el.id}`) .map((el) => `#${el.id}`)
.join(', '); .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<string, any>) {
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 this.sources = this.sourceElements
.filter((el) => el !== element) .filter((el) => el !== geometry)
.map((el) => `#${el.id}`) .map((el) => `#${el.id}`)
.join(', '); .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('move', this.#handleProximity);
this.addEventListener('resize', 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) => { #handleProximity = (e) => {

View File

@ -8,6 +8,8 @@ declare global {
} }
} }
const defaultRect = DOMRectReadOnly.fromRect();
export class FolkSet extends HTMLElement { export class FolkSet extends HTMLElement {
static tagName = 'folk-set'; static tagName = 'folk-set';
@ -60,6 +62,7 @@ export class FolkSet extends HTMLElement {
this.unobserveSources(elementsToUnobserve); this.unobserveSources(elementsToUnobserve);
for (const el of elementsToObserve) { for (const el of elementsToObserve) {
this.#sourcesMap.set(el, defaultRect);
clientRectObserver.observe(el, this.#sourcesCallback); clientRectObserver.observe(el, this.#sourcesCallback);
} }