@ -0,0 +1,143 @@ |
|||||
|
///
|
||||
|
/// Copyright © 2016-2024 The Thingsboard Authors
|
||||
|
///
|
||||
|
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
/// you may not use this file except in compliance with the License.
|
||||
|
/// You may obtain a copy of the License at
|
||||
|
///
|
||||
|
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
///
|
||||
|
/// Unless required by applicable law or agreed to in writing, software
|
||||
|
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
/// See the License for the specific language governing permissions and
|
||||
|
/// limitations under the License.
|
||||
|
///
|
||||
|
|
||||
|
import tinycolor from 'tinycolor2'; |
||||
|
import { MatIconRegistry } from '@angular/material/icon'; |
||||
|
import { DomSanitizer } from '@angular/platform-browser'; |
||||
|
import { Observable, of, switchMap } from 'rxjs'; |
||||
|
import { catchError, map, take } from 'rxjs/operators'; |
||||
|
import { isSvgIcon, splitIconName } from '@shared/models/icon.models'; |
||||
|
import { Element, Text, G } from '@svgdotjs/svg.js'; |
||||
|
|
||||
|
export enum MarkerShape { |
||||
|
markerShape1 = 'markerShape1', |
||||
|
markerShape2 = 'markerShape2', |
||||
|
markerShape3 = 'markerShape3', |
||||
|
markerShape4 = 'markerShape4', |
||||
|
markerShape5 = 'markerShape5', |
||||
|
markerShape6 = 'markerShape6', |
||||
|
markerShape7 = 'markerShape7', |
||||
|
markerShape8 = 'markerShape8', |
||||
|
markerShape9 = 'markerShape9', |
||||
|
markerShape10 = 'markerShape10' |
||||
|
} |
||||
|
|
||||
|
export const markerShapeMap = new Map<MarkerShape, string>( |
||||
|
[ |
||||
|
[MarkerShape.markerShape1, '/assets/markers/shape1.svg'], |
||||
|
[MarkerShape.markerShape2, '/assets/markers/shape2.svg'], |
||||
|
[MarkerShape.markerShape3, '/assets/markers/shape3.svg'], |
||||
|
[MarkerShape.markerShape4, '/assets/markers/shape4.svg'], |
||||
|
[MarkerShape.markerShape5, '/assets/markers/shape5.svg'], |
||||
|
[MarkerShape.markerShape6, '/assets/markers/shape6.svg'], |
||||
|
[MarkerShape.markerShape7, '/assets/markers/shape7.svg'], |
||||
|
[MarkerShape.markerShape8, '/assets/markers/shape8.svg'], |
||||
|
[MarkerShape.markerShape9, '/assets/markers/shape9.svg'], |
||||
|
[MarkerShape.markerShape10, '/assets/markers/shape10.svg'] |
||||
|
] |
||||
|
); |
||||
|
|
||||
|
const createColorMarkerShape = (iconRegistry: MatIconRegistry, domSanitizer: DomSanitizer, shape: MarkerShape, color: tinycolor.Instance): Observable<SVGElement> => { |
||||
|
const markerAssetUrl = markerShapeMap.get(shape); |
||||
|
const safeUrl = domSanitizer.bypassSecurityTrustResourceUrl(markerAssetUrl); |
||||
|
return iconRegistry.getSvgIconFromUrl(safeUrl).pipe( |
||||
|
map((svgElement) => { |
||||
|
const colorElements = Array.from(svgElement.getElementsByClassName('marker-color')); |
||||
|
colorElements.forEach(el => { |
||||
|
el.setAttribute('fill', '#'+color.toHex()); |
||||
|
}); |
||||
|
const strokeElements = Array.from(svgElement.getElementsByClassName('marker-stroke')); |
||||
|
strokeElements.forEach(el => { |
||||
|
el.setAttribute('stroke', '#'+color.toHex()); |
||||
|
}); |
||||
|
return svgElement; |
||||
|
}) |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
export const createColorMarkerShapeURI = (iconRegistry: MatIconRegistry, domSanitizer: DomSanitizer, shape: MarkerShape, color: tinycolor.Instance): Observable<string> => { |
||||
|
return createColorMarkerShape(iconRegistry, domSanitizer, shape, color).pipe( |
||||
|
map((svgElement) => { |
||||
|
const svg = svgElement.outerHTML; |
||||
|
return 'data:image/svg+xml;base64,' + btoa(svg); |
||||
|
}) |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
const createIconElement = (iconRegistry: MatIconRegistry, icon: string, size: number, color: tinycolor.Instance): Observable<Element> => { |
||||
|
const isSvg = isSvgIcon(icon); |
||||
|
const iconColor = tinycolor.mix(color, tinycolor('rgba(0,0,0,0.38)')); |
||||
|
if (isSvg) { |
||||
|
const [namespace, iconName] = splitIconName(icon); |
||||
|
return iconRegistry |
||||
|
.getNamedSvgIcon(iconName, namespace) |
||||
|
.pipe( |
||||
|
take(1), |
||||
|
map((svgElement) => { |
||||
|
const element = new Element(svgElement.firstChild); |
||||
|
element.fill('#'+iconColor.toHex()); |
||||
|
//const box = element.bbox();
|
||||
|
const scale = size / 24;//box.height;
|
||||
|
element.scale(scale); |
||||
|
return element; |
||||
|
}), |
||||
|
catchError(() => of(null)) |
||||
|
); |
||||
|
} else { |
||||
|
const iconName = splitIconName(icon)[1]; |
||||
|
const textElement = new Text(document.createElementNS('http://www.w3.org/2000/svg', 'text')); |
||||
|
const fontSetClasses = ( |
||||
|
iconRegistry.getDefaultFontSetClass() |
||||
|
).filter(className => className.length > 0); |
||||
|
fontSetClasses.forEach(className => textElement.addClass(className)); |
||||
|
textElement.font({size: `${size}px`}); |
||||
|
textElement.attr({ |
||||
|
style: `font-size: ${size}px`, |
||||
|
'text-anchor': 'start' |
||||
|
}); |
||||
|
textElement.fill('#'+iconColor.toHex()); |
||||
|
const tspan = textElement.tspan(iconName); |
||||
|
tspan.attr({ |
||||
|
'dominant-baseline': 'hanging' |
||||
|
}); |
||||
|
return of(textElement); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export const createColorMarkerIconElement = (iconRegistry: MatIconRegistry, domSanitizer: DomSanitizer, icon: string, color: tinycolor.Instance): Observable<SVGElement> => { |
||||
|
return createColorMarkerShape(iconRegistry, domSanitizer, MarkerShape.markerShape6, color).pipe( |
||||
|
switchMap((svgElement) => { |
||||
|
return createIconElement(iconRegistry, icon, 12, color).pipe( |
||||
|
map((iconElement) => { |
||||
|
let elements = svgElement.getElementsByClassName('marker-icon-container'); |
||||
|
if (iconElement && elements.length) { |
||||
|
const iconContainer = new G(elements[0] as SVGGElement); |
||||
|
iconContainer.add(iconElement); |
||||
|
const box = iconElement.bbox(); |
||||
|
iconElement.translate(-box.cx, -box.cy); |
||||
|
} |
||||
|
elements = svgElement.getElementsByClassName('marker-icon-background'); |
||||
|
if (elements.length) { |
||||
|
(elements[0] as SVGGElement).style.display = ''; |
||||
|
} |
||||
|
return svgElement; |
||||
|
}) |
||||
|
); |
||||
|
}) |
||||
|
); |
||||
|
} |
||||
|
|
||||
@ -0,0 +1,34 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
Copyright © 2016-2024 The Thingsboard Authors |
||||
|
|
||||
|
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
you may not use this file except in compliance with the License. |
||||
|
You may obtain a copy of the License at |
||||
|
|
||||
|
http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
|
||||
|
Unless required by applicable law or agreed to in writing, software |
||||
|
distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
See the License for the specific language governing permissions and |
||||
|
limitations under the License. |
||||
|
|
||||
|
--> |
||||
|
<div class="flex flex-row items-center justify-start gap-2" [formGroup]="shapeSettingsFormGroup"> |
||||
|
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic"> |
||||
|
<input matInput formControlName="size" type="number" min="1" step="1" placeholder="{{ 'widget-config.set' | translate }}"> |
||||
|
<div matSuffix>px</div> |
||||
|
</mat-form-field> |
||||
|
<button type="button" |
||||
|
mat-stroked-button |
||||
|
class="tb-box-button" |
||||
|
[disabled]="disabled" |
||||
|
#matButton |
||||
|
(click)="openShapePopup($event, matButton)"> |
||||
|
<img *ngIf="markerType === MarkerType.shape" matButtonIcon width="24px" height="24px" style="object-fit: contain;" [src]="shapePreview$ | async" [class.disabled]="disabled"/> |
||||
|
<div *ngIf="markerType === MarkerType.icon" matButtonIcon style="object-fit: contain; width: 24px; height: 24px;" [innerHTML]="iconPreview$ | async" [class.disabled]="disabled"> |
||||
|
</div> |
||||
|
</button> |
||||
|
<tb-data-layer-color-settings formControlName="color"></tb-data-layer-color-settings> |
||||
|
</div> |
||||
@ -0,0 +1,204 @@ |
|||||
|
///
|
||||
|
/// Copyright © 2016-2024 The Thingsboard Authors
|
||||
|
///
|
||||
|
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
/// you may not use this file except in compliance with the License.
|
||||
|
/// You may obtain a copy of the License at
|
||||
|
///
|
||||
|
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
///
|
||||
|
/// Unless required by applicable law or agreed to in writing, software
|
||||
|
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
/// See the License for the specific language governing permissions and
|
||||
|
/// limitations under the License.
|
||||
|
///
|
||||
|
|
||||
|
import { |
||||
|
ChangeDetectorRef, |
||||
|
Component, |
||||
|
DestroyRef, |
||||
|
forwardRef, |
||||
|
Input, |
||||
|
OnInit, |
||||
|
Renderer2, |
||||
|
ViewContainerRef |
||||
|
} from '@angular/core'; |
||||
|
import { |
||||
|
ControlValueAccessor, |
||||
|
NG_VALUE_ACCESSOR, |
||||
|
UntypedFormBuilder, |
||||
|
UntypedFormGroup, |
||||
|
Validators |
||||
|
} from '@angular/forms'; |
||||
|
import { MatButton } from '@angular/material/button'; |
||||
|
import { TbPopoverService } from '@shared/components/popover.service'; |
||||
|
import { MarkerIconSettings, MarkerShapeSettings, MarkerType } from '@home/components/widget/lib/maps/map.models'; |
||||
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; |
||||
|
import { Observable } from 'rxjs'; |
||||
|
import { DomSanitizer, SafeHtml, SafeUrl } from '@angular/platform-browser'; |
||||
|
import { MatIconRegistry } from '@angular/material/icon'; |
||||
|
import { |
||||
|
createColorMarkerIconElement, |
||||
|
createColorMarkerShapeURI |
||||
|
} from '@home/components/widget/lib/maps/marker-shape.models'; |
||||
|
import tinycolor from 'tinycolor2'; |
||||
|
import { map, share } from 'rxjs/operators'; |
||||
|
import { MarkerShapesComponent } from '@home/components/widget/lib/settings/common/map/marker-shapes.component'; |
||||
|
import { MaterialIconsComponent } from '@shared/components/material-icons.component'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-marker-shape-settings', |
||||
|
templateUrl: './marker-shape-settings.component.html', |
||||
|
styleUrls: [], |
||||
|
providers: [ |
||||
|
{ |
||||
|
provide: NG_VALUE_ACCESSOR, |
||||
|
useExisting: forwardRef(() => MarkerShapeSettingsComponent), |
||||
|
multi: true |
||||
|
} |
||||
|
] |
||||
|
}) |
||||
|
export class MarkerShapeSettingsComponent implements ControlValueAccessor, OnInit { |
||||
|
|
||||
|
MarkerType = MarkerType; |
||||
|
|
||||
|
@Input() |
||||
|
disabled: boolean; |
||||
|
|
||||
|
@Input() |
||||
|
markerType: MarkerType; |
||||
|
|
||||
|
modelValue: MarkerShapeSettings | MarkerIconSettings; |
||||
|
|
||||
|
public shapeSettingsFormGroup: UntypedFormGroup; |
||||
|
|
||||
|
public shapePreview$: Observable<SafeUrl>; |
||||
|
public iconPreview$: Observable<SafeHtml>; |
||||
|
|
||||
|
private propagateChange: (v: any) => void = () => { }; |
||||
|
|
||||
|
constructor(private popoverService: TbPopoverService, |
||||
|
private fb: UntypedFormBuilder, |
||||
|
private destroyRef: DestroyRef, |
||||
|
private iconRegistry: MatIconRegistry, |
||||
|
private domSanitizer: DomSanitizer, |
||||
|
private renderer: Renderer2, |
||||
|
private cd: ChangeDetectorRef, |
||||
|
private viewContainerRef: ViewContainerRef) {} |
||||
|
|
||||
|
ngOnInit(): void { |
||||
|
this.shapeSettingsFormGroup = this.fb.group({ |
||||
|
size: [null, [Validators.required, Validators.min(1)]], |
||||
|
color: [null, [Validators.required]] |
||||
|
}); |
||||
|
if (this.markerType === MarkerType.shape) { |
||||
|
this.shapeSettingsFormGroup.addControl('shape', this.fb.control(null, [Validators.required])); |
||||
|
} |
||||
|
if (this.markerType === MarkerType.icon) { |
||||
|
this.shapeSettingsFormGroup.addControl('icon', this.fb.control(null, [Validators.required])); |
||||
|
} |
||||
|
this.shapeSettingsFormGroup.valueChanges.pipe( |
||||
|
takeUntilDestroyed(this.destroyRef) |
||||
|
).subscribe(() => { |
||||
|
this.updateModel(); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
registerOnChange(fn: any): void { |
||||
|
this.propagateChange = fn; |
||||
|
} |
||||
|
|
||||
|
registerOnTouched(fn: any): void { |
||||
|
} |
||||
|
|
||||
|
setDisabledState(isDisabled: boolean): void { |
||||
|
this.disabled = isDisabled; |
||||
|
if (isDisabled) { |
||||
|
this.shapeSettingsFormGroup.disable({emitEvent: false}); |
||||
|
} else { |
||||
|
this.shapeSettingsFormGroup.enable({emitEvent: false}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
writeValue(value: MarkerShapeSettings | MarkerIconSettings): void { |
||||
|
this.modelValue = value; |
||||
|
this.shapeSettingsFormGroup.patchValue( |
||||
|
value, {emitEvent: false} |
||||
|
); |
||||
|
this.updatePreview(); |
||||
|
} |
||||
|
|
||||
|
openShapePopup($event: Event, matButton: MatButton) { |
||||
|
if ($event) { |
||||
|
$event.stopPropagation(); |
||||
|
} |
||||
|
const trigger = matButton._elementRef.nativeElement; |
||||
|
if (this.popoverService.hasPopover(trigger)) { |
||||
|
this.popoverService.hidePopover(trigger); |
||||
|
} else { |
||||
|
if (this.markerType === MarkerType.shape) { |
||||
|
const ctx: any = { |
||||
|
shape: (this.modelValue as MarkerShapeSettings).shape, |
||||
|
color: this.modelValue.color.color |
||||
|
}; |
||||
|
const markerShapesPopover = this.popoverService.displayPopover(trigger, this.renderer, |
||||
|
this.viewContainerRef, MarkerShapesComponent, 'left', true, null, |
||||
|
ctx, |
||||
|
{}, |
||||
|
{}, {}, true); |
||||
|
markerShapesPopover.tbComponentRef.instance.popover = markerShapesPopover; |
||||
|
markerShapesPopover.tbComponentRef.instance.markerShapeSelected.subscribe((shape) => { |
||||
|
markerShapesPopover.hide(); |
||||
|
this.shapeSettingsFormGroup.get('shape').patchValue( |
||||
|
shape |
||||
|
); |
||||
|
}); |
||||
|
} else if (this.markerType === MarkerType.icon) { |
||||
|
const ctx: any = { |
||||
|
selectedIcon: (this.modelValue as MarkerIconSettings).icon, |
||||
|
iconClearButton: false |
||||
|
}; |
||||
|
const materialIconsPopover = this.popoverService.displayPopover(trigger, this.renderer, |
||||
|
this.viewContainerRef, MaterialIconsComponent, 'left', true, null, |
||||
|
ctx, |
||||
|
{}, |
||||
|
{}, {}, true); |
||||
|
materialIconsPopover.tbComponentRef.instance.popover = materialIconsPopover; |
||||
|
materialIconsPopover.tbComponentRef.instance.iconSelected.subscribe((icon) => { |
||||
|
materialIconsPopover.hide(); |
||||
|
this.shapeSettingsFormGroup.get('icon').patchValue( |
||||
|
icon |
||||
|
); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private updateModel() { |
||||
|
this.modelValue = this.shapeSettingsFormGroup.getRawValue(); |
||||
|
this.propagateChange(this.modelValue); |
||||
|
this.updatePreview(); |
||||
|
} |
||||
|
|
||||
|
private updatePreview() { |
||||
|
const color = this.modelValue.color.color; |
||||
|
if (this.markerType === MarkerType.shape) { |
||||
|
const shape = (this.modelValue as MarkerShapeSettings).shape; |
||||
|
this.shapePreview$ = createColorMarkerShapeURI(this.iconRegistry, this.domSanitizer, shape, tinycolor(color)).pipe( |
||||
|
map((url) => { |
||||
|
return this.domSanitizer.bypassSecurityTrustUrl(url); |
||||
|
}), |
||||
|
share() |
||||
|
); |
||||
|
} else if (this.markerType === MarkerType.icon) { |
||||
|
const icon = (this.modelValue as MarkerIconSettings).icon; |
||||
|
this.iconPreview$ = createColorMarkerIconElement(this.iconRegistry, this.domSanitizer, icon, tinycolor(color)).pipe( |
||||
|
map((element) => { |
||||
|
return this.domSanitizer.bypassSecurityTrustHtml(element.outerHTML); |
||||
|
}), |
||||
|
share() |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,38 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
Copyright © 2016-2024 The Thingsboard Authors |
||||
|
|
||||
|
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
you may not use this file except in compliance with the License. |
||||
|
You may obtain a copy of the License at |
||||
|
|
||||
|
http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
|
||||
|
Unless required by applicable law or agreed to in writing, software |
||||
|
distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
See the License for the specific language governing permissions and |
||||
|
limitations under the License. |
||||
|
|
||||
|
--> |
||||
|
<div class="tb-marker-shapes-panel"> |
||||
|
<div class="tb-marker-shapes-title" translate>widgets.maps.data-layer.marker.marker-shapes</div> |
||||
|
<div class="flex flex-row flex-wrap gap-3"> |
||||
|
<ng-container *ngFor="let shapeInfo of shapes"> |
||||
|
<button *ngIf="shapeInfo.shape === shape" |
||||
|
class="tb-select-shape-button" |
||||
|
mat-raised-button |
||||
|
(click)="selectShape(shapeInfo.shape)" |
||||
|
type="button"> |
||||
|
<img matButtonIcon class="tb-marker-shape" [src]="shapeInfo.url$ | async"/> |
||||
|
</button> |
||||
|
<button *ngIf="shapeInfo.shape !== shape" |
||||
|
class="tb-select-shape-button" |
||||
|
mat-button |
||||
|
(click)="selectShape(shapeInfo.shape)" |
||||
|
type="button"> |
||||
|
<img matButtonIcon class="tb-marker-shape" [src]="shapeInfo.url$ | async"/> |
||||
|
</button> |
||||
|
</ng-container> |
||||
|
</div> |
||||
|
</div> |
||||
@ -0,0 +1,39 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2024 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
.tb-marker-shapes-panel { |
||||
|
width: 100%; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
gap: 16px; |
||||
|
.tb-marker-shapes-title { |
||||
|
font-size: 16px; |
||||
|
font-weight: 500; |
||||
|
line-height: 24px; |
||||
|
letter-spacing: 0.25px; |
||||
|
color: rgba(0, 0, 0, 0.87); |
||||
|
} |
||||
|
button.mat-mdc-button-base.tb-select-shape-button { |
||||
|
width: 42px; |
||||
|
min-width: 42px; |
||||
|
height: 42px; |
||||
|
padding: 4px; |
||||
|
img.tb-marker-shape { |
||||
|
width: 34px; |
||||
|
height: 34px; |
||||
|
object-fit: contain; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,84 @@ |
|||||
|
///
|
||||
|
/// Copyright © 2016-2024 The Thingsboard Authors
|
||||
|
///
|
||||
|
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
/// you may not use this file except in compliance with the License.
|
||||
|
/// You may obtain a copy of the License at
|
||||
|
///
|
||||
|
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
///
|
||||
|
/// Unless required by applicable law or agreed to in writing, software
|
||||
|
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
/// See the License for the specific language governing permissions and
|
||||
|
/// limitations under the License.
|
||||
|
///
|
||||
|
|
||||
|
import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; |
||||
|
import { PageComponent } from '@shared/components/page.component'; |
||||
|
import { TbPopoverComponent } from '@shared/components/popover.component'; |
||||
|
import { Store } from '@ngrx/store'; |
||||
|
import { AppState } from '@core/core.state'; |
||||
|
import { createColorMarkerShapeURI, MarkerShape } from '@home/components/widget/lib/maps/marker-shape.models'; |
||||
|
import { Observable } from 'rxjs'; |
||||
|
import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; |
||||
|
import { MatIconRegistry } from '@angular/material/icon'; |
||||
|
import tinycolor from 'tinycolor2'; |
||||
|
import { map, share } from 'rxjs/operators'; |
||||
|
|
||||
|
interface MarkerShapeInfo { |
||||
|
shape: MarkerShape; |
||||
|
url$: Observable<SafeUrl>; |
||||
|
} |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-marker-shapes', |
||||
|
templateUrl: './marker-shapes.component.html', |
||||
|
providers: [], |
||||
|
styleUrls: ['./marker-shapes.component.scss'], |
||||
|
encapsulation: ViewEncapsulation.None |
||||
|
}) |
||||
|
export class MarkerShapesComponent extends PageComponent implements OnInit { |
||||
|
|
||||
|
@Input() |
||||
|
shape: MarkerShape; |
||||
|
|
||||
|
@Input() |
||||
|
color: string; |
||||
|
|
||||
|
@Input() |
||||
|
popover: TbPopoverComponent<MarkerShapesComponent>; |
||||
|
|
||||
|
@Output() |
||||
|
markerShapeSelected = new EventEmitter<MarkerShape>(); |
||||
|
|
||||
|
shapes: MarkerShapeInfo[]; |
||||
|
|
||||
|
constructor(protected store: Store<AppState>, |
||||
|
private iconRegistry: MatIconRegistry, |
||||
|
private domSanitizer: DomSanitizer) { |
||||
|
super(store); |
||||
|
} |
||||
|
|
||||
|
ngOnInit(): void { |
||||
|
this.shapes = (Object.keys(MarkerShape) as MarkerShape[]).map((shape) => { |
||||
|
return { |
||||
|
shape, |
||||
|
url$: createColorMarkerShapeURI(this.iconRegistry, this.domSanitizer, shape, tinycolor(this.color)).pipe( |
||||
|
map((url) => { |
||||
|
return this.domSanitizer.bypassSecurityTrustUrl(url); |
||||
|
}), |
||||
|
share() |
||||
|
) |
||||
|
}; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
cancel() { |
||||
|
this.popover?.hide(); |
||||
|
} |
||||
|
|
||||
|
selectShape(shape: MarkerShape) { |
||||
|
this.markerShapeSelected.emit(shape); |
||||
|
} |
||||
|
} |
||||
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.9 KiB |