@fusion.dev/helix-map 0.113.0 → 0.114.0

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.
@@ -33,9 +33,13 @@ export interface MapDriver {
33
33
  stop(): void;
34
34
  };
35
35
  /**
36
- * Calls `done` once the renderer has drawn its view with its tiles, and
37
- * returns what stops the waiting. What takes a snapshot drawn on the server
38
- * away; without it, the snapshot stays under the renderer's drawing.
36
+ * Calls `done` once the renderer has drawn its view whole, with every tile
37
+ * it needs and the ground where there is terrain, and returns what stops the
38
+ * waiting. Leave the frame's own shapes out, since a route that animates is
39
+ * redrawn every frame and would never count as drawn. What takes a snapshot
40
+ * drawn on the server away and sends the frame's `helix-map-ready`; without
41
+ * it, the snapshot stays under the renderer's drawing and the frame is never
42
+ * `:state(ready)`.
39
43
  */
40
44
  ready?(done: () => void): () => void;
41
45
  /**
@@ -726,23 +726,23 @@ export const connectMap = (frame, driver, options = {}) => {
726
726
  snapshot.style.display = 'none';
727
727
  };
728
728
  const snapshots = [...frame.querySelectorAll(':scope > [data-helix-map-snapshot]')];
729
- const stopWaiting = snapshots.length > 0
730
- ? driver.ready?.(() => {
731
- const motion = motionOf(frame, 'step');
732
- for (const snapshot of snapshots) {
733
- if (!motion || typeof snapshot.animate !== 'function') {
734
- away(snapshot);
735
- continue;
736
- }
737
- snapshot
738
- .animate([{ opacity: 1 }, { opacity: 0 }], {
739
- duration: motion.duration,
740
- easing: `linear(${Array.from({ length: 13 }, (_, step) => motion.easing(step / 12).toFixed(3)).join(', ')})`,
741
- })
742
- .finished.then(() => away(snapshot), () => away(snapshot));
729
+ const stopWaiting = driver.ready?.(() => {
730
+ place();
731
+ frame.drawn?.();
732
+ const motion = motionOf(frame, 'step');
733
+ for (const snapshot of snapshots) {
734
+ if (!motion || typeof snapshot.animate !== 'function') {
735
+ away(snapshot);
736
+ continue;
743
737
  }
744
- })
745
- : undefined;
738
+ snapshot
739
+ .animate([{ opacity: 1 }, { opacity: 0 }], {
740
+ duration: motion.duration,
741
+ easing: `linear(${Array.from({ length: 13 }, (_, step) => motion.easing(step / 12).toFixed(3)).join(', ')})`,
742
+ })
743
+ .finished.then(() => away(snapshot), () => away(snapshot));
744
+ }
745
+ });
746
746
  let live = true;
747
747
  let placing = false;
748
748
  let drawing = false;
@@ -876,6 +876,7 @@ export const connectMap = (frame, driver, options = {}) => {
876
876
  stopViewing?.();
877
877
  clearTimeout(looking);
878
878
  stopWaiting?.();
879
+ frame.drawn?.(false);
879
880
  stopTheming?.();
880
881
  styled.disconnect();
881
882
  children.disconnect();
@@ -101,6 +101,11 @@ export interface MapLibreMap {
101
101
  loaded?(): boolean;
102
102
  /** Whether the style has loaded: `true`, and anything else is not yet. MapLibre 6 types it loosely. */
103
103
  isStyleLoaded?(): unknown;
104
+ /**
105
+ * Whether one source has every tile the view needs, once the view has asked
106
+ * for them; a source no shown layer draws from has nothing to wait for.
107
+ */
108
+ isSourceLoaded?(id: string): boolean;
104
109
  /** The canvas, which MapLibre makes focusable. */
105
110
  getCanvas(): HTMLElement;
106
111
  /** The middle of the view, which a map-type picture is drawn around. */
