💄 Place creation inputs placeholder and tooltip for hidden features

This commit is contained in:
itskovacs 2025-07-19 12:05:22 +02:00
parent 606a78a95f
commit 86a4a05e9c
3 changed files with 87 additions and 12 deletions

View File

@ -6,7 +6,7 @@
</p-floatlabel> </p-floatlabel>
<p-floatlabel variant="in"> <p-floatlabel variant="in">
<input id="lat" formControlName="lat" pInputText fluid /> <input id="lat" formControlName="lat" pInputText fluid placeholder="Lat or Lat, Lng" />
<label for="lat">Latitude</label> <label for="lat">Latitude</label>
</p-floatlabel> </p-floatlabel>
@ -15,10 +15,16 @@
<label for="lng">Longitude</label> <label for="lng">Longitude</label>
</p-floatlabel> </p-floatlabel>
<p-floatlabel variant="in" class="col-span-2 md:col-span-3"> <p-inputgroup class="col-span-2 md:col-span-3">
<input id="place" formControlName="place" pInputText fluid /> <p-floatlabel variant="in">
<label for="place">Place</label> <input id="place" formControlName="place" pInputText fluid placeholder="" />
</p-floatlabel> <label for="place">Place</label>
</p-floatlabel>
<p-inputgroup-addon>
<p-button icon="pi pi-info-circle" [pTooltip]="placeInputTooltip" [escape]="false" tooltipEvent="focus"
severity="secondary" class="h-full" />
</p-inputgroup-addon>
</p-inputgroup>
<p-floatlabel variant="in" class="col-span-2 md:col-span-1"> <p-floatlabel variant="in" class="col-span-2 md:col-span-1">
<p-select [options]="(categories$ | async) || []" optionValue="id" optionLabel="name" <p-select [options]="(categories$ | async) || []" optionValue="id" optionLabel="name"

View File

@ -0,0 +1,21 @@
.p-floatlabel:not(:has(input:focus)) input::placeholder,
.p-floatlabel:not(:has(input.p-filled)) input::placeholder,
.p-floatlabel:not(:has(input:-webkit-autofill)) input::placeholder,
.p-floatlabel:not(:has(textarea:focus)) textarea::placeholder,
.p-floatlabel:not(:has(textarea.p-filled)) textarea::placeholder,
.p-floatlabel:not(:has(.p-inputwrapper-focus)) input::placeholder,
.p-floatlabel:not(:has(.p-inputwrapper-filled)) input::placeholder {
color: transparent !important;
}
.p-floatlabel:has(input:focus) input::placeholder,
.p-floatlabel:has(input:-webkit-autofill) input::placeholder,
.p-floatlabel:has(textarea:focus) input::placeholder,
.p-floatlabel:has(.p-inputwrapper-focus) input::placeholder {
color: var(--p-inputtext-placeholder-color) !important;
}
.p-tooltip > .p-tooltip-text {
width: 350px !important;
background: red;
}

View File

