Skip to content

Commit

Permalink
Merge pull request #19 from MIERUNE/feat/custom-control
Browse files Browse the repository at this point in the history
Add CustomControl components
  • Loading branch information
ciscorn authored Nov 21, 2024
2 parents 6bdb38c + a870266 commit 25075c5
Show file tree
Hide file tree
Showing 8 changed files with 215 additions and 3 deletions.
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@ Everyone is welcomed to contribute to this project! There are many ways to suppo
- TODO: Add core contributors
- And [all contributors](https://github.com/MIERUNE/svelte-maplibre-gl/graphs/contributors)

Supported by [MIERUNE Inc.](https://www.mierune.co.jp/)

## Acknowledgements

This project `svelte-maplibre-gl` is inspired by the efforts and innovations of the following libraries:
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "svelte-maplibre-gl",
"version": "0.0.5",
"version": "0.0.6",
"license": "(MIT OR Apache-2.0)",
"description": "Svelte library for using MapLibre GL JS as reactive components",
"scripts": {
Expand Down
1 change: 1 addition & 0 deletions src/content/examples/Index.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@
<li><a href="/examples/canvas-source">Canvas Source</a></li>
<li><a href="/examples/fullscreen">Fullscreen</a></li>
<li><a href="/examples/geolocate">Locate the User</a></li>
<li><a href="/examples/custom-control">Custom Control</a></li>
</ul>
88 changes: 88 additions & 0 deletions src/content/examples/custom-control/CustomControl.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<script lang="ts">
import { HillshadeLayer, MapLibre, RasterDEMTileSource, Terrain, CustomControl } from 'svelte-maplibre-gl';
import maplibregl from 'maplibre-gl';
import Sun from 'lucide-svelte/icons/sun';
import Moon from 'lucide-svelte/icons/moon';
import ArrowUpLeft from 'lucide-svelte/icons/arrow-up-left';
import ArrowUpRight from 'lucide-svelte/icons/arrow-up-right';
import ArrowDownLeft from 'lucide-svelte/icons/arrow-down-left';
import ArrowDownRight from 'lucide-svelte/icons/arrow-down-right';
import { MyControl } from './MyControl.js';
let isHillshadeVisible = $state(true);
let isTerrainVisible = $state(true);
let isDarkMode = $state(false);
const mapStyle = $derived(
isDarkMode
? 'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json'
: 'https://basemaps.cartocdn.com/gl/voyager-gl-style/style.json'
);
let center = $state({ lng: 11.09085, lat: 47.3 });
let controlPosition: maplibregl.ControlPosition = $state('top-left');
const myControl = new MyControl({
toggleHillshade: () => {
isHillshadeVisible = !isHillshadeVisible;
return isHillshadeVisible;
},
toggleTerrain: () => {
isTerrainVisible = !isTerrainVisible;
return isTerrainVisible;
}
});
</script>

<MapLibre class="h-[50vh] min-h-[200px]" style={mapStyle} zoom={12} pitch={40} maxPitch={85} bind:center>
<!-- inject IControl (useful for plugin) -->
<CustomControl position="top-left" control={myControl} />

<!-- Control / Group / Icon -->
<CustomControl position="bottom-left">
<button onclick={() => (isDarkMode = !isDarkMode)} class="grid place-items-center text-gray-900">
{#if isDarkMode}
<Moon class="w-5" />
{:else}
<Sun class="w-5" />
{/if}
</button>
</CustomControl>

<!-- Group -->
<CustomControl position={controlPosition} class="text-gray-900">
<button class="place-items-center" onclick={() => (controlPosition = 'top-left')}
><ArrowUpLeft class="w-5" /></button
>
<button class="place-items-center" onclick={() => (controlPosition = 'top-right')}
><ArrowUpRight class="w-5" /></button
>
<button class="place-items-center" onclick={() => (controlPosition = 'bottom-right')}
><ArrowDownRight class="w-5" /></button
>
<button class="place-items-center" onclick={() => (controlPosition = 'bottom-left')}
><ArrowDownLeft class="w-5" /></button
>
</CustomControl>

<!-- Control / Group / any svelte elements -->
<CustomControl position="top-right">
<div class="p-2 text-yellow-700">Arbitrary HTML</div>
<div class="border-t border-t-[#ddd] p-2 text-center text-yellow-700">
({center.lat.toFixed(4)}, {center.lat.toFixed(4)})
</div>
</CustomControl>

<RasterDEMTileSource
id="terrain"
tiles={['https://demotiles.maplibre.org/terrain-tiles/{z}/{x}/{y}.png']}
minzoom={0}
maxzoom={12}
attribution="<a href='https://earth.jaxa.jp/en/data/policy/'>AW3D30 (JAXA)</a>"
>
{#if isTerrainVisible}
<Terrain />
{/if}
{#if isHillshadeVisible}
<HillshadeLayer />
{/if}
</RasterDEMTileSource>
</MapLibre>
58 changes: 58 additions & 0 deletions src/content/examples/custom-control/MyControl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
interface MyControlConstructorOptions {
toggleTerrain: () => boolean;
toggleHillshade: () => boolean;
}

class MyControl implements maplibregl.IControl {
private _container: HTMLElement | undefined;
private _toggleTerrain: () => boolean;
private _toggleHillshade: () => boolean;

constructor(options: MyControlConstructorOptions) {
this._toggleTerrain = options.toggleTerrain;
this._toggleHillshade = options.toggleHillshade;
}

onAdd() {
this._container = document.createElement('div');
this._container.className = 'maplibregl-ctrl maplibregl-ctrl-group p-2 rounded flex w-[240px] gap-x-2';

const toggleTerrain = document.createElement('button');
toggleTerrain.textContent = 'Disable Terrain';
toggleTerrain.type = 'button';
toggleTerrain.style.backgroundColor = 'red';
toggleTerrain.style.color = 'white';
toggleTerrain.style.width = '50%';
toggleTerrain.style.height = '100%';
toggleTerrain.style.borderRadius = '0.25rem';
toggleTerrain.addEventListener('click', () => {
const newState = this._toggleTerrain();
toggleTerrain.textContent = newState ? 'Disable Terrain' : 'Enable Terrain';
});

const toggleHillshade = document.createElement('button');
toggleHillshade.textContent = 'Disable Hillshade';
toggleHillshade.type = 'button';
toggleHillshade.style.backgroundColor = 'blue';
toggleHillshade.style.color = 'white';
toggleHillshade.style.height = '100%';
toggleHillshade.style.width = '50%';
toggleHillshade.style.borderRadius = '0.25rem';
toggleHillshade.addEventListener('click', () => {
const newState = this._toggleHillshade();
toggleHillshade.textContent = newState ? 'Disable Hillshade' : 'Enable Hillshade';
});

this._container.appendChild(toggleTerrain);
this._container.appendChild(toggleHillshade);
return this._container!;
}

onRemove() {
if (this._container && this._container.parentNode) {
this._container.parentNode.removeChild(this._container);
}
}
}

export { MyControl };
14 changes: 14 additions & 0 deletions src/content/examples/custom-control/content.svelte.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
title: Custom Control
description: Custom Control allows to easily create user defined controls.
---

<script lang="ts">
import CustomControl from "./CustomControl.svelte";
import demoRaw from "./CustomControl.svelte?raw";
import CodeBlock from "../../CodeBlock.svelte";
</script>

<CustomControl />

<CodeBlock content={demoRaw} />
52 changes: 52 additions & 0 deletions src/lib/maplibre/controls/CustomControl.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<script lang="ts">
// https://maplibre.org/maplibre-gl-js/docs/API/interfaces/IControl/
import type { Snippet } from 'svelte';
import maplibregl from 'maplibre-gl';
import { getMapContext } from '../contexts.svelte.js';
interface Props {
position?: maplibregl.ControlPosition;
control?: maplibregl.IControl;
group?: boolean;
class?: string;
children?: Snippet;
}
let { position, control: givenControl, class: className, group = true, children }: Props = $props();
if (!givenControl && !children) throw new Error('You must provide either control or children.');
const mapCtx = getMapContext();
if (!mapCtx.map) throw new Error('Map instance is not initialized.');
let el: HTMLDivElement | undefined = $state();
let control = $derived.by(() => {
if (givenControl) {
return givenControl;
}
return {
onAdd: () => {
return el!;
},
onRemove: () => {
el?.parentNode?.removeChild(el);
}
};
});
$effect(() => {
if (control) {
mapCtx.map?.addControl(control, position);
}
return () => {
control && mapCtx.map?.removeControl(control);
};
});
</script>

{#if !givenControl}
<div bind:this={el} class={`maplibregl-ctrl ${className}`} class:maplibregl-ctrl-group={group}>
{@render children?.()}
</div>
{/if}
1 change: 1 addition & 0 deletions src/lib/maplibre/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,5 @@ export { default as FullScreenControl } from './controls/FullScreenControl.svelt
export { default as TerrainControl } from './controls/TerrainControl.svelte';
export { default as ScaleControl } from './controls/ScaleControl.svelte';
export { default as LogoControl } from './controls/LogoControl.svelte';
export { default as CustomControl } from './controls/CustomControl.svelte';
export { default as Hash } from './controls/Hash.svelte';

0 comments on commit 25075c5

Please sign in to comment.