@@ -1056,30 +1056,68 @@ export const mapLibreDriver = (map) => ({
1056
1056
  return { x, y };
1057
1057
  },
1058
1058
  ready: (done) => {
1059
- if (map.loaded?.() && map.areTilesLoaded?.()) {
1060
- done();
1061
- return () => undefined;
1059
+ if (!map.isSourceLoaded) {
1060
+ if (map.loaded?.() && map.areTilesLoaded?.()) {
1061
+ done();
1062
+ return () => undefined;
1063
+ }
1064
+ let finished = false;
1065
+ const finish = () => {
1066
+ if (!finished) {
1067
+ finished = true;
1068
+ map.off('load', finish);
1069
+ map.off('render', drawn);
1070
+ done();
1071
+ }
1072
+ };
1073
+ const drawn = () => {
1074
+ if (map.isStyleLoaded?.() === true && map.areTilesLoaded?.() === true) {
1075
+ finish();
1076
+ }
1077
+ };
1078
+ map.on('load', finish);
1079
+ map.on('render', drawn);
1080
+ return () => {
1081
+ map.off('load', finish);
1082
+ map.off('render', drawn);
1083
+ };
1062
1084
  }
1063
- let finished = false;
1064
- const finish = () => {
1065
- if (finished) {
1066
- return;
1085
+ let tiled;
1086
+ const read = () => {
1087
+ const sources = map.getStyle()?.sources;
1088
+ tiled = sources
1089
+ ? Object.entries(sources)
1090
+ .filter(([, source]) => source.type !== 'geojson')
1091
+ .map(([id]) => id)
1092
+ : undefined;
1093
+ };
1094
+ const whole = () => {
1095
+ if (tiled === undefined) {
1096
+ read();
1067
1097
  }
1068
- finished = true;
1069
- stop();
1098
+ return tiled?.every((id) => map.isSourceLoaded?.(id) === true) === true;
1099
+ };
1100
+ if (whole()) {
1070
1101
  done();
1102
+ return () => undefined;
1103
+ }
1104
+ const restyled = () => {
1105
+ tiled = undefined;
1071
1106
  };
1072
- const drawn = () => {
1073
- if (map.isStyleLoaded?.() === true && map.areTilesLoaded?.() === true) {
1074
- finish();
1107
+ const heard = () => {
1108
+ if (whole()) {
1109
+ stop();
1110
+ done();
1075
1111
  }
1076
1112
  };
1077
1113
  const stop = () => {
1078
- map.off('load', finish);
1079
- map.off('render', drawn);
1114
+ map.off('styledata', restyled);
1115
+ map.off('render', heard);
1116
+ map.off('idle', heard);
1080
1117
  };
1081
- map.on('load', finish);
1082
- map.on('render', drawn);
1118
+ map.on('styledata', restyled);
1119
+ map.on('render', heard);
1120
+ map.on('idle', heard);
1083
1121
  return stop;
1084
1122
  },
1085
1123
  rotate: ({ by, to }, motion) => {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@fusion.dev/helix-map",
3
3
  "description": "Connects the Helix Map frame to a map renderer: MapLibre, Leaflet, or a driver of your own.",
4
- "version": "0.113.0",
4
+ "version": "0.114.0",
5
5
  "license": "MIT",
6
6
  "author": "Ross Sheppard <ross.sheppard@fusion.dev>",
7
7
  "repository": {
@@ -43,14 +43,14 @@
43
43
  },
44
44
  "bugs": "https://github.com/project-fusion/fusion/issues",
45
45
  "dependencies": {
46
- "@fusion.dev/helix-core": "0.113.0",
47
- "@fusion.dev/helix-icons": "0.113.0",
46
+ "@fusion.dev/helix-core": "0.114.0",
47
+ "@fusion.dev/helix-icons": "0.114.0",
48
48
  "@mapbox/vector-tile": "^3.0.0",
49
49
  "pbf": "^5.1.2"
50
50
  },
51
51
  "devDependencies": {
52
- "@fusion.dev/helix-headless": "0.113.0",
53
- "@fusion.dev/helix-testing": "0.113.0",
52
+ "@fusion.dev/helix-headless": "0.114.0",
53
+ "@fusion.dev/helix-testing": "0.114.0",
54
54
  "@maplibre/maplibre-gl-style-spec": "^26.4.4",
55
55
  "@types/bun": "^1.3.0",
56
56
  "@types/leaflet": "^1.9.20",