@maptiler/sdk 3.7.0 → 3.8.0-rc.9

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
@@ -451,7 +451,145 @@ const map = new Map({
451
451
  })
452
452
  ```
453
453
 
454
+ # 🧩 Custom Controls
454
455
 
456
+ MapTiler SDK JS supports two flexible ways to add custom controls to your map interface. Whether you're building a dynamic application or integrating with static HTML, there's a matching approach for both.
457
+
458
+ ## Programmatic Controls
459
+
460
+ Use `map.addControl()` with `MaptilerExternalControl` to register custom UI elements manually. This approach is ideal for dynamic logic, event-driven behavior, or integration with frameworks like React. The control element can be provided either as a reference to the **element itself**, or as its **CSS selector**. Optionally, two callback functions can be provided:
461
+
462
+ * `onClick` function that is called when the element is clicked, and
463
+ * `onRender` function that is called every time the map renders a new state.
464
+
465
+ Both callbacks receive the active `Map` instance, the associated control element itself, and an event object associated with the original event (`PointerEvent` and `MapLibreEvent` respectively).
466
+
467
+ ### Example
468
+
469
+ ```ts
470
+ const panControl = new maptilersdk.MaptilerExternalControl(
471
+ ".pan-control",
472
+ (map) => map.panBy([10, 10]), // Move southeast on click
473
+ (map, el) => el.classList.toggle( // Style based on hemisphere
474
+ "northern-hemisphere", map.getCenter().lat > 0
475
+ )
476
+ );
477
+ map.addControl(panControl);
478
+
479
+ const element = document.createElement("button");
480
+ element.textContent = "Pan NW";
481
+ map.addControl(
482
+ new maptilersdk.MaptilerExternalControl(
483
+ element,
484
+ (map) => map.panBy([-10, -10]) // Move northwest
485
+ ),
486
+ "top-left"
487
+ );
488
+ ```
489
+
490
+ ### Behavior Overview
491
+
492
+ - On add, control element is moved into the map UI
493
+ - `onClick` binds user interaction
494
+ - `onRender` enables state-based styling or logic
495
+ - Control maintains its own DOM context
496
+ - On removal, element is returned to its original DOM position (if any) to not interfere with DOM handling of frameworks like React
497
+
498
+ ## Declarative Controls
499
+
500
+ Add controls using HTML attributes – no JavaScript required. This is perfect for simple UI setups.
501
+
502
+ ### Enabling Detection
503
+
504
+ ```ts
505
+ const map = new maptilersdk.Map({
506
+ container: "map",
507
+ customControls: true, // or ".custom-ui"
508
+ });
509
+ ```
510
+
511
+ You can pass `true` to enable detection globally, or a CSS selector to scope it.
512
+
513
+ ### Declaring Controls
514
+
515
+ Use `data-maptiler-control` attribute:
516
+
517
+ ```html
518
+ <button data-maptiler-control="zoom-in">+</button>
519
+ ```
520
+
521
+ Supported values:
522
+
523
+ | Value | Description |
524
+ |---------------------|--------------------------------------------------|
525
+ | `zoom-in` | Zooms in |
526
+ | `zoom-out` | Zooms out |
527
+ | `toggle-projection` | Switches Mercator ↔ Globe |
528
+ | `toggle-terrain` | Toggles terrain layer |
529
+ | `reset-view` | Resets bearing, pitch, and roll |
530
+ | `reset-bearing` | Resets bearing only |
531
+ | `reset-pitch` | Resets pitch only |
532
+ | `reset-roll` | Resets roll only |
533
+ | *(empty)* | Registers control without built-in functionality |
534
+
535
+ > ⚠️ An error is thrown if an unrecognized value is used.
536
+
537
+ ### Grouping Controls
538
+
539
+ Use `data-maptiler-control-group` to group buttons:
540
+
541
+ ```html
542
+ <div data-maptiler-control-group>
543
+ <button data-maptiler-control="zoom-in">+</button>
544
+ <button data-maptiler-control="zoom-out">−</button>
545
+ </div>
546
+ ```
547
+
548
+ Groups are styled and positioned together but don't add functionality themselves. Functional behavior is attached to valid descendant elements.
549
+
550
+ ### Positioning Controls
551
+
552
+ Use `data-maptiler-position` to set placement:
553
+
554
+ ```html
555
+ <button data-maptiler-control="reset-view" data-maptiler-position="top-left">↻</button>
556
+ <div data-maptiler-control-group data-maptiler-position="bottom-right">
557
+ <button data-maptiler-control="zoom-in">+</button>
558
+ <button data-maptiler-control="zoom-out">−</button>
559
+ </div>
560
+ ```
561
+
562
+ Valid positions: `'top-left'`, `'top-right'`, `'bottom-left'`, `'bottom-right'`
563
+
564
+ ## Styling with CSS Variables
565
+
566
+ When declarative controls are enabled, the map container exposes dynamic CSS variables:
567
+
568
+ | Variable | Description | Type |
569
+ |----------------------------------|--------------------------------------------------|-----------------|
570
+ | `--maptiler-center-lng` | Longitude of center | unitless number |
571
+ | `--maptiler-center-lat` | Latitude of center | unitless number |
572
+ | `--maptiler-zoom` | Zoom level | unitless number |
573
+ | `--maptiler-bearing` | Map rotation | unitless number |
574
+ | `--maptiler-pitch` | Pitch angle | unitless number |
575
+ | `--maptiler-roll` | Roll angle | unitless number |
576
+ | `--maptiler-is-globe-projection` | `true` if globe is active<br>`false` otherwise | string |
577
+ | `--maptiler-has-terrain` | `true` if terrain is active<br>`false` otherwise | string |
578
+
579
+ ### Example
580
+
581
+ ```css
582
+ .compass-icon {
583
+ transform: rotateX(calc(var(--maptiler-pitch) * 1deg))
584
+ rotateZ(calc(var(--maptiler-bearing) * -1deg));
585
+ }
586
+
587
+ @container style(--maptiler-is-globe-projection: true) {
588
+ .projection-icon {
589
+ content: "globe";
590
+ }
591
+ }
592
+ ```
455
593
 
456
594
  # 3D terrain in one call
457
595
  <p align="center">
@@ -592,6 +730,13 @@ map.on("load", () => {
592
730
  });
593
731
  });
594
732
  ```
