@a2f0/skyline 0.1.4 → 0.1.6

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/docs/package.md CHANGED
@@ -113,6 +113,11 @@ toolbar and study links so they do not overlap the host's navigation. Set
113
113
  loading silhouette, star controls, original comparison, and building details.
114
114
  The scene's control bar starts folded behind its star; set `controls: 'open'` to
115
115
  start it open, with no fold, from its first frame. Visitors can still fold it.
116
+ Open, the bar shows the camera hint and the OpenStreetMap credit for the streets
117
+ and footprints. Set `attribution: false` to leave the credit out. OpenStreetMap's
118
+ [licence](https://www.openstreetmap.org/copyright) requires crediting its contributors
119
+ wherever its data is shown, so do this only when the host page credits
120
+ © OpenStreetMap contributors itself.
116
121
  A small independent **show original** button remains available until the first
117
122
  3D frame succeeds, and reappears if the graphics context is lost. The SVG stays
118
123
  reachable even if WebGL, the scene document, or its bootstrap script cannot load.
@@ -10,6 +10,12 @@ export interface SkylineOptions {
10
10
  navigation?: boolean;
11
11
  /** Start the 3D scene's control bar open, or folded behind its star. Closed by default. */
12
12
  controls?: "open" | "closed";
13
+ /**
14
+ * Show the OpenStreetMap credit with the 3D scene's controls. Shown by default. OpenStreetMap's
15
+ * licence requires that credit wherever its data appears, so hide it only when the host page
16
+ * credits OpenStreetMap contributors itself.
17
+ */
18
+ attribution?: boolean;
13
19
  }
14
20
  export interface SkylineInstance {
15
21
  /** The viewer's frame; its styles are isolated from the host application. */
@@ -22,4 +28,4 @@ export interface SkylineInstance {
22
28
  * this module never touches the DOM; call it after mounting a browser component.
23
29
  * The container controls placement and size. Destroy it when that component leaves.
24
30
  */
25
- export declare function mountSkyline(container: HTMLElement, { assetsUrl, title, navigation, controls }: SkylineOptions): SkylineInstance;
31
+ export declare function mountSkyline(container: HTMLElement, { assetsUrl, title, navigation, controls, attribution }: SkylineOptions): SkylineInstance;
@@ -3,7 +3,7 @@
3
3
  * this module never touches the DOM; call it after mounting a browser component.
4
4
  * The container controls placement and size. Destroy it when that component leaves.
5
5
  */
6
- export function mountSkyline(container, { assetsUrl, title = "Interactive Chicago skyline", navigation = false, controls = "closed" }) {
6
+ export function mountSkyline(container, { assetsUrl, title = "Interactive Chicago skyline", navigation = false, controls = "closed", attribution = true }) {
7
7
  if (controls !== "open" && controls !== "closed")
8
8
  throw new TypeError('Skyline controls must be "open" or "closed".');
9
9
  const document = container.ownerDocument;
@@ -21,6 +21,8 @@ export function mountSkyline(container, { assetsUrl, title = "Interactive Chicag
21
21
  viewerUrl.searchParams.set("embed", "1");
22
22
  if (controls === "open")
23
23
  viewerUrl.searchParams.set("controls", "open");
24
+ if (!attribution)
25
+ viewerUrl.searchParams.set("attribution", "hidden");
24
26
  element.src = viewerUrl.href;
25
27
  element.allowFullscreen = true;
26
28
  element.style.cssText = "display:block;width:100%;height:100%;border:0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@a2f0/skyline",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Interactive Chicago skyline viewer and reusable Three.js building models",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -79,20 +79,23 @@
79
79
  </div>
80
80
  </footer>
81
81
  <script>
82
- // ?controls=open, on this page's address or on the viewer that frames it, starts the bar
83
- // open from its first frame, with no fold; skyline-3d.ts folds it from there.
82
+ // Settings from this page's address, then from the viewer that frames it, applied before
83
+ // the bar's first frame. ?controls=open starts the bar open, with no fold; skyline-3d.ts
84
+ // folds it from there. ?attribution=hidden leaves out the OpenStreetMap credit.
84
85
  (() => {
85
- let controls = new URLSearchParams(location.search).get("controls");
86
+ const settings = new URLSearchParams(location.search);
86
87
  try {
87
- if (!controls && parent !== window && parent.document.querySelector("#skyline-3d-scene")?.contentWindow === window) {
88
- controls = new URLSearchParams(parent.location.search).get("controls");
88
+ if (parent !== window && parent.document.querySelector("#skyline-3d-scene")?.contentWindow === window) {
89
+ for (const [key, value] of new URLSearchParams(parent.location.search)) if (!settings.has(key)) settings.set(key, value);
89
90
  }
90
91
  } catch { /* A scene embedded on another origin reads only its own address. */ }
91
- if (controls !== "open") return;
92
- const toggle = document.querySelector("#menu-toggle");
93
- toggle.setAttribute("aria-expanded", "true");
94
- toggle.title = "Hide the controls";
95
- document.querySelector(".control-bar").classList.add("open");
92
+ if (settings.get("controls") === "open") {
93
+ const toggle = document.querySelector("#menu-toggle");
94
+ toggle.setAttribute("aria-expanded", "true");
95
+ toggle.title = "Hide the controls";
96
+ document.querySelector(".control-bar").classList.add("open");
97
+ }
98
+ if (settings.get("attribution") === "hidden") document.querySelector(".attribution").hidden = true;
96
99
  })();
97
100
  </script>
98
101
  <div class="tooltip" id="tooltip" aria-hidden="true" hidden></div>
@@ -75,7 +75,8 @@ for (const button of celebrationButtons) {
75
75
  return message ? [`${message.lines.join(" ")}${message.adapted ? " (adapted tribute)" : ""} · ${model.building.userData["geography"]?.name ?? model.building.name}`] : [];
76
76
  });
77
77
  button.title = `${preset.label}: ${displays.join("; ")} · click to light all; click again to turn off`;
78
- button.disabled = displays.length === 0;
78
+ // A badge no mapped building can display is left out rather than greyed out.
79
+ button.hidden = displays.length === 0;
79
80
  button.addEventListener("click", () => {
80
81
  const next = button.getAttribute("aria-pressed") === "true" ? null : preset.id;
81
82
  const targets = next === null ? illuminated.filter((model) => model.illumination.active === preset.id) : illuminated;
@@ -74,7 +74,8 @@ export function createSkylineComparison(models, anchor) {
74
74
  const streetButton = document.querySelector("#streets");
75
75
  function onLayoutChange(name) {
76
76
  const geographic = name === "geographic";
77
- streetButton.disabled = !geographic;
77
+ // Only the geographic layout maps streets; elsewhere the toggle is left out.
78
+ streetButton.hidden = !geographic;
78
79
  document.querySelector("#layout-status").textContent = geographic
79
80
  ? "Geographic layout · mapped footprints and published heights, or heights read on the drawing where none is published, with detailed facades on all thirty-four buildings. Skyline view: the drawing’s camera, on the lakefront by the Adler Planetarium. Light grey outlines: mapped ground coverage. Grey lines: street centerlines. Grid: 100 m."
80
81
  : "Original layout · proportions and spacing fitted to the drawing. Use ground plan or height comparison, then toggle layouts to compare at the same camera scale.";
@@ -50,7 +50,7 @@
50
50
  <button type="button" id="reset">reset view</button>
51
51
  </div>
52
52
  <div class="button-group" role="group" aria-label="Model display">
53
- <button type="button" id="streets" aria-pressed="true" disabled>streets</button>
53
+ <button type="button" id="streets" aria-pressed="true" hidden>streets</button>
54
54
  <button type="button" id="wireframe" aria-pressed="false">wireframe</button>
55
55
  <button type="button" id="turntable" aria-pressed="false" aria-describedby="motion-status">turntable</button>
56
56
  </div>
@@ -470,7 +470,8 @@ export function createBuildingStudy({ models, defaultView = "quarter", views = {
470
470
  }
471
471
  function updateMotionPreference() {
472
472
  controls.enabled = !reducedMotion.matches;
473
- turntableButton.disabled = reducedMotion.matches;
473
+ // Under reduced motion the turntable cannot run, so its button goes rather than greying out.
474
+ turntableButton.hidden = reducedMotion.matches;
474
475
  if (reducedMotion.matches)
475
476
  setTurning(false);
476
477
  updateMotionStatus();