worldorbit 3.0.5 → 3.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -138,7 +138,7 @@ For direct browser usage, use the browser bundle:
138
138
  import {
139
139
  createInteractiveViewer,
140
140
  loadWorldOrbitSource
141
- } from "https://unpkg.com/worldorbit@3.0.5/dist/unpkg/worldorbit.esm.js";
141
+ } from "https://unpkg.com/worldorbit@3.0.7/dist/unpkg/worldorbit.esm.js";
142
142
 
143
143
  const source = `
144
144
  schema 2.5
@@ -432,7 +432,7 @@ const scene = renderDocumentToScene(loaded.document, {
432
432
 
433
433
  ## Viewer Capabilities
434
434
 
435
- Viewer features in `v3.0.5` include:
435
+ Viewer features in `v3.0.7` include:
436
436
 
437
437
  * scene-based SVG rendering
438
438
  * renderer-neutral spatial scenes through `renderDocumentToSpatialScene(...)`
@@ -67,6 +67,7 @@ export function createWorldOrbitEmbedMarkup(payload, options = {}) {
67
67
  }
68
68
  export function mountWorldOrbitEmbeds(root = document, options = {}) {
69
69
  const viewers = new Map();
70
+ const cleanupCallbacks = [];
70
71
  const elements = [...root.querySelectorAll(EMBED_SELECTOR)];
71
72
  for (const element of elements) {
72
73
  const payload = deserializePayloadFromElement(element);
@@ -82,14 +83,16 @@ export function mountWorldOrbitEmbeds(root = document, options = {}) {
82
83
  const viewMode = options.viewer?.viewMode ??
83
84
  payload.options?.viewMode ??
84
85
  embedModeToViewMode(mode);
86
+ const measureViewport = () => resolveEmbedViewport(element, payload.scene, options);
85
87
  if (mode === "interactive-2d" || mode === "interactive-3d") {
86
88
  try {
89
+ const viewport = measureViewport();
87
90
  const viewer = createInteractiveViewer(element, {
88
91
  ...options.viewer,
89
92
  scene: payload.scene,
90
93
  spatialScene: payload.spatialScene,
91
- width: options.width ?? payload.scene.width,
92
- height: options.height ?? payload.scene.height,
94
+ width: viewport.width,
95
+ height: viewport.height,
93
96
  padding: options.padding ?? payload.scene.padding,
94
97
  preset,
95
98
  theme,
@@ -105,6 +108,13 @@ export function mountWorldOrbitEmbeds(root = document, options = {}) {
105
108
  viewer.setAtlasState(payload.options.atlasState);
106
109
  }
107
110
  viewers.set(element, viewer);
111
+ cleanupCallbacks.push(bindEmbedResize(element, () => {
112
+ const nextViewport = measureViewport();
113
+ viewer.setRenderOptions({
114
+ width: nextViewport.width,
115
+ height: nextViewport.height,
116
+ });
117
+ }));
108
118
  options.onMount?.(viewer, element);
109
119
  }
110
120
  catch (error) {
@@ -118,17 +128,22 @@ export function mountWorldOrbitEmbeds(root = document, options = {}) {
118
128
  }
119
129
  }
120
130
  else {
121
- element.innerHTML = renderSceneToSvg(payload.scene, {
122
- width: options.width ?? payload.scene.width,
123
- height: options.height ?? payload.scene.height,
124
- padding: options.padding ?? payload.scene.padding,
125
- preset,
126
- theme,
127
- layers,
128
- filter: initialFilter,
129
- selectedObjectId: initialSelectionObjectId ?? null,
130
- subtitle,
131
- });
131
+ const renderStaticEmbed = () => {
132
+ const viewport = measureViewport();
133
+ element.innerHTML = renderSceneToSvg(payload.scene, {
134
+ width: viewport.width,
135
+ height: viewport.height,
136
+ padding: options.padding ?? payload.scene.padding,
137
+ preset,
138
+ theme,
139
+ layers,
140
+ filter: initialFilter,
141
+ selectedObjectId: initialSelectionObjectId ?? null,
142
+ subtitle,
143
+ });
144
+ };
145
+ renderStaticEmbed();
146
+ cleanupCallbacks.push(bindEmbedResize(element, renderStaticEmbed));
132
147
  options.onMount?.(null, element);
133
148
  }
134
149
  element.dataset.worldorbitMounted = "true";
@@ -136,14 +151,72 @@ export function mountWorldOrbitEmbeds(root = document, options = {}) {
136
151
  return {
137
152
  viewers: [...viewers.values()],
138
153
  destroy() {
154
+ for (const cleanup of cleanupCallbacks) {
155
+ cleanup();
156
+ }
139
157
  for (const [element, viewer] of viewers.entries()) {
140
158
  viewer.destroy();
141
159
  element.removeAttribute("data-worldorbit-mounted");
142
160
  }
161
+ for (const element of elements) {
162
+ element.removeAttribute("data-worldorbit-mounted");
163
+ }
143
164
  viewers.clear();
144
165
  },
145
166
  };
146
167
  }
168
+ function resolveEmbedViewport(element, scene, options) {
169
+ const rect = element.getBoundingClientRect();
170
+ const width = sanitizeViewportDimension(options.width) ??
171
+ sanitizeViewportDimension(element.clientWidth) ??
172
+ sanitizeViewportDimension(rect.width) ??
173
+ scene.width;
174
+ const explicitHeight = sanitizeViewportDimension(options.height) ??
175
+ sanitizeViewportDimension(element.clientHeight) ??
176
+ sanitizeViewportDimension(rect.height);
177
+ const fallbackHeight = Math.max(Math.round(width * (scene.height / Math.max(scene.width, 1))), Math.min(scene.height, 240));
178
+ return {
179
+ width,
180
+ height: explicitHeight ?? fallbackHeight,
181
+ };
182
+ }
183
+ function sanitizeViewportDimension(value) {
184
+ return typeof value === "number" && Number.isFinite(value) && value > 0
185
+ ? Math.round(value)
186
+ : null;
187
+ }
188
+ function bindEmbedResize(element, callback) {
189
+ let lastWidth = -1;
190
+ let lastHeight = -1;
191
+ const run = () => {
192
+ const rect = element.getBoundingClientRect();
193
+ const nextWidth = Math.round(Math.max(element.clientWidth || rect.width, 0));
194
+ const nextHeight = Math.round(Math.max(element.clientHeight || rect.height, 0));
195
+ if (nextWidth === lastWidth && nextHeight === lastHeight) {
196
+ return;
197
+ }
198
+ lastWidth = nextWidth;
199
+ lastHeight = nextHeight;
200
+ callback();
201
+ };
202
+ run();
203
+ if (typeof ResizeObserver !== "undefined") {
204
+ const observer = new ResizeObserver(() => {
205
+ run();
206
+ });
207
+ observer.observe(element);
208
+ return () => {
209
+ observer.disconnect();
210
+ };
211
+ }
212
+ const handleWindowResize = () => {
213
+ run();
214
+ };
215
+ window.addEventListener("resize", handleWindowResize);
216
+ return () => {
217
+ window.removeEventListener("resize", handleWindowResize);
218
+ };
219
+ }
147
220
  function deserializePayloadFromElement(element) {
148
221
  const serialized = element.dataset.worldorbitPayload;
149
222
  if (!serialized) {