@maplibre-yaml/core 0.1.3-beta.1 → 0.2.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.
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-IvxniEQy.js';
4
- import '../page.schema-VBytF9l5.js';
3
+ import '../map-renderer-8-c51Ww7.js';
4
+ import '../page.schema-Cad2FFqh.js';
5
5
  import 'zod';
6
6
 
7
7
  /**
@@ -4,6 +4,42 @@ import maplibregl2 from 'maplibre-gl';
4
4
 
5
5
  // @maplibre-yaml/core - Declarative web maps with YAML
6
6
 
7
+ var LongitudeSchema = z.number().min(-180, "Longitude must be >= -180").max(180, "Longitude must be <= 180").describe("Longitude in degrees (-180 to 180)");
8
+ var LatitudeSchema = z.number().min(-90, "Latitude must be >= -90").max(90, "Latitude must be <= 90").describe("Latitude in degrees (-90 to 90)");
9
+ var LngLatSchema = z.tuple([LongitudeSchema, LatitudeSchema]).describe("Geographic coordinates as [longitude, latitude]");
10
+ var LngLatBoundsSchema = z.tuple([
11
+ LongitudeSchema,
12
+ // west
13
+ LatitudeSchema,
14
+ // south
15
+ LongitudeSchema,
16
+ // east
17
+ LatitudeSchema
18
+ // north
19
+ ]).describe("Bounding box as [west, south, east, north]");
20
+ var ColorSchema = z.string().refine(
21
+ (val) => {
22
+ if (val.startsWith("#")) {
23
+ return /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(val);
24
+ }
25
+ if (val.startsWith("rgb")) {
26
+ return /^rgba?\s*\([^)]+\)$/.test(val);
27
+ }
28
+ if (val.startsWith("hsl")) {
29
+ return /^hsla?\s*\([^)]+\)$/.test(val);
30
+ }
31
+ return true;
32
+ },
33
+ {
34
+ message: "Invalid color format. Use hex (#rgb, #rrggbb), rgb(), rgba(), hsl(), hsla(), or named colors."
35
+ }
36
+ ).describe("CSS color value");
37
+ var ExpressionSchema = z.array(z.any()).refine((val) => val.length > 0 && typeof val[0] === "string", {
38
+ message: 'Expression must be an array starting with a string operator (e.g., ["get", "property"])'
39
+ }).describe("MapLibre expression for data-driven styling");
40
+ var NumberOrExpressionSchema = z.union([z.number(), ExpressionSchema]).describe("Number value or MapLibre expression");
41
+ var ColorOrExpressionSchema = z.union([ColorSchema, ExpressionSchema]).describe("Color value or MapLibre expression");
42
+ var ZoomLevelSchema = z.number().min(0, "Zoom level must be >= 0").max(24, "Zoom level must be <= 24").describe("Map zoom level (0-24)");
7
43
  var ValidTagNames = [
8
44
  "h1",
9
45
  "h2",
@@ -54,42 +90,6 @@ var ContentBlockSchema = z.object({
54
90
  style: z.string().optional().describe("Inline CSS styles for the block container"),
55
91
  content: z.array(ContentItemSchema).describe("Array of content items to render")
56
92
  }).describe("Content block for rich text and media");
57
- var LongitudeSchema = z.number().min(-180, "Longitude must be >= -180").max(180, "Longitude must be <= 180").describe("Longitude in degrees (-180 to 180)");
58
- var LatitudeSchema = z.number().min(-90, "Latitude must be >= -90").max(90, "Latitude must be <= 90").describe("Latitude in degrees (-90 to 90)");
59
- var LngLatSchema = z.tuple([LongitudeSchema, LatitudeSchema]).describe("Geographic coordinates as [longitude, latitude]");
60
- var LngLatBoundsSchema = z.tuple([
61
- LongitudeSchema,
62
- // west
63
- LatitudeSchema,
64
- // south
65
- LongitudeSchema,
66
- // east
67
- LatitudeSchema
68
- // north
69
- ]).describe("Bounding box as [west, south, east, north]");
70
- var ColorSchema = z.string().refine(
71
- (val) => {
72
- if (val.startsWith("#")) {
73
- return /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(val);
74
- }
75
- if (val.startsWith("rgb")) {
76
- return /^rgba?\s*\([^)]+\)$/.test(val);
77
- }
78
- if (val.startsWith("hsl")) {
79
- return /^hsla?\s*\([^)]+\)$/.test(val);
80
- }
81
- return true;
82
- },
83
- {
84
- message: "Invalid color format. Use hex (#rgb, #rrggbb), rgb(), rgba(), hsl(), hsla(), or named colors."
85
- }
86
- ).describe("CSS color value");
87
- var ExpressionSchema = z.array(z.any()).refine((val) => val.length > 0 && typeof val[0] === "string", {
88
- message: 'Expression must be an array starting with a string operator (e.g., ["get", "property"])'
89
- }).describe("MapLibre expression for data-driven styling");
90
- var NumberOrExpressionSchema = z.union([z.number(), ExpressionSchema]).describe("Number value or MapLibre expression");
91
- var ColorOrExpressionSchema = z.union([ColorSchema, ExpressionSchema]).describe("Color value or MapLibre expression");
92
- var ZoomLevelSchema = z.number().min(0, "Zoom level must be >= 0").max(24, "Zoom level must be <= 24").describe("Map zoom level (0-24)");
93
93
  var StreamConfigSchema = z.object({
94
94
  type: z.enum(["websocket", "sse"]).describe("Streaming connection type"),
95
95
  url: z.string().url().optional().describe("WebSocket or SSE endpoint URL"),
@@ -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")
@@ -704,6 +706,10 @@ var GlobalConfigSchema = z.object({
704
706
  description: z.string().optional().describe("Application description"),
705
707
  defaultMapStyle: z.string().url().optional().describe("Default map style URL"),
706
708
  theme: z.enum(["light", "dark"]).default("light").describe("Default theme"),
709
+ defaultZoom: z.number().min(0).max(24).optional().describe("Default zoom level for all maps"),
710
+ defaultCenter: LngLatSchema.optional().describe(
711
+ "Default center [lng, lat] for all maps"
712
+ ),
707
713
  dataFetching: z.object({
708
714
  defaultStrategy: z.enum(["runtime", "build", "hybrid"]).default("runtime").describe("Default fetch strategy"),
709
715
  timeout: z.number().min(1e3).default(3e4).describe("Default timeout in milliseconds"),
@@ -3564,9 +3570,16 @@ var LayerManager = class {
3564
3570
  this.abortControllers = /* @__PURE__ */ new Map();