@ -1,5 +1,10 @@
import { Component } from "@angular/core"; import { Component } from "@angular/core";
import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from "@angular/forms"; import {
FormBuilder,
FormGroup,
ReactiveFormsModule,
Validators,
} from "@angular/forms";
import { ButtonModule } from "primeng/button"; import { ButtonModule } from "primeng/button";
import { DynamicDialogConfig, DynamicDialogRef } from "primeng/dynamicdialog"; import { DynamicDialogConfig, DynamicDialogRef } from "primeng/dynamicdialog";
import { FloatLabelModule } from "primeng/floatlabel"; import { FloatLabelModule } from "primeng/floatlabel";
@ -8,11 +13,14 @@ import { SelectModule } from "primeng/select";
import { TextareaModule } from "primeng/textarea"; import { TextareaModule } from "primeng/textarea";
import { Observable } from "rxjs"; import { Observable } from "rxjs";
import { AsyncPipe } from "@angular/common"; import { AsyncPipe } from "@angular/common";
import { InputGroupModule } from "primeng/inputgroup";
import { InputGroupAddonModule } from "primeng/inputgroupaddon";
import { ApiService } from "../../services/api.service"; import { ApiService } from "../../services/api.service";
import { UtilsService } from "../../services/utils.service"; import { UtilsService } from "../../services/utils.service";
import { FocusTrapModule } from "primeng/focustrap"; import { FocusTrapModule } from "primeng/focustrap";
import { Category, Place } from "../../types/poi"; import { Category, Place } from "../../types/poi";
import { CheckboxModule } from "primeng/checkbox"; import { CheckboxModule } from "primeng/checkbox";
import { TooltipModule } from "primeng/tooltip";
@Component({ @Component({
selector: "app-place-create-modal", selector: "app-place-create-modal",
@ -23,6 +31,9 @@ import { CheckboxModule } from "primeng/checkbox";
SelectModule, SelectModule,
ReactiveFormsModule, ReactiveFormsModule,
TextareaModule, TextareaModule,
InputGroupModule,
InputGroupAddonModule,
TooltipModule,
CheckboxModule, CheckboxModule,
AsyncPipe, AsyncPipe,
FocusTrapModule, FocusTrapModule,
@ -37,12 +48,15 @@ export class PlaceCreateModalComponent {
previous_image_id: number | null = null; previous_image_id: number | null = null;
previous_image: string | null = null; previous_image: string | null = null;
placeInputTooltip: string =
"<div class='text-center'>You can paste a Google Maps Place link to fill <i>Name</i>, <i>Place</i>, <i>Lat</i>, <i>Lng</i>.</div>\n<div class='text-sm text-center'>https://google.com/maps/place/XXX</div>\n<div class='text-xs text-center'>Either « click » on a point of interest or « search » for it (eg: British Museum) and copy the URL</div>\n<div class='text-xs text-center'>Warning: there is often a slight offset from the actual coordinates.</div>";
constructor( constructor(
private ref: DynamicDialogRef, private ref: DynamicDialogRef,
private apiService: ApiService, private apiService: ApiService,
private utilsService: UtilsService, private utilsService: UtilsService,
private fb: FormBuilder, private fb: FormBuilder,
private config: DynamicDialogConfig private config: DynamicDialogConfig,
) { ) {
this.categories$ = this.apiService.getCategories(); this.categories$ = this.apiService.getCategories();
@ -50,11 +64,25 @@ export class PlaceCreateModalComponent {
id: -1, id: -1,
name: ["", Validators.required], name: ["", Validators.required],
place: ["", { validators: Validators.required, updateOn: "blur" }], place: ["", { validators: Validators.required, updateOn: "blur" }],
lat: ["", { validators: [Validators.required, Validators.pattern("-?(90(\\.0+)?|[1-8]?\\d(\\.\\d+)?)")] }], lat: [
"",
{
validators: [
Validators.required,
Validators.pattern("-?(90(\\.0+)?|[1-8]?\\d(\\.\\d+)?)"),
],
updateOn: "blur",
},
],
lng: [ lng: [
"", "",
{ {
validators: [Validators.required, Validators.pattern("-?(180(\\.0+)?|1[0-7]\\d(\\.\\d+)?|[1-9]?\\d(\\.\\d+)?)")], validators: [
Validators.required,
Validators.pattern(
"-?(180(\\.0+)?|1[0-7]\\d(\\.\\d+)?|[1-9]?\\d(\\.\\d+)?)",
),
],
}, },
], ],
category: [null, Validators.required], category: [null, Validators.required],
@ -85,8 +113,27 @@ export class PlaceCreateModalComponent {
next: (value: string) => { next: (value: string) => {
if (/\-?\d+\.\d+,\s\-?\d+\.\d+/.test(value)) { if (/\-?\d+\.\d+,\s\-?\d+\.\d+/.test(value)) {
let [lat, lng] = value.split(", "); let [lat, lng] = value.split(", ");
this.placeForm.get("lat")?.setValue(parseFloat(lat).toFixed(5)); const latLength = lat.split(".")[1].length;
this.placeForm.get("lng")?.setValue(parseFloat(lng).toFixed(5)); const lngLength = lng.split(".")[1].length;
const latControl = this.placeForm.get("lat");
const lngControl = this.placeForm.get("lng");
latControl?.setValue(
parseFloat(lat).toFixed(latLength > 5 ? 5 : latLength),
{
emitEvent: false,
},
);
lngControl?.setValue(
parseFloat(lng).toFixed(lngLength > 5 ? 5 : lngLength),
{
emitEvent: false,
},
);
lngControl?.markAsDirty();
lngControl?.updateValueAndValidity();
} }
}, },
}); });
@ -119,7 +166,8 @@ export class PlaceCreateModalComponent {
this.placeForm.get("lat")?.setValue(lat); this.placeForm.get("lat")?.setValue(lat);
this.placeForm.get("lng")?.setValue(lng); this.placeForm.get("lng")?.setValue(lng);
if (!this.placeForm.get("name")?.value) this.placeForm.get("name")?.setValue(place); if (!this.placeForm.get("name")?.value)
this.placeForm.get("name")?.setValue(place);
} }
onFileSelected(event: Event) { onFileSelected(event: Event) {