mirror of https://github.com/Squidex/squidex.git
33 changed files with 438 additions and 39 deletions
@ -0,0 +1,15 @@ |
|||||
|
<div [formGroup]="editForm"> |
||||
|
<div class="form-group row"> |
||||
|
<label for="field-editor" class="col col-3 col-form-label">Editor</label> |
||||
|
|
||||
|
<div class="col col-9"> |
||||
|
<label class="btn btn-radio" [class.active]="editForm.controls.editor.value === 'Map'"> |
||||
|
<input type="radio" class="radio-input" formControlName="editor" value="Map" /> |
||||
|
|
||||
|
<i class="icon-control-Map"></i> |
||||
|
|
||||
|
<span class="radio-label">Map</span> |
||||
|
</label> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div> |
||||
@ -0,0 +1,2 @@ |
|||||
|
@import '_vars'; |
||||
|
@import '_mixins'; |
||||
@ -0,0 +1,31 @@ |
|||||
|
/* |
||||
|
* Squidex Headless CMS |
||||
|
* |
||||
|
* @license |
||||
|
* Copyright (c) Sebastian Stehle. All rights reserved |
||||
|
*/ |
||||
|
|
||||
|
import { Component, Input, OnInit } from '@angular/core'; |
||||
|
import { FormGroup, FormControl, Validators } from '@angular/forms'; |
||||
|
|
||||
|
import { GeolocationFieldPropertiesDto } from 'shared'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'sqx-geolocation-ui', |
||||
|
styleUrls: ['geolocation-ui.component.scss'], |
||||
|
templateUrl: 'geolocation-ui.component.html' |
||||
|
}) |
||||
|
export class GeolocationUIComponent implements OnInit { |
||||
|
@Input() |
||||
|
public editForm: FormGroup; |
||||
|
|
||||
|
@Input() |
||||
|
public properties: GeolocationFieldPropertiesDto; |
||||
|
|
||||
|
public ngOnInit() { |
||||
|
this.editForm.addControl('editor', |
||||
|
new FormControl(this.properties.editor, [ |
||||
|
Validators.required |
||||
|
])); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
<div [formGroup]="editForm"> |
||||
|
<div class="form-group row"> |
||||
|
<label class="col col-3 col-form-checkbox-label" for="field-required">Required</label> |
||||
|
|
||||
|
<div class="col col-6"> |
||||
|
<input type="checkbox" class="form-check-input" id="field-required" formControlName="isRequired" /> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
@ -0,0 +1,10 @@ |
|||||
|
@import '_vars'; |
||||
|
@import '_mixins'; |
||||
|
|
||||
|
.form-check-input { |
||||
|
margin: 0; |
||||
|
} |
||||
|
|
||||
|
.form-group { |
||||
|
margin-top: .5rem; |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
/* |
||||
|
* Squidex Headless CMS |
||||
|
* |
||||
|
* @license |
||||
|
* Copyright (c) Sebastian Stehle. All rights reserved |
||||
|
*/ |
||||
|
|
||||
|
import { Component, Input } from '@angular/core'; |
||||
|
import { FormGroup } from '@angular/forms'; |
||||
|
|
||||
|
import { GeolocationFieldPropertiesDto } from 'shared'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'sqx-geolocation-validation', |
||||
|
styleUrls: ['geolocation-validation.component.scss'], |
||||
|
templateUrl: 'geolocation-validation.component.html' |
||||
|
}) |
||||
|
export class GeolocationValidationComponent { |
||||
|
@Input() |
||||
|
public editForm: FormGroup; |
||||
|
|
||||
|
@Input() |
||||
|
public properties: GeolocationFieldPropertiesDto; |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
<div> |
||||
|
<div class="editor" #editor></div> |
||||
|
|
||||
|
<div> |
||||
|
<form class="form-inline" [formGroup]="geolocationForm" (submit)="updateValueByInput()"> |
||||
|
<div class="form-group latitude-group"> |
||||
|
<input type="number" class="form-control" formControlName="latitude" step="any" #dateInput /> |
||||
|
</div> |
||||
|
<div class="form-group longitude-group"> |
||||
|
<input type="number" class="form-control" formControlName="longitude" step="any" /> |
||||
|
</div> |
||||
|
<div class="form-group" [class.hidden]="!hasValue"> |
||||
|
<button type="reset" class="btn btn-link clear" (click)="reset()">Clear</button> |
||||
|
</div> |
||||
|
|
||||
|
<button type="submit" class="hidden"></button> |
||||
|
</form> |
||||
|
</div> |
||||
|
</div> |
||||
@ -0,0 +1,14 @@ |
|||||
|
@import '_mixins'; |
||||
|
@import '_vars'; |
||||
|
|
||||
|
.editor { |
||||
|
height: 30rem; |
||||
|
} |
||||
|
|
||||
|
.form-inline { |
||||
|
margin-top: .5rem; |
||||
|
} |
||||
|
|
||||
|
.latitude-group { |
||||
|
margin-right: .25rem; |
||||
|
} |
||||
@ -0,0 +1,160 @@ |
|||||
|
/* |
||||
|
* Squidex Headless CMS |
||||
|
* |
||||
|
* @license |
||||
|
* Copyright (c) Sebastian Stehle. All rights reserved |
||||
|
*/ |
||||
|
|
||||
|
import { AfterViewInit, Component, ElementRef, forwardRef, ViewChild } from '@angular/core'; |
||||
|
import { ControlValueAccessor, FormBuilder, NG_VALUE_ACCESSOR } from '@angular/forms'; |
||||
|
|
||||
|
import { ResourceLoaderService } from './../services/resource-loader.service'; |
||||
|
import { ValidatorsEx } from './validators'; |
||||
|
|
||||
|
const NOOP = () => { /* NOOP */ }; |
||||
|
|
||||
|
declare var L: any; |
||||
|
|
||||
|
export const SQX_GEOLOCATION_EDITOR_CONTROL_VALUE_ACCESSOR: any = { |
||||
|
provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => GeolocationEditorComponent), multi: true |
||||
|
}; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'sqx-geolocation-editor', |
||||
|
styleUrls: ['./geolocation-editor.component.scss'], |
||||
|
templateUrl: './geolocation-editor.component.html', |
||||
|
providers: [SQX_GEOLOCATION_EDITOR_CONTROL_VALUE_ACCESSOR] |
||||
|
}) |
||||
|
export class GeolocationEditorComponent implements ControlValueAccessor, AfterViewInit { |
||||
|
private changeCallback: (value: any) => void = NOOP; |
||||
|
private touchedCallback: () => void = NOOP; |
||||
|
private marker: any; |
||||
|
private map: any; |
||||
|
private value: any; |
||||
|
|
||||
|
public get hasValue() { |
||||
|
return !!this.value; |
||||
|
} |
||||
|
|
||||
|
public geolocationForm = |
||||
|
this.formBuilder.group({ |
||||
|
latitude: ['', |
||||
|
[ |
||||
|
ValidatorsEx.between(-90, 90) |
||||
|
]], |
||||
|
longitude: ['', |
||||
|
[ |
||||
|
ValidatorsEx.between(-180, 180) |
||||
|
]] |
||||
|
}); |
||||
|
|
||||
|
public isDisabled = false; |
||||
|
|
||||
|
@ViewChild('editor') |
||||
|
public editor: ElementRef; |
||||
|
|
||||
|
constructor( |
||||
|
private readonly resourceLoader: ResourceLoaderService, |
||||
|
private readonly formBuilder: FormBuilder |
||||
|
) { |
||||
|
} |
||||
|
|
||||
|
public writeValue(value: any) { |
||||
|
this.value = value; |
||||
|
|
||||
|
if (this.marker) { |
||||
|
this.updateMarker(true, false); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public setDisabledState(isDisabled: boolean): void { |
||||
|
this.isDisabled = isDisabled; |
||||
|
} |
||||
|
|
||||
|
public registerOnChange(fn: any) { |
||||
|
this.changeCallback = fn; |
||||
|
} |
||||
|
|
||||
|
public registerOnTouched(fn: any) { |
||||
|
this.touchedCallback = fn; |
||||
|
} |
||||
|
|
||||
|
public updateValueByInput() { |
||||
|
if (this.geolocationForm.controls['latitude'].value !== null && |
||||
|
this.geolocationForm.controls['longitude'].value !== null && |
||||
|
this.geolocationForm.valid) { |
||||
|
this.value = this.geolocationForm.value; |
||||
|
} else { |
||||
|
this.value = null; |
||||
|
} |
||||
|
|
||||
|
this.updateMarker(true, true); |
||||
|
} |
||||
|
|
||||
|
public ngAfterViewInit() { |
||||
|
this.resourceLoader.loadStyle('https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.3/leaflet.css'); |
||||
|
this.resourceLoader.loadScript('https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.3/leaflet.js').then(() => { |
||||
|
this.map = L.map(this.editor.nativeElement).fitWorld(); |
||||
|
|
||||
|
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', { |
||||
|
attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors' |
||||
|
}).addTo(this.map); |
||||
|
|
||||
|
this.map.on('click', (event: any) => { |
||||
|
this.value = { latitude: event.latlng.lat, longitude: event.latlng.lng }; |
||||
|
|
||||
|
this.updateMarker(false, true); |
||||
|
}); |
||||
|
|
||||
|
this.updateMarker(true, false); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
public reset() { |
||||
|
this.value = null; |
||||
|
|
||||
|
this.updateMarker(true, true); |
||||
|
} |
||||
|
|
||||
|
private updateMarker(zoom: boolean, fireEvent: boolean) { |
||||
|
if (this.value) { |
||||
|
if (!this.marker) { |
||||
|
this.marker = L.marker([0, 90], { draggable: true }).addTo(this.map); |
||||
|
|
||||
|
this.marker.on('drag', (event: any) => { |
||||
|
this.value = { latitude: event.latlng.lat, longitude: event.latlng.lng }; |
||||
|
}); |
||||
|
|
||||
|
this.marker.on('dragend', (event: any) => { |
||||
|
this.updateMarker(false, true); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
const latLng = L.latLng(this.value.latitude, this.value.longitude); |
||||
|
|
||||
|
if (zoom) { |
||||
|
this.map.setView(latLng, 8); |
||||
|
} else { |
||||
|
this.map.panTo(latLng); |
||||
|
} |
||||
|
|
||||
|
this.marker.setLatLng(latLng); |
||||
|
|
||||
|
this.geolocationForm.setValue(this.value, { emitEvent: false, onlySelf: false }); |
||||
|
} else { |
||||
|
if (this.marker) { |
||||
|
this.marker.removeFrom(this.map); |
||||
|
this.marker = null; |
||||
|
} |
||||
|
|
||||
|
this.map.fitWorld(); |
||||
|
|
||||
|
this.geolocationForm.reset(undefined, { emitEvent: false, onlySelf: false }); |
||||
|
} |
||||
|
|
||||
|
if (fireEvent) { |
||||
|
this.changeCallback(this.value); |
||||
|
this.touchedCallback(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Binary file not shown.
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 51 KiB |
Binary file not shown.
Binary file not shown.
Loading…
Reference in new issue