733
+
734
+ To disable state transitions for halo or space:
735
+ ```ts
736
+ map.disableHaloAnimations();
737
+ map.disableSpaceAnimations();
738
+ ```
739
+
595
740
  ## `space` (Background Environment)
596
741
 
597
742
  The space option allows customizing the background environment of the globe, simulating deep space or skybox effects.
@@ -675,6 +820,50 @@ Note: if `space.color` or `space.<faces | path | preset>` are not explicitly set
675
820
 
676
821
  Further code examples can be found in `~/demos/`
677
822
 
823
+ # `ImageViewer`
824
+
825
+ MapTiler's `ImageViewer` component allows you to display tiled, non-georeferenced images but interact with them in almost the same way you would if you were displaying map. These can be handy for zoomable non-georeferenced, geographically "inaccurate" maps such as hotel maps, golf courses, theme parks etc. Think pixels instead of lattitudes and longtidues.
826
+
827
+ ```ts
828
+ export type ImageViewerConstructorOptions = {
829
+ imageUUID: string; // the unique UUID of the image object you are displaying
830
+ center?: [number, number]; // the center you want the viewer to init on, defaults to the center of the image.
831
+ container: string | HTMLElement // the container element you want to mount the viewer on
832
+ apiKey: string; // your MapTiler API Key
833
+ zoom?: number;
834
+ maxZoom?: number;
835
+ minZoom?: number;
836
+ bearing?: number;
837
+ debug?: boolean; // whether you want to debug the tiles
838
+ };
839
+
840
+ const imageViewer = new ImageViewer({
841
+ container: document.getElementById("map")!, // the container element you want to use
842
+ apiKey: "YOUR_MAPTILER_API_KEY", // your api key
843
+ imageUUID: "11111111-2222-3333-4444-555555555555", // unique UUID of the image object
844
+ // ...other options, see below
845
+ }: ImageViewerConstructorOptions);
846
+
847
+ await imageViewer.onReadyAsync()
848
+
849
+ // OR
850
+
851
+ imageViewer.on("imageviewerready", () => { console.log('Ready!') })
852
+ ```
853
+
854
+ ## Methods
855
+ `ImageViewer` provides a subset of methods for interaction with the map. A major caveat is that the `ImageViewer` component works in pixels and not in LngLat. Thus, when using methods such as `setCenter` or `flyTo` the pixel values provided refer to the _absolute pixel position_ on the image, not screen pixel position.
856
+
857
+ Imagine your image is 10,000px x 10,000px, if regardless if your zoom is 2 or 4, calling `.setCenter(500,500)` will always position the viewer over the same part of the image.
858
+
859
+ For full details on supported methods see the type declaration for `ImageViewer`.
860
+
861
+ ## Events
862
+ In a similar manner, a subset of map events are fired by the image viewer. All UI interaction events that would normally include a `LngLat` in the event data instead receive an `imageX` and `imageY` field, representing an absolute pixel position of the image. This is same for `flyTo`, `jumpTo`, `panTo` etc.
863
+
864
+ A full list of supported events can be found in the exported type declaration `ImageViewerEventTypes`
865
+
866
+
678
867
  # Easy language switching
679
868
  The language generally depends on the style but we made it possible to easily set and update from a built-in list of languages.
680
869
 
@@ -143,6 +143,11 @@
143
143
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20width%3D%2233%22%20height%3D%2233%22%20viewBox%3D%220%200%2033%2033%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%3Cpath%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20d%3D%22M26.5%209.85H6.5C6.41716%209.85%206.35%209.91716%206.35%2010V23C6.35%2023.0828%206.41716%2023.15%206.5%2023.15H26.5C26.5828%2023.15%2026.65%2023.0828%2026.65%2023V10C26.65%209.91716%2026.5828%209.85%2026.5%209.85ZM6.5%208C5.39543%208%204.5%208.89543%204.5%2010V23C4.5%2024.1046%205.39543%2025%206.5%2025H26.5C27.6046%2025%2028.5%2024.1046%2028.5%2023V10C28.5%208.89543%2027.6046%208%2026.5%208H6.5Z%22%20fill%3D%22%23444952%22%2F%3E%0A%3Cpath%20d%3D%22M5.5%2012.8H28.5V14.65H5.5V12.8Z%22%20fill%3D%22%23444952%22%2F%3E%0A%3Cpath%20d%3D%22M10.0375%2025L10.0375%208L11.8875%208L11.8875%2025H10.0375Z%22%20fill%3D%22%23444952%22%2F%3E%0A%3Cpath%20d%3D%22M15.5751%2025L15.5751%208L17.4251%208L17.4251%2025H15.5751Z%22%20fill%3D%22%23444952%22%2F%3E%0A%3Cpath%20d%3D%22M21.1127%2025V8L22.9627%208L22.9627%2025H21.1127Z%22%20fill%3D%22%23444952%22%2F%3E%0A%3Cpath%20d%3D%22M5.5%2018.35H28.5V20.2H5.5V18.35Z%22%20fill%3D%22%23444952%22%2F%3E%0A%3C%2Fsvg%3E%0A");
144
144
  }
145
145
 
146
+ /* Center image control icon - globe */
147
+ .maplibregl-ctrl button.maplibregl-ctrl-fit-image-to-bounds .maplibregl-ctrl-icon {
148
+ background-image: url("data:image/svg+xml;charset=utf-8,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22UTF-8%22%3F%3E%0A%3Csvg%0A%20%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%0A%20%20width%3D%2224%22%0A%20%20height%3D%2224%22%0A%20%20viewBox%3D%220%200%2032%2032%22%0A%3E%0A%20%20%3Cpath%0A%20%20%20%20fill%3D%22%23444952%22%0A%20%20%20%20d%3D%22M8%202H2v6h2V4h4zm16%200h6v6h-2V4h-4zM8%2030H2v-6h2v4h4zm16%200h6v-6h-2v4h-4zm0-6H8a2%202%200%200%201-2-2V10a2%202%200%200%201%202-2h16a2%202%200%200%201%202%202v12a2%202%200%200%201-2%202M8%2010v12h16V10z%22%0A%20%20%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E");
149
+ }
150
+
146
151
  .maplibregl-ctrl-scale {
147
152
  background-color: hsla(0,0%,100%,.75);
148
153
  border: 1px solid #444952;