mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-14 23:32:20 +00:00
feat(scenes): render the data-pack attribution cards from a store
The data-pack presentations keep the Cesium resources, object URLs and pick targets and present each pack's attribution card, with its media player, to the gev-data-packs store; the DataPacks view teleports one panel per set of presentations into the viewer's container with the same markup and inline styles, and stops a removed card's player as it goes.
This commit is contained in:
1 parent
59e3aaa271
commit
e0abf74308
8 files changed
+290
-69
No files matched your search
@@ -1,55 +1,56 @@
|
||||
import * as Cesium from 'cesium';
|
||||
import { renderPackGeometry } from './geometry.js';
|
||||
import { t } from '../../i18n/index.js';
|
||||
import { useDataPacksStore } from '../../ui/stores/dataPacks.js';
|
||||
import { applicationPinia } from '../../ui/vueRuntime.js';
|
||||
|
||||
function caption(viewer, pack) {
|
||||
const card = document.createElement('section');
|
||||
card.dataset.directorPack = pack.id;
|
||||
Object.assign(card.style, {
|
||||
color: '#e3faff',
|
||||
background: '#08141eed',
|
||||
border: '1px solid #2491a8',
|
||||
padding: '8px',
|
||||
margin: '4px',
|
||||
maxWidth: '260px',
|
||||
font: '12px sans-serif',
|
||||
});
|
||||
const title = document.createElement('div');
|
||||
title.textContent = `${pack.id} · ${pack.attribution.text} · ${pack.attribution.license}`;
|
||||
card.append(title);
|
||||
if (pack.attribution.url) {
|
||||
const link = document.createElement('a');
|
||||
link.textContent = t('Source');
|
||||
link.href = pack.attribution.url;
|
||||
link.target = '_blank';
|
||||
link.rel = 'noopener noreferrer';
|
||||
link.style.color = '#6eeaff';
|
||||
card.append(link);
|
||||
}
|
||||
return card;
|
||||
}
|
||||
const xyz = (p) => Cesium.Cartesian3.fromDegrees(...p);
|
||||
|
||||
/** Create rendering adapters; data acquisition and authored placement stay independent. */
|
||||
export function createPackPresentations(viewer, targets = new Map()) {
|
||||
let panel;
|
||||
// Key every panel and card on the page, so a new one never reuses an old
|
||||
// one's DOM.
|
||||
let panelKeys = 0,
|
||||
cardKeys = 0;
|
||||
|
||||
/**
|
||||
* Create rendering adapters; data acquisition and authored placement stay
|
||||
* independent. Each pack's attribution card is presented to the data packs
|
||||
* store, which its view renders inside the viewer's container.
|
||||
*/
|
||||
export function createPackPresentations(
|
||||
viewer,
|
||||
targets = new Map(),
|
||||
{ store = useDataPacksStore(applicationPinia()) } = {},
|
||||
) {
|
||||
const owner = {};
|
||||
let key,
|
||||
cards = [];
|
||||
function present() {
|
||||
store.publish(
|
||||
owner,
|
||||
cards.length ? { key, container: viewer.container, cards } : null,
|
||||
);
|
||||
}
|
||||
function addCard(pack) {
|
||||
if (!panel) {
|
||||
panel = document.createElement('div');
|
||||
panel.dataset.directorPacks = '';
|
||||
Object.assign(panel.style, {
|
||||
position: 'absolute',
|
||||
right: '10px',
|
||||
bottom: '70px',
|
||||
maxHeight: '40vh',
|
||||
overflowY: 'auto',
|
||||
zIndex: '30',
|
||||
});
|
||||
viewer.container.append(panel);
|
||||
}
|
||||
const card = caption(viewer, pack);
|
||||
panel.append(card);
|
||||
return card;
|
||||
if (!cards.length) key = ++panelKeys;
|
||||
const card = {
|
||||
key: ++cardKeys,
|
||||
id: pack.id,
|
||||
title: `${pack.id} · ${pack.attribution.text} · ${pack.attribution.license}`,
|
||||
url: pack.attribution.url || null,
|
||||
media: null,
|
||||
};
|
||||
cards = [...cards, card];
|
||||
present();
|
||||
return card.key;
|
||||
}
|
||||
function updateCard(key, patch) {
|
||||
cards = cards.map((card) =>
|
||||
card.key === key ? { ...card, ...patch } : card,
|
||||
);
|
||||
present();
|
||||
}
|
||||
function removeCard(key) {
|
||||
cards = cards.filter((card) => card.key !== key);
|
||||
present();
|
||||
}
|
||||
function resource(pack) {
|
||||
const entities = [],
|
||||
@@ -78,7 +79,12 @@ export function createPackPresentations(viewer, targets = new Map()) {
|
||||
viewer.scene.requestRender();
|
||||
},
|
||||
card() {
|
||||
return (card ||= addCard(pack));
|
||||
card ||= addCard(pack);
|
||||
},
|
||||
/** @param {{kind: 'video'|'audio', src: string}} media */
|
||||
media(media) {
|
||||
card ||= addCard(pack);
|
||||
updateCard(card, { media });
|
||||
},
|
||||
url(blob) {
|
||||
const url = URL.createObjectURL(blob);
|
||||
@@ -89,16 +95,8 @@ export function createPackPresentations(viewer, targets = new Map()) {
|
||||
if (destroyed) return;
|
||||
destroyed = true;
|
||||
for (const key of featureKeys.splice(0)) targets.delete(key);
|
||||
for (const media of card?.querySelectorAll('video,audio') || []) {
|
||||
media.pause();
|
||||
media.removeAttribute('src');
|
||||
media.load();
|
||||
}
|
||||
card?.remove();
|
||||
if (panel && !panel.childElementCount) {
|
||||
panel.remove();
|
||||
panel = null;
|
||||
}
|
||||
// The view stops a removed card's player as it goes.
|
||||
if (card) removeCard(card);
|
||||
for (const entity of entities.splice(0)) viewer.entities.remove(entity);
|
||||
for (const primitive of primitives.splice(0))
|
||||
viewer.scene.primitives.remove(primitive);
|
||||
@@ -212,14 +210,10 @@ export function createPackPresentations(viewer, targets = new Map()) {
|
||||
pixelOffset: new Cesium.Cartesian2(0, -24),
|
||||
},
|
||||
});
|
||||
const media = document.createElement(
|
||||
asset.mimeType.startsWith('video/') ? 'video' : 'audio',
|
||||
);
|
||||
media.controls = true;
|
||||
media.preload = 'none';
|
||||
media.style.width = '240px';
|
||||
media.src = handle.url(new Blob([asset.bytes], { type: asset.mimeType }));
|
||||
handle.card().append(media);
|
||||
handle.media({
|
||||
kind: asset.mimeType.startsWith('video/') ? 'video' : 'audio',
|
||||
src: handle.url(new Blob([asset.bytes], { type: asset.mimeType })),
|
||||
});
|
||||
}),
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user