Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,405 changes: 1,399 additions & 6 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
],
"scripts": {
"test": "npm run test -w @studiometa/ui-tests",
"test:watch": "npm run test:watch -w @studiometa/ui-tests",
"build": "rm -rf dist && npm run build:pkg && npm run build:sizes && npm run build:types && npm run build:twig && npm run build:cp-files",
"build:cp-files": "cat packages/ui/package.json | sed 's/index\\.ts/index\\.js/' > dist/package.json && cat dist/package.json && cp LICENSE.md dist/ && cp README.md dist/",
"build:twig": "rsync -avh --include='*/' --include='**/*.twig' --exclude='*' --prune-empty-dirs packages/ui/ dist/",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@
url.searchParams.set('style', css.value);
}

url.searchParams.set('html-editor', html.value !== defaultContent ? 'true' : 'false');
url.searchParams.set('script-editor', script.value !== defaultContent ? 'true' : 'false');
url.searchParams.set('html-editor', html.value !== defaultContent && props.htmlEditor !== false ? 'true' : 'false');
url.searchParams.set('script-editor', script.value !== defaultContent && props.scriptEditor !== false ? 'true' : 'false');
url.searchParams.set(
'style-editor',
css.value !== defaultContent && [true, null].includes(props.cssEditor) ? 'true' : 'false',
Expand Down
28 changes: 28 additions & 0 deletions packages/docs/components/MapboxMap/examples.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
title: MapboxMap examples
---

# Examples

## Simple map

<PreviewPlayground
:html="() => import('./stories/map/app.twig')"
:html-editor="false"
:script="() => import('./stories/map/app.js?raw')"
:script-editor="false"
:css="() => import('./stories/map/app.css?raw')"
:css-editor="false"
/>


## Markers

<PreviewPlayground
:html="() => import('./stories/markers/app.twig')"
:html-editor="false"
:script="() => import('./stories/markers/app.js?raw')"
:script-editor="false"
:css="() => import('./stories/markers/app.css?raw')"
:css-editor="false"
/>
51 changes: 51 additions & 0 deletions packages/docs/components/MapboxMap/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
---
badges: [JS]
---

# MapboxMap <Badges :texts="$frontmatter.badges" />

## Table of content

- [Examples](./examples.md)
- [JS API](./js-api.md)

## Usage

Use this component to display a map with [mapbox-gl](https://github.com/mapbox/mapbox-gl-js).

::: code-group

```js twoslash [app.js]
import { Base, createApp } from '@studiometa/js-toolkit';
import { MapboxMap } from '@studiometa/ui';

class App extends Base {
static config = {
name: 'App',
components: {
MapboxMap,
},
};
}

export default createApp(App);
```

```html [index.html]
<div data-component="MapboxMap">
<div data-ref="container"></div>
</div>
```

```css [app.css]
@import 'mapbox-gl/dist/mapbox-gl.css';
```

<PreviewPlayground
:html="() => import('./stories/map/app.twig')"
:html-editor="false"
:script="() => import('./stories/map/app.js?raw')"
:script-editor="false"
:css="() => import('./stories/map/app.css?raw')"
:css-editor="false"
/>
5 changes: 5 additions & 0 deletions packages/docs/components/MapboxMap/js-api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
title: MapboxMap JS API
---

# JS API
10 changes: 10 additions & 0 deletions packages/docs/components/MapboxMap/stories/map/app.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
@import 'https://esm.sh/mapbox-gl@3.13.0/dist/mapbox-gl.css';

html.dark {
background-color: #222;
color: #eee;
}

body {
padding: 1rem;
}
13 changes: 13 additions & 0 deletions packages/docs/components/MapboxMap/stories/map/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Base, createApp } from '@studiometa/js-toolkit';
import { MapboxMap } from '@studiometa/ui';

class App extends Base {
static config = {
name: 'App',
components: {
MapboxMap,
},
};
}

createApp(App);
6 changes: 6 additions & 0 deletions packages/docs/components/MapboxMap/stories/map/app.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<div data-component="MapboxMap"
data-option-access-token="pk.eyJ1IjoiYWdlbmNlc3R1ZGlvbWV0YSIsImEiOiJjbTZxZXZidzYxaXR3MmtzaG5qNzc3NGxqIn0.c_YVpXQptiZtOgzj_5jvaw"
class="w-full max-w-4xl h-96 grid grid-cols-1 grid-rows-1">
<div data-ref="container"></div>
<template data-component="MapboxNavigationControl"></template>
</div>
10 changes: 10 additions & 0 deletions packages/docs/components/MapboxMap/stories/markers/app.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
@import 'https://esm.sh/mapbox-gl@3.13.0/dist/mapbox-gl.css';

html.dark {
background-color: #222;
color: #eee;
}

body {
padding: 1rem;
}
51 changes: 51 additions & 0 deletions packages/docs/components/MapboxMap/stories/markers/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Base, createApp } from '@studiometa/js-toolkit';
import {
createElement,
randomInt,
randomItem,
} from '@studiometa/js-toolkit/utils';
import { MapboxMap } from '@studiometa/ui';

class App extends Base {
static config = {
name: 'App',
refs: ['add', 'remove'],
components: {
MapboxMap,
},
};

onAddClick() {
this.$children.MapboxMap[0].$el.append(
createElement(
'template',
{
dataComponent: 'MapboxMarker',
dataOptionLngLat: JSON.stringify([
randomInt(-50, 50),
randomInt(-50, 50),
]),
},
[
createElement(
'template',
{
dataComponent: 'MapboxPopup',
},
'Hello world',
),
],
),
);
this.$children.MapboxMap[0].$update();
}

onRemoveClick() {
const marker = randomItem(
this.$children.MapboxMap[0].$children.MapboxMarker,
);
marker?.$el.remove();
}
}

createApp(App);
37 changes: 37 additions & 0 deletions packages/docs/components/MapboxMap/stories/markers/app.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<div data-component="MapboxMap"
data-option-access-token="pk.eyJ1IjoiYWdlbmNlc3R1ZGlvbWV0YSIsImEiOiJjbTZxZXZidzYxaXR3MmtzaG5qNzc3NGxqIn0.c_YVpXQptiZtOgzj_5jvaw"
class="w-full max-w-4xl h-96 grid grid-cols-1 grid-rows-1">
<div data-ref="container"></div>
<template data-component="MapboxMarker"
data-option-lng-lat="[10,0]">
<template data-component="MapboxPopup">
Hello world
</template>
</template>
</div>

<div class="flex gap-4 mt-4">
{{
include(
'@ui/Button/StyledButton.twig',
{
label: 'Add marker',
attr: {
data_ref: 'add'
}
}
)
}}

{{
include(
'@ui/Button/StyledButton.twig',
{
label: 'Remove marker',
attr: {
data_ref: 'remove'
}
}
)
}}
</div>
2 changes: 2 additions & 0 deletions packages/playground/meta.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ export default defineWebpackConfig({
'@studiometa/ui': '/-/play/static/ui/index.js',
deepmerge: '/-/play/static/deepmerge.js',
morphdom: '/-/play/static/morphdom.js',
'mapbox-gl': '/-/play/static/mapbox-gl.js',
'@mapbox/mapbox-gl-geocoder': '/-/play/static/mapbox-gl-geocoder.js',
},
defaults: {
html: `{% html_element 'span' with { class: 'dark:text-white font-bold border-b-2 border-current' } %}
Expand Down
3 changes: 3 additions & 0 deletions packages/playground/static/mapbox-gl-geocoder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import GeocoderControl from '@mapbox/mapbox-gl-geocoder';

export default GeocoderControl;
4 changes: 4 additions & 0 deletions packages/playground/static/mapbox-gl.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import mapboxgl from 'mapbox-gl';
export { Map, Marker, Popup, NavigationControl, GeolocateControl } from 'mapbox-gl';

export default mapboxgl;
6 changes: 6 additions & 0 deletions packages/tests/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ test('components exports', () => {
"Hoverable",
"LargeText",
"LazyInclude",
"MapboxGeocoder",
"MapboxGeolocateControl",
"MapboxMap",
"MapboxMarker",
"MapboxNavigationControl",
"MapboxPopup",
"Menu",
"MenuBtn",
"MenuList",
Expand Down
18 changes: 18 additions & 0 deletions packages/ui/MapboxMap/AbstractMapboxMapChild.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Base, BaseProps } from '@studiometa/js-toolkit';
import type { MapboxMap } from './MapboxMap.js';

export interface AbstractMapboxMapChildProps extends BaseProps {
$parent: MapboxMap;
}

export class AbstractMapboxMapChild<T extends BaseProps = BaseProps> extends Base<T & AbstractMapboxMapChildProps> {
get map() {
const { map } = this.$parent;

if (!map) {
this.$warn('Can not find the parent map, does this component has a parent MapboxMap component?');
}

return map;
}
}
75 changes: 75 additions & 0 deletions packages/ui/MapboxMap/MapboxGeocoder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { type BaseProps, type BaseConfig } from '@studiometa/js-toolkit';
import { AbstractMapboxMapChild, AbstractMapboxMapChildProps } from './AbstractMapboxMapChild.js';
import GeocoderControl from '@mapbox/mapbox-gl-geocoder';
import * as mapboxgl from 'mapbox-gl';
import type { Map } from 'mapbox-gl';

export interface MapboxGeocoderProps extends AbstractMapboxMapChildProps {
$options: {
/**
* Wether to add the geocoder to the map or to the component's root element.
*/
addToMap: boolean;
/**
* All MapboxGeocoder options, except the non serializable ones.
* @see https://github.com/mapbox/mapbox-gl-geocoder/blob/master/API.md#parameters
*/
options: Omit<
GeocoderControl.GeocoderOptions,
'filter' | 'externalGeocoder' | 'render' | 'getItemValue' | 'localGeocoder'
>;
};
}

/**
* Add a navigation control to the map.
* @see https://ui.studiometa.dev/-/components/MapboxMap/
*/
export class MapboxGeocoder<T extends BaseProps = BaseProps> extends AbstractMapboxMapChild<
T & MapboxGeocoderProps
> {
static config: BaseConfig = {
name: 'MapboxGeocoder',
options: {
addToMap: Boolean,
options: Object,
},
};

__control: GeocoderControl;

get control() {
if (!this.__control) {
const { options } = this.$options;
options.mapboxgl = mapboxgl;
if (!options.accessToken) {
options.accessToken = this.$parent.$options.accessToken;
}
this.__control = new GeocoderControl(options);
}

return this.__control;
}

/**
* Target element for the geocoder.
*/
get target(): Map | HTMLElement | string {
return this.$options.addToMap ? this.map : this.$el;
}

/**
* Mounted hook.
*/
mounted() {
this.control.addTo(this.target);
}

/**
* Destroyed hook.
*/
destroyed() {
this.map.removeControl(this.control);
this.__control = undefined;
}
}
Loading
Loading