3565
3571
  }
3566
3572
  async addLayer(layer) {
3567
- const sourceId = `${layer.id}-source`;
3573
+ const isSourceRef = typeof layer.source === "string";
3574
+ const sourceId = isSourceRef ? layer.source : `${layer.id}-source`;
3568
3575
  this.layerToSource.set(layer.id, sourceId);
3569
- 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
+ }
3570
3583
  const layerSpec = {
3571
3584
  id: layer.id,
3572
3585
  type: layer.type,
@@ -3594,12 +3607,6 @@ var LayerManager = class {
3594
3607
  }
3595
3608
  }
3596
3609
  async addSource(sourceId, layer) {
3597
- if (typeof layer.source === "string") {
3598
- if (!this.map.getSource(layer.source)) {
3599
- throw new Error(`Source reference '${layer.source}' not found`);
3600
- }
3601
- return;
3602
- }
3603
3610
  const source = layer.source;
3604
3611
  if (source.type === "geojson") {
3605
3612
  const geojsonSource = source;
@@ -3819,7 +3826,10 @@ var LayerManager = class {
3819
3826
  }
3820
3827
  if (this.map.getLayer(layerId)) this.map.removeLayer(layerId);
3821
3828
  const sourceId = this.layerToSource.get(layerId) || `${layerId}-source`;
3822
- 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
+ }
3823
3833
  this.sourceData.delete(sourceId);
3824
3834
  this.layerToSource.delete(layerId);
3825
3835
  }
@@ -4181,7 +4191,7 @@ var MapRenderer = class {
4181
4191
  controlsManager;
4182
4192
  eventListeners;
4183
4193
  isLoaded;
4184
- constructor(container, config, layers = [], options = {}) {
4194
+ constructor(container, config, layers = [], options = {}, sources) {
4185
4195
  this.eventListeners = /* @__PURE__ */ new Map();
4186
4196
  this.isLoaded = false;
4187
4197
  this.map = new maplibregl2.Map({
@@ -4209,6 +4219,13 @@ var MapRenderer = class {
4209
4219
  this.controlsManager = new ControlsManager(this.map);
4210
4220
  this.map.on("load", () => {
4211
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
+ }
4212
4229
  Promise.all(layers.map((layer) => this.addLayer(layer))).then(() => {
4213
4230
  this.emit("load", void 0);
4214
4231
  options.onLoad?.();
@@ -4508,7 +4525,7 @@ var MLMap = class extends HTMLElement {
4508
4525
  }
4509
4526
  this.appendChild(this.mapContainer);
4510
4527
  try {
4511
- const { config, layers = [] } = mapBlock;
4528
+ const { config, sources, layers = [] } = mapBlock;
4512
4529
  this.renderer = new MapRenderer(this.mapContainer, config, layers, {
4513
4530
  onLoad: () => {
4514
4531
  },
@@ -4520,7 +4537,7 @@ var MLMap = class extends HTMLElement {
4520
4537
  })
4521
4538
  );
4522
4539
  }
4523
- });
4540
+ }, sources);
4524
4541
  this.setupEventForwarding();
4525
4542
  } catch (error) {
4526
4543
  this.handleError([