xref: /webtrees/resources/views/admin/location-edit.phtml (revision 04a72e8f85a8cc0f85d1c2f6c21b8274b75a1554)
1<?php
2
3use Fisharebest\Webtrees\Http\RequestHandlers\MapDataList;
4use Fisharebest\Webtrees\Http\RequestHandlers\MapDataSave;
5use Fisharebest\Webtrees\I18N;
6use Fisharebest\Webtrees\PlaceLocation;
7use Fisharebest\Webtrees\View;
8
9/**
10 * @var array<string,string> $breadcrumbs
11 * @var string               $latitude
12 * @var object               $leaflet_config
13 * @var string               $longitude
14 * @var PlaceLocation        $location
15 * @var array<array<float>>  $map_bounds
16 * @var array<float>         $marker_position
17 * @var PlaceLocation        $parent
18 * @var string               $title
19 */
20
21?>
22
23<?= view('components/breadcrumbs', ['links' => $breadcrumbs]) ?>
24
25<h1><?= $title ?></h1>
26
27<div class="row form-group mb-3">
28    <div class="col-sm-12">
29        <div id="wt-map" class="wt-ajax-load col-sm-12 wt-map-admin" dir="ltr"></div>
30    </div>
31</div>
32
33<form method="post" action="<?= e(route(MapDataSave::class)) ?>">
34    <?= csrf_field() ?>
35    <input type="hidden" name="parent_id" value="<?= e($parent->id()) ?>">
36    <input type="hidden" name="place_id" value="<?= e($location->id()) ?>">
37
38    <div class="row form-group mb-3">
39        <label class="col-form-label col-sm-1" for="new_place_name">
40            <?= I18N::translate('Place') ?>
41        </label>
42        <div class="col-sm-3">
43            <input type="text" id="new_place_name" name="new_place_name" value="<?= e($location->locationName()) ?>" class="form-control" required="required" maxlength="120" pattern="[^,]+" dir="auto">
44        </div>
45    </div>
46
47    <div class="row form-group mb-3">
48        <label class="col-form-label col-sm-1" for="new_place_lati">
49            <?= I18N::translate('Latitude') ?>
50        </label>
51        <div class="col-sm-3">
52            <div class="input-group">
53                <input type="text" dir="ltr" id="new_place_lati" class="editable form-control" name="new_place_lati" placeholder="<?= I18N::translate('degrees') ?>" value="<?= e($latitude) ?>">
54            </div>
55        </div>
56    </div>
57
58    <div class="row form-group mb-3">
59        <label class="col-form-label col-sm-1" for="new_place_long">
60            <?= I18N::translate('Longitude') ?>
61        </label>
62        <div class="col-sm-3">
63            <div class="input-group">
64                <input type="text" dir="ltr" id="new_place_long" class="editable form-control" name="new_place_long" placeholder="<?= I18N::translate('degrees') ?>" value="<?= e($longitude) ?>">
65            </div>
66        </div>
67    </div>
68
69    <div class="row form-group mb-3">
70        <div class="col-sm-10 offset-sm-1">
71            <button class="btn btn-primary" type="submit">
72                <?= /* I18N: A button label. */
73                I18N::translate('save')
74                ?>
75            </button>
76            <a class="btn btn-secondary" href="<?= e(route(MapDataList::class, ['parent_id' => $parent->id()])) ?>">
77                <?= I18N::translate('cancel') ?>
78            </a>
79        </div>
80    </div>
81</form>
82
83<?php View::push('styles') ?>
84<style>
85    .wt-map-admin {
86        height: 55vh;
87        border: 1px solid darkGrey
88    }
89</style>
90<?php View::endpush() ?>
91
92<?php View::push('javascript') ?>
93<script>
94  'use strict';
95
96  (function () {
97    const config = <?= json_encode($leaflet_config, JSON_THROW_ON_ERROR) ?>;
98    const add_place = <?= json_encode($location->id() === null, JSON_THROW_ON_ERROR) ?>;
99
100    let new_place_lati = document.getElementById('new_place_lati');
101    let new_place_long = document.getElementById('new_place_long');
102
103    // postcss_image_inliner breaks the autodetection of image paths.
104    L.Icon.Default.imagePath = <?= json_encode(asset('css/images/'), JSON_THROW_ON_ERROR) ?>;
105
106    // draggable marker
107    let marker = L.marker(<?= json_encode($marker_position, JSON_THROW_ON_ERROR) ?>, {
108      draggable: true,
109    })
110    .on('dragend', function () {
111      let coords = marker.getLatLng();
112      map.panTo(coords);
113      new_place_lati.value = Number(coords.lat).toFixed(5);
114      new_place_long.value = Number(coords.lng).toFixed(5);
115    });
116
117    //reset map to initial state
118    let resetControl = L.Control.extend({
119      options: {
120          position: 'topleft'
121      },
122      onAdd: function (map) {
123        let container = L.DomUtil.create('div', 'leaflet-bar leaflet-control leaflet-control-custom');
124        container.onclick = function () {
125          map.fitBounds(<?= json_encode($map_bounds, JSON_THROW_ON_ERROR) ?>, {padding: [50, 30]});
126          marker.setLatLng(<?= json_encode([$location->latitude(), $location->longitude()], JSON_THROW_ON_ERROR) ?>);
127          document.querySelector('form').reset();
128
129          return false;
130        };
131        let reset = config.i18n.reset;
132        let anchor = L.DomUtil.create('a', 'leaflet-control-reset', container);
133        anchor.setAttribute('aria-label', reset);
134        anchor.href = '#';
135        anchor.title = reset;
136        anchor.role = 'button';
137        let image = L.DomUtil.create('i', 'fas fa-redo', anchor);
138        image.alt = reset;
139
140        return container;
141      }
142    });
143
144    // Geocoder (place lookup)
145    let geocoder = new L.Control.geocoder({
146      position: 'bottomleft',
147      defaultMarkGeocode: false,
148      expand: 'click',
149      showResultIcons: true,
150      query: <?= json_encode($location->locationName(), JSON_THROW_ON_ERROR) ?>,
151      placeholder: <?= json_encode(I18N::translate('Place'), JSON_THROW_ON_ERROR) ?>,
152      errorMessage: <?= json_encode(I18N::translate('Nothing found.'), JSON_THROW_ON_ERROR) ?>,
153      iconLabel: <?= json_encode(I18N::translate('Search'), JSON_THROW_ON_ERROR) ?>
154    })
155    .on('markgeocode', function (result) {
156      let coords = result.geocode.center;
157      let place = result.geocode.name.split(',', 1).toString();
158      marker.setLatLng(coords);
159      map.panTo(coords);
160      if (add_place) {
161        document.getElementById('new_place_name').value = place
162      }
163      new_place_lati.value = Number(coords.lat).toFixed(5);
164      new_place_long.value = Number(coords.lng).toFixed(5);
165    });
166
167    const map = webtrees.buildLeafletJsMap('wt-map', config)
168      .addControl(new resetControl())
169      .addControl(geocoder)
170      .addLayer(marker)
171      .fitBounds(<?= json_encode($map_bounds, JSON_THROW_ON_ERROR) ?>, {padding: [50, 30]})
172      .on('zoomend', function () {
173        if (!map.getBounds().contains(marker.getLatLng())) {
174          map.panTo(marker.getLatLng());
175        }
176    });
177
178    document.querySelectorAll('.editable').forEach((element) => {
179      element.addEventListener('change', () => {
180        let lat = new_place_lati.value;
181        let lng = new_place_long.value;
182        marker.setLatLng([lat, lng]);
183        map.panTo([lat, lng]);
184      });
185    });
186
187    window.onload = function() {
188      let icon = document.querySelector('.leaflet-control-geocoder-icon');
189      icon.setAttribute('title', <?= json_encode(I18N::translate('Search'), JSON_THROW_ON_ERROR) ?>);
190    }
191  })();
192</script>
193<?php View::endpush() ?>
194