@maplibre-yaml/core 0.1.3 → 0.2.1

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
@@ -104,6 +104,50 @@ layers:
104
104
  circle-color: "#ef4444"
105
105
  ```
106
106
 
107
+ ### Named Sources
108
+
109
+ Define sources at the block level and reference them by name across multiple layers:
110
+
111
+ ```yaml
112
+ type: map
113
+ id: neighborhood-map
114
+ config:
115
+ center: [-73.985, 40.674]
116
+ zoom: 14
117
+ mapStyle: https://basemaps.cartocdn.com/gl/positron-gl-style/style.json
118
+
119
+ sources:
120
+ boundary:
121
+ type: geojson
122
+ url: https://example.com/boundary.geojson
123
+ parcels:
124
+ type: vector
125
+ url: https://example.com/parcels.json
126
+
127
+ layers:
128
+ - id: boundary-fill
129
+ type: fill
130
+ source: boundary
131
+ paint:
132
+ fill-color: "#3388ff"
133
+ fill-opacity: 0.2
134
+ - id: boundary-outline
135
+ type: line
136
+ source: boundary
137
+ paint:
138
+ line-color: "#3388ff"
139
+ line-width: 2
140
+ - id: parcels-fill
141
+ type: fill
142
+ source: parcels
143
+ source-layer: parcels
144
+ paint:
145
+ fill-color: "#ccc"
146
+ fill-opacity: 0.5
147
+ ```
148
+
149
+ Sources defined at the block level are added to the map before layers, so any layer can reference them by name. This avoids duplicating source definitions when multiple layers use the same data. Inline sources on individual layers still work as before.
150
+
107
151
  ### Real-time Data with Polling
108
152
 
109
153
  ```yaml
@@ -1,7 +1,7 @@
1
1
  export { MLMap, registerMLMap } from '../register.js';
2
2
  import 'maplibre-gl';
3
- import '../map-renderer-Br4guic2.js';
4
- import '../page.schema-EBT_0Ojm.js';
3
+ import '../map-renderer-SjO3KQmx.js';
4
+ import '../page.schema-Cad2FFqh.js';
5
5
  import 'zod';
6
6
 
7
7
  /**
@@ -1,6 +1,6 @@
1
1
  import { parse } from 'yaml';
2
2
  import { z, ZodError } from 'zod';
3
- import maplibregl2 from 'maplibre-gl';
3
+ import { Map as Map$1, Popup, NavigationControl, GeolocateControl, ScaleControl, FullscreenControl } from 'maplibre-gl';
4
4
 
5
5
  // @maplibre-yaml/core - Declarative web maps with YAML
6
6
 
@@ -589,6 +589,7 @@ var MapBlockSchema = z.object({
589
589
  className: z.string().optional().describe("CSS class name for container"),
590
590
  style: z.string().optional().describe("Inline CSS styles for container"),
591
591
  config: MapConfigSchema.describe("Map configuration"),
592
+ sources: z.record(z.string(), LayerSourceSchema).optional().describe("Map sources"),
592
593
  layers: z.array(LayerOrReferenceSchema).default([]).describe("Map layers"),
593
594
  controls: ControlsConfigSchema.optional().describe("Map controls"),
594
595
  legend: LegendConfigSchema.optional().describe("Legend configuration")
@@ -599,6 +600,7 @@ var MapFullPageBlockSchema = z.object({
599
600
  className: z.string().optional().describe("CSS class name for container"),
600
601
  style: z.string().optional().describe("Inline CSS styles for container"),
601
602
  config: MapConfigSchema.describe("Map configuration"),
603
+ sources: z.record(z.string(), LayerSourceSchema).optional().describe("Map sources"),
602
604
  layers: z.array(LayerOrReferenceSchema).default([]).describe("Map layers"),
603
605
  controls: ControlsConfigSchema.optional().describe("Map controls"),
604
606
  legend: LegendConfigSchema.optional().describe("Legend configuration")
@@ -3568,9 +3570,16 @@ var LayerManager = class {
3568
3570
  this.abortControllers = /* @__PURE__ */ new Map();
3569
3571
  }
3570
3572
  async addLayer(layer) {
3571
- const sourceId = `${layer.id}-source`;
3573
+ const isSourceRef = typeof layer.source === "string";
3574
+ const sourceId = isSourceRef ? layer.source : `${layer.id}-source`;
3572
3575
  this.layerToSource.set(layer.id, sourceId);
3573
- await this.addSource(sourceId, layer);
3576
+ if (!isSourceRef) {
3577
+ await this.addSource(sourceId, layer);
3578
+ } else if (!this.map.getSource(sourceId)) {
3579
+ throw new Error(
3580
+ `Source '${sourceId}' referenced by layer '${layer.id}' not found. Ensure it is defined in the block-level 'sources' map.`
3581
+ );
3582
+ }
3574
3583
  const layerSpec = {
3575
3584
  id: layer.id,
3576
3585
  type: layer.type,
@@ -3598,12 +3607,6 @@ var LayerManager = class {
3598
3607
  }
3599
3608
  }
3600
3609
  async addSource(sourceId, layer) {
3601
- if (typeof layer.source === "string") {
3602
- if (!this.map.getSource(layer.source)) {
3603
- throw new Error(`Source reference '${layer.source}' not found`);
3604
- }
3605
- return;
3606
- }
3607
3610
  const source = layer.source;
3608
3611
  if (source.type === "geojson") {
3609
3612
  const geojsonSource = source;
@@ -3823,7 +3826,10 @@ var LayerManager = class {
3823
3826
  }
3824
3827
  if (this.map.getLayer(layerId)) this.map.removeLayer(layerId);
3825
3828
  const sourceId = this.layerToSource.get(layerId) || `${layerId}-source`;
3826
- if (this.map.getSource(sourceId)) this.map.removeSource(sourceId);
3829
+ const isInlineSource = sourceId === `${layerId}-source`;
3830
+ if (isInlineSource && this.map.getSource(sourceId)) {
3831
+ this.map.removeSource(sourceId);
3832
+ }
3827
3833
  this.sourceData.delete(sourceId);
3828
3834
  this.layerToSource.delete(layerId);
3829
3835
  }
@@ -4031,7 +4037,7 @@ var EventHandler = class {
4031
4037
  showPopup(content, feature, lngLat) {
4032
4038
  this.activePopup?.remove();
4033
4039
  const html = this.popupBuilder.build(content, feature.properties);
4034
- this.activePopup = new maplibregl2.Popup().setLngLat(lngLat).setHTML(html).addTo(this.map);
4040
+ this.activePopup = new Popup().setLngLat(lngLat).setHTML(html).addTo(this.map);
4035
4041
  }
4036
4042
  /**
4037
4043
  * Detach events for a layer
@@ -4136,14 +4142,14 @@ var ControlsManager = class {
4136
4142
  if (config.navigation) {
4137
4143
  const options = typeof config.navigation === "object" ? config.navigation : {};
4138
4144
  const position = options.position || "top-right";
4139
- const control = new maplibregl2.NavigationControl();
4145
+ const control = new NavigationControl();
4140
4146
  this.map.addControl(control, position);
4141
4147
  this.addedControls.push(control);
4142
4148
  }
4143
4149
  if (config.geolocate) {
4144
4150
  const options = typeof config.geolocate === "object" ? config.geolocate : {};
4145
4151
  const position = options.position || "top-right";
4146
- const control = new maplibregl2.GeolocateControl({
4152
+ const control = new GeolocateControl({
4147
4153
  positionOptions: { enableHighAccuracy: true },
4148
4154
  trackUserLocation: true
4149
4155
  });
@@ -4153,14 +4159,14 @@ var ControlsManager = class {
4153
4159
  if (config.scale) {
4154
4160
  const options = typeof config.scale === "object" ? config.scale : {};
4155
4161
  const position = options.position || "bottom-left";
4156
- const control = new maplibregl2.ScaleControl();
4162
+ const control = new ScaleControl();
4157
4163
  this.map.addControl(control, position);
4158
4164
  this.addedControls.push(control);
4159
4165
  }
4160
4166
  if (config.fullscreen) {
4161
4167
  const options = typeof config.fullscreen === "object" ? config.fullscreen : {};
4162
4168
  const position = options.position || "top-right";
4163
- const control = new maplibregl2.FullscreenControl();
4169
+ const control = new FullscreenControl();
4164
4170
  this.map.addControl(control, position);
4165
4171
  this.addedControls.push(control);
4166
4172
  }
@@ -4185,10 +4191,10 @@ var MapRenderer = class {
4185
4191
  controlsManager;
4186
4192
  eventListeners;
4187
4193
  isLoaded;
4188
- constructor(container, config, layers = [], options = {}) {
4194
+ constructor(container, config, layers = [], options = {}, sources) {
4189
4195
  this.eventListeners = /* @__PURE__ */ new Map();
4190
4196
  this.isLoaded = false;
4191
- this.map = new maplibregl2.Map({
4197
+ this.map = new Map$1({
4192
4198
  ...config,
4193
4199
  container: typeof container === "string" ? container : container,
4194
4200
  style: config.mapStyle,
@@ -4213,6 +4219,13 @@ var MapRenderer = class {
4213
4219
  this.controlsManager = new ControlsManager(this.map);
4214
4220
  this.map.on("load", () => {
4215
4221
  this.isLoaded = true;
4222
+ if (sources) {
4223
+ for (const [id, sourceSpec] of Object.entries(sources)) {
4224
+ if (!this.map.getSource(id)) {
4225
+ this.map.addSource(id, sourceSpec);
4226
+ }
4227
+ }
4228
+ }
4216
4229
  Promise.all(layers.map((layer) => this.addLayer(layer))).then(() => {
4217
4230
  this.emit("load", void 0);
4218
4231
  options.onLoad?.();
@@ -4512,7 +4525,7 @@ var MLMap = class extends HTMLElement {
4512
4525
  }
4513
4526
  this.appendChild(this.mapContainer);
4514
4527
  try {
4515
- const { config, layers = [] } = mapBlock;
4528
+ const { config, sources, layers = [] } = mapBlock;
4516
4529
  this.renderer = new MapRenderer(this.mapContainer, config, layers, {
4517
4530
  onLoad: () => {
4518
4531
  },
@@ -4524,7 +4537,7 @@ var MLMap = class extends HTMLElement {
4524
4537
  })
4525
4538
  );
4526
4539
  }
4527
- });
4540
+ }, sources);
4528
4541
  this.setupEventForwarding();
4529
4542
  } catch (error) {
4530
4543
  this.handleError([