@moatamed-sce/ui-components 0.1.5 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1326 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, output, viewChild, afterNextRender, effect, ChangeDetectionStrategy, Component, inject, computed, ViewChild, ViewEncapsulation, DestroyRef, signal } from '@angular/core';
3
+ import * as i1 from '@moatamed-sce/core';
4
+ import { fixLeafletDefaultIcon, scheduleLeafletResize, SceVariantDirective, MAP_CONFIG, LanguageService, createLeafletDefaultIcon, isGeoLookupRestricted, JEDDAH_MAP_CENTER, NominatimService, GeoMapPositionValidationService, ToastService, mapPositionValidationMessage, resolveInitialCenter, SCE_MIRROR_LABEL_AS_PLACEHOLDER, DIALOG_SELECT_OVERLAY_Z_INDEX, inferInputTextDirection, BaladyGisService, BaladyGisError } from '@moatamed-sce/core';
5
+ import { TranslateService, TranslatePipe } from '@ngx-translate/core';
6
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
7
+ import * as i2 from '@angular/forms';
8
+ import { FormsModule } from '@angular/forms';
9
+ import { DomSanitizer } from '@angular/platform-browser';
10
+ import { CommonModule } from '@angular/common';
11
+ import { Select } from 'primeng/select';
12
+ import { HugeiconsIconComponent } from '@hugeicons/angular';
13
+ import { ArrowDown01Icon, AlertCircleIcon, RefreshIcon } from '@hugeicons/core-free-icons';
14
+
15
+ /** OpenStreetMap Leaflet map that lazily loads Leaflet, renders the given markers and emits map clicks and marker drag-ends; setting `interactive` to false disables panning, zooming, the zoom control and click events. */
16
+ class SceOsmLeafletMapComponent {
17
+ latitude = input(24.7136, ...(ngDevMode ? [{ debugName: "latitude" }] : /* istanbul ignore next */ []));
18
+ longitude = input(46.6753, ...(ngDevMode ? [{ debugName: "longitude" }] : /* istanbul ignore next */ []));
19
+ zoom = input(6, ...(ngDevMode ? [{ debugName: "zoom" }] : /* istanbul ignore next */ []));
20
+ markers = input([], ...(ngDevMode ? [{ debugName: "markers" }] : /* istanbul ignore next */ []));
21
+ interactive = input(true, ...(ngDevMode ? [{ debugName: "interactive" }] : /* istanbul ignore next */ []));
22
+ mapClick = output();
23
+ markerDragEnd = output();
24
+ mapEl = viewChild('mapEl', ...(ngDevMode ? [{ debugName: "mapEl" }] : /* istanbul ignore next */ []));
25
+ map = null;
26
+ markerLayer = null;
27
+ L = null;
28
+ cancelLeafletResize = null;
29
+ async initMap() {
30
+ const el = this.mapEl()?.nativeElement;
31
+ if (!el) {
32
+ return;
33
+ }
34
+ const leafletModule = await import('leaflet');
35
+ // CJS/ESM interop: in the esbuild bundle the Leaflet API lands on .default, in dev it may not.
36
+ const L = (leafletModule.default ??
37
+ leafletModule);
38
+ this.L = L;
39
+ fixLeafletDefaultIcon(L);
40
+ this.map = L.map(el, {
41
+ center: [this.latitude(), this.longitude()],
42
+ zoom: this.zoom(),
43
+ zoomControl: false,
44
+ dragging: this.interactive(),
45
+ scrollWheelZoom: false,
46
+ doubleClickZoom: this.interactive(),
47
+ touchZoom: this.interactive(),
48
+ });
49
+ if (this.interactive()) {
50
+ const isRtl = getComputedStyle(el).direction === 'rtl';
51
+ L.control.zoom({ position: isRtl ? 'bottomright' : 'bottomleft' }).addTo(this.map);
52
+ }
53
+ L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
54
+ maxZoom: 19,
55
+ }).addTo(this.map);
56
+ this.markerLayer = L.layerGroup().addTo(this.map);
57
+ if (this.interactive()) {
58
+ this.map.on('click', (e) => {
59
+ this.mapClick.emit({ lat: e.latlng.lat, lng: e.latlng.lng });
60
+ });
61
+ el.addEventListener('wheel', (e) => {
62
+ if (!this.map || !(e.ctrlKey || e.metaKey)) {
63
+ return;
64
+ }
65
+ e.preventDefault();
66
+ if (e.deltaY < 0) {
67
+ this.map.zoomIn();
68
+ }
69
+ else {
70
+ this.map.zoomOut();
71
+ }
72
+ }, { passive: false });
73
+ }
74
+ this.cancelLeafletResize?.();
75
+ this.cancelLeafletResize = scheduleLeafletResize(this.map);
76
+ this.renderMarkers(this.markers());
77
+ }
78
+ renderMarkers(markers) {
79
+ if (!this.markerLayer || !this.L) {
80
+ return;
81
+ }
82
+ this.markerLayer.clearLayers();
83
+ for (let i = 0; i < markers.length; i++) {
84
+ const m = markers[i];
85
+ const marker = this.L.marker([m.lat, m.lng], {
86
+ draggable: m.draggable ?? false,
87
+ });
88
+ if (m.popupHtml) {
89
+ marker.bindPopup(m.popupHtml);
90
+ }
91
+ if (m.draggable) {
92
+ const index = i;
93
+ marker.on('dragend', () => {
94
+ const pos = marker.getLatLng();
95
+ this.markerDragEnd.emit({ lat: pos.lat, lng: pos.lng, index });
96
+ });
97
+ }
98
+ marker.addTo(this.markerLayer);
99
+ }
100
+ }
101
+ ngOnDestroy() {
102
+ this.cancelLeafletResize?.();
103
+ this.cancelLeafletResize = null;
104
+ this.map?.remove();
105
+ this.map = null;
106
+ }
107
+ bindHost = (() => {
108
+ afterNextRender(() => {
109
+ this.initMap();
110
+ });
111
+ effect(() => {
112
+ const mkrs = this.markers();
113
+ this.renderMarkers(mkrs);
114
+ });
115
+ effect(() => {
116
+ const lat = this.latitude();
117
+ const lng = this.longitude();
118
+ const zm = this.zoom();
119
+ if (this.map) {
120
+ this.map.setView([lat, lng], zm);
121
+ }
122
+ });
123
+ })();
124
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SceOsmLeafletMapComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
125
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.17", type: SceOsmLeafletMapComponent, isStandalone: true, selector: "sce-osm-leaflet-map", inputs: { latitude: { classPropertyName: "latitude", publicName: "latitude", isSignal: true, isRequired: false, transformFunction: null }, longitude: { classPropertyName: "longitude", publicName: "longitude", isSignal: true, isRequired: false, transformFunction: null }, zoom: { classPropertyName: "zoom", publicName: "zoom", isSignal: true, isRequired: false, transformFunction: null }, markers: { classPropertyName: "markers", publicName: "markers", isSignal: true, isRequired: false, transformFunction: null }, interactive: { classPropertyName: "interactive", publicName: "interactive", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { mapClick: "mapClick", markerDragEnd: "markerDragEnd" }, host: { classAttribute: "sce-osm-leaflet-map" }, viewQueries: [{ propertyName: "mapEl", first: true, predicate: ["mapEl"], descendants: true, isSignal: true }], hostDirectives: [{ directive: i1.SceVariantDirective, inputs: ["sceVariant", "sceVariant", "sceDensity", "sceDensity"] }], ngImport: i0, template: "<div #mapEl class=\"mapRoot\"></div>\n", styles: ["@charset \"UTF-8\";:host.sce-osm-leaflet-map{display:block}.mapRoot{width:100%;height:100%;min-height:0;background:var(--color-neutral-200)}.mapRoot.mapRoot.leaflet-container{width:100%;height:100%;font-family:inherit}.mapRoot.mapRoot img.leaflet-tile,.mapRoot.mapRoot img.leaflet-marker-icon,.mapRoot.mapRoot img.leaflet-marker-shadow{max-width:none;max-height:none}.mapRoot.mapRoot .leaflet-control-attribution,.mapRoot.mapRoot .leaflet-control-container.leaflet-bottom{display:none}.mapRoot .leaflet-pane,.mapRoot .leaflet-map-pane,.mapRoot .leaflet-tile-pane{width:100%;height:100%}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
126
+ }
127
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SceOsmLeafletMapComponent, decorators: [{
128
+ type: Component,
129
+ args: [{ hostDirectives: [{ directive: SceVariantDirective, inputs: ['sceVariant', 'sceDensity'] }], host: { class: 'sce-osm-leaflet-map' }, selector: 'sce-osm-leaflet-map', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div #mapEl class=\"mapRoot\"></div>\n", styles: ["@charset \"UTF-8\";:host.sce-osm-leaflet-map{display:block}.mapRoot{width:100%;height:100%;min-height:0;background:var(--color-neutral-200)}.mapRoot.mapRoot.leaflet-container{width:100%;height:100%;font-family:inherit}.mapRoot.mapRoot img.leaflet-tile,.mapRoot.mapRoot img.leaflet-marker-icon,.mapRoot.mapRoot img.leaflet-marker-shadow{max-width:none;max-height:none}.mapRoot.mapRoot .leaflet-control-attribution,.mapRoot.mapRoot .leaflet-control-container.leaflet-bottom{display:none}.mapRoot .leaflet-pane,.mapRoot .leaflet-map-pane,.mapRoot .leaflet-tile-pane{width:100%;height:100%}\n"] }]
130
+ }], propDecorators: { latitude: [{ type: i0.Input, args: [{ isSignal: true, alias: "latitude", required: false }] }], longitude: [{ type: i0.Input, args: [{ isSignal: true, alias: "longitude", required: false }] }], zoom: [{ type: i0.Input, args: [{ isSignal: true, alias: "zoom", required: false }] }], markers: [{ type: i0.Input, args: [{ isSignal: true, alias: "markers", required: false }] }], interactive: [{ type: i0.Input, args: [{ isSignal: true, alias: "interactive", required: false }] }], mapClick: [{ type: i0.Output, args: ["mapClick"] }], markerDragEnd: [{ type: i0.Output, args: ["markerDragEnd"] }], mapEl: [{ type: i0.ViewChild, args: ['mapEl', { isSignal: true }] }] } });
131
+
132
+ const TILE_URL$1 = MAP_CONFIG.tileUrl;
133
+ const DEFAULT_CENTER = [24.7136, 46.6753];
134
+ const DEFAULT_ZOOM = 6;
135
+ const SINGLE_OFFICE_ZOOM = 12;
136
+ function isPlottableOffice(office) {
137
+ return (Number.isFinite(office.latitude) &&
138
+ Number.isFinite(office.longitude) &&
139
+ Math.abs(office.latitude) <= 90 &&
140
+ Math.abs(office.longitude) <= 180);
141
+ }
142
+ function markerTooltipLabel(office, translate) {
143
+ if (!office.approximate) {
144
+ return office.name;
145
+ }
146
+ const suffix = translate?.instant('map.approximateLocation') ?? 'موقع تقريبي (مركز المدينة)';
147
+ return `${office.name}\n${suffix}`;
148
+ }
149
+ /** React: google-maps/osm-offices-map.tsx */
150
+ class SceOsmOfficesMapComponent {
151
+ language = inject(LanguageService);
152
+ mapRoot;
153
+ translate = inject(TranslateService);
154
+ offices = input([], ...(ngDevMode ? [{ debugName: "offices" }] : /* istanbul ignore next */ []));
155
+ selectedOfficeId = input(...(ngDevMode ? [undefined, { debugName: "selectedOfficeId" }] : /* istanbul ignore next */ []));
156
+ showTooltip = input(true, ...(ngDevMode ? [{ debugName: "showTooltip" }] : /* istanbul ignore next */ []));
157
+ ariaLabelInput = input('', ...(ngDevMode ? [{ debugName: "ariaLabelInput" }] : /* istanbul ignore next */ []));
158
+ ariaLabel = computed(() => {
159
+ // Re-resolve the default label when the active locale changes.
160
+ this.language.translationsRevision();
161
+ return this.ariaLabelInput() || this.translate.instant('map.officesMap');
162
+ }, ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
163
+ selectOffice = output();
164
+ ready = output();
165
+ mapError = output();
166
+ map = null;
167
+ markers = [];
168
+ markerByOfficeId = new Map();
169
+ mapReady = false;
170
+ disposed = false;
171
+ icon = null;
172
+ cancelLeafletResize = null;
173
+ ngAfterViewInit() {
174
+ void this.bootMap();
175
+ }
176
+ ngOnChanges(changes) {
177
+ if (changes['offices'] && this.mapReady) {
178
+ void this.syncMarkers();
179
+ }
180
+ if (changes['selectedOfficeId'] && this.mapReady && this.selectedOfficeId() != null) {
181
+ this.focusSelectedOffice(this.selectedOfficeId());
182
+ }
183
+ }
184
+ ngOnDestroy() {
185
+ this.disposed = true;
186
+ this.mapReady = false;
187
+ this.cancelLeafletResize?.();
188
+ this.cancelLeafletResize = null;
189
+ this.clearMarkers();
190
+ this.map?.remove();
191
+ this.map = null;
192
+ const el = this.mapRoot?.nativeElement;
193
+ if (el) {
194
+ delete el._leaflet_id;
195
+ }
196
+ }
197
+ async bootMap() {
198
+ const container = this.mapRoot?.nativeElement;
199
+ if (!container) {
200
+ return;
201
+ }
202
+ try {
203
+ const leafletModule = await import('leaflet');
204
+ // CJS/ESM interop: in the esbuild bundle the Leaflet API lands on .default, in dev it may not.
205
+ const L = (leafletModule.default ??
206
+ leafletModule);
207
+ if (this.disposed) {
208
+ return;
209
+ }
210
+ this.icon = createLeafletDefaultIcon(L);
211
+ const map = L.map(container, {
212
+ center: DEFAULT_CENTER,
213
+ zoom: DEFAULT_ZOOM,
214
+ scrollWheelZoom: true,
215
+ preferCanvas: false,
216
+ attributionControl: false,
217
+ });
218
+ L.tileLayer(TILE_URL$1, { attribution: '', maxZoom: MAP_CONFIG.maxZoom }).addTo(map);
219
+ this.map = map;
220
+ this.queueLeafletResize(map);
221
+ window.setTimeout(() => {
222
+ if (!this.disposed) {
223
+ this.queueLeafletResize(map);
224
+ }
225
+ }, 400);
226
+ if (!this.disposed) {
227
+ this.mapReady = true;
228
+ this.ready.emit();
229
+ await this.syncMarkers();
230
+ }
231
+ }
232
+ catch {
233
+ if (!this.disposed) {
234
+ this.mapError.emit();
235
+ }
236
+ }
237
+ }
238
+ async syncMarkers() {
239
+ const map = this.map;
240
+ if (!map) {
241
+ return;
242
+ }
243
+ const leafletModule = await import('leaflet');
244
+ // CJS/ESM interop: in the esbuild bundle the Leaflet API lands on .default, in dev it may not.
245
+ const L = (leafletModule.default ??
246
+ leafletModule);
247
+ this.clearMarkers();
248
+ const plottable = this.offices().filter(isPlottableOffice);
249
+ if (plottable.length === 0) {
250
+ map.setView(DEFAULT_CENTER, DEFAULT_ZOOM);
251
+ this.queueLeafletResize(map);
252
+ return;
253
+ }
254
+ if (!this.icon) {
255
+ this.icon = createLeafletDefaultIcon(L);
256
+ }
257
+ const bounds = L.latLngBounds([]);
258
+ for (const office of plottable) {
259
+ const marker = L.marker([office.latitude, office.longitude], {
260
+ icon: this.icon || undefined,
261
+ }).addTo(map);
262
+ if (this.showTooltip()) {
263
+ marker.bindTooltip(markerTooltipLabel(office, this.translate), {
264
+ direction: 'top',
265
+ opacity: 0.95,
266
+ sticky: true,
267
+ });
268
+ }
269
+ marker.on('click', () => this.selectOffice.emit(office.id));
270
+ this.markers.push(marker);
271
+ this.markerByOfficeId.set(office.id, marker);
272
+ bounds.extend([office.latitude, office.longitude]);
273
+ }
274
+ const focusMarker = () => {
275
+ if (!this.map || this.disposed) {
276
+ return;
277
+ }
278
+ if (plottable.length > 1) {
279
+ map.fitBounds(bounds, { padding: [48, 48], maxZoom: 12 });
280
+ }
281
+ else {
282
+ map.setView([plottable[0].latitude, plottable[0].longitude], SINGLE_OFFICE_ZOOM, {
283
+ animate: false,
284
+ });
285
+ if (this.showTooltip()) {
286
+ const marker = this.markerByOfficeId.get(plottable[0].id);
287
+ marker?.openTooltip();
288
+ }
289
+ }
290
+ this.queueLeafletResize(map);
291
+ };
292
+ focusMarker();
293
+ window.setTimeout(() => {
294
+ if (!this.disposed) {
295
+ focusMarker();
296
+ }
297
+ }, 120);
298
+ window.setTimeout(() => {
299
+ if (!this.disposed) {
300
+ focusMarker();
301
+ }
302
+ }, 450);
303
+ }
304
+ queueLeafletResize(map) {
305
+ this.cancelLeafletResize?.();
306
+ this.cancelLeafletResize = scheduleLeafletResize(map);
307
+ }
308
+ focusSelectedOffice(officeId) {
309
+ const map = this.map;
310
+ if (!map) {
311
+ return;
312
+ }
313
+ const marker = this.markerByOfficeId.get(officeId);
314
+ if (!marker) {
315
+ return;
316
+ }
317
+ const latLng = marker.getLatLng();
318
+ map.setView(latLng, Math.max(map.getZoom(), SINGLE_OFFICE_ZOOM), { animate: true });
319
+ if (this.showTooltip()) {
320
+ marker.openTooltip();
321
+ }
322
+ this.queueLeafletResize(map);
323
+ }
324
+ clearMarkers() {
325
+ this.markers.forEach(marker => marker.remove());
326
+ this.markers = [];
327
+ this.markerByOfficeId.clear();
328
+ }
329
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SceOsmOfficesMapComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
330
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.17", type: SceOsmOfficesMapComponent, isStandalone: true, selector: "sce-osm-offices-map", inputs: { offices: { classPropertyName: "offices", publicName: "offices", isSignal: true, isRequired: false, transformFunction: null }, selectedOfficeId: { classPropertyName: "selectedOfficeId", publicName: "selectedOfficeId", isSignal: true, isRequired: false, transformFunction: null }, showTooltip: { classPropertyName: "showTooltip", publicName: "showTooltip", isSignal: true, isRequired: false, transformFunction: null }, ariaLabelInput: { classPropertyName: "ariaLabelInput", publicName: "ariaLabelInput", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectOffice: "selectOffice", ready: "ready", mapError: "mapError" }, host: { classAttribute: "sce-osm-offices-map" }, viewQueries: [{ propertyName: "mapRoot", first: true, predicate: ["mapRoot"], descendants: true }], usesOnChanges: true, hostDirectives: [{ directive: i1.SceVariantDirective, inputs: ["sceVariant", "sceVariant", "sceDensity", "sceDensity"] }], ngImport: i0, template: "<div #mapRoot class=\"mapRoot\" role=\"presentation\" [attr.aria-label]=\"ariaLabel()\"></div>\n", styles: ["@charset \"UTF-8\";.mapRoot{width:100%;height:100%;min-height:var(--size-600);z-index:0}.mapRoot.leaflet-container{width:100%;height:100%;min-height:var(--size-600);font-family:var(--font-family-arabic-primary),Arial,sans-serif}.mapRoot.mapRoot img.leaflet-tile,.mapRoot.mapRoot img.leaflet-marker-icon,.mapRoot.mapRoot img.leaflet-marker-shadow{max-width:none;max-height:none}.mapRoot.mapRoot .leaflet-control-attribution,.mapRoot.mapRoot .leaflet-control-container.leaflet-bottom{display:none}.mapRoot .leaflet-pane,.mapRoot .leaflet-map-pane,.mapRoot .leaflet-tile-pane{width:100%;height:100%}.popup{font-size:var(--font-size-md);font-weight:600;color:var(--text-text-primary-900);padding:var(--space-2) 0}.mapRoot .sce-map-popup{font-size:var(--font-size-md);font-weight:600;color:var(--text-text-primary-900);padding:var(--space-2) 0;font-family:var(--font-family-arabic-primary),Arial,sans-serif}.mapRoot .sce-map-popup-approx{margin-block-start:var(--space-4);font-size:var(--font-size-xs);font-weight:500;color:var(--text-text-secondary-700)}.mapRoot .leaflet-marker-pane{z-index:650}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
331
+ }
332
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SceOsmOfficesMapComponent, decorators: [{
333
+ type: Component,
334
+ args: [{ hostDirectives: [{ directive: SceVariantDirective, inputs: ['sceVariant', 'sceDensity'] }], host: { class: 'sce-osm-offices-map' }, selector: 'sce-osm-offices-map', standalone: true, encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div #mapRoot class=\"mapRoot\" role=\"presentation\" [attr.aria-label]=\"ariaLabel()\"></div>\n", styles: ["@charset \"UTF-8\";.mapRoot{width:100%;height:100%;min-height:var(--size-600);z-index:0}.mapRoot.leaflet-container{width:100%;height:100%;min-height:var(--size-600);font-family:var(--font-family-arabic-primary),Arial,sans-serif}.mapRoot.mapRoot img.leaflet-tile,.mapRoot.mapRoot img.leaflet-marker-icon,.mapRoot.mapRoot img.leaflet-marker-shadow{max-width:none;max-height:none}.mapRoot.mapRoot .leaflet-control-attribution,.mapRoot.mapRoot .leaflet-control-container.leaflet-bottom{display:none}.mapRoot .leaflet-pane,.mapRoot .leaflet-map-pane,.mapRoot .leaflet-tile-pane{width:100%;height:100%}.popup{font-size:var(--font-size-md);font-weight:600;color:var(--text-text-primary-900);padding:var(--space-2) 0}.mapRoot .sce-map-popup{font-size:var(--font-size-md);font-weight:600;color:var(--text-text-primary-900);padding:var(--space-2) 0;font-family:var(--font-family-arabic-primary),Arial,sans-serif}.mapRoot .sce-map-popup-approx{margin-block-start:var(--space-4);font-size:var(--font-size-xs);font-weight:500;color:var(--text-text-secondary-700)}.mapRoot .leaflet-marker-pane{z-index:650}\n"] }]
335
+ }], propDecorators: { mapRoot: [{
336
+ type: ViewChild,
337
+ args: ['mapRoot']
338
+ }], offices: [{ type: i0.Input, args: [{ isSignal: true, alias: "offices", required: false }] }], selectedOfficeId: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedOfficeId", required: false }] }], showTooltip: [{ type: i0.Input, args: [{ isSignal: true, alias: "showTooltip", required: false }] }], ariaLabelInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabelInput", required: false }] }], selectOffice: [{ type: i0.Output, args: ["selectOffice"] }], ready: [{ type: i0.Output, args: ["ready"] }], mapError: [{ type: i0.Output, args: ["mapError"] }] } });
339
+
340
+ /** Loading placeholder in one of several shape variants, optionally repeated `count` times with a shortened last line. */
341
+ class SceSkeleton {
342
+ /** Shape variant of the skeleton */
343
+ variant = input('text', ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
344
+ /** Animation style */
345
+ animation = input('wave', ...(ngDevMode ? [{ debugName: "animation" }] : /* istanbul ignore next */ []));
346
+ /** Width -- accepts any CSS value (px, %, rem, etc.). Defaults to 100% for most variants. */
347
+ width = input('', ...(ngDevMode ? [{ debugName: "width" }] : /* istanbul ignore next */ []));
348
+ /** Height -- accepts any CSS value. Auto-calculated for text variant if not set. */
349
+ height = input('', ...(ngDevMode ? [{ debugName: "height" }] : /* istanbul ignore next */ []));
350
+ /** Border radius -- override the variant's default radius */
351
+ borderRadius = input('', ...(ngDevMode ? [{ debugName: "borderRadius" }] : /* istanbul ignore next */ []));
352
+ /** Number of skeleton lines to render (useful for paragraph placeholders) */
353
+ count = input(1, ...(ngDevMode ? [{ debugName: "count" }] : /* istanbul ignore next */ []));
354
+ /** Gap between repeated lines */
355
+ gap = input('12px', ...(ngDevMode ? [{ debugName: "gap" }] : /* istanbul ignore next */ []));
356
+ /** Whether the last line in a multi-line skeleton should be shorter */
357
+ shrinkLastLine = input(true, ...(ngDevMode ? [{ debugName: "shrinkLastLine" }] : /* istanbul ignore next */ []));
358
+ /** Custom aspect ratio (e.g. '16/9', '1/1') -- sets height automatically via CSS */
359
+ aspectRatio = input('', ...(ngDevMode ? [{ debugName: "aspectRatio" }] : /* istanbul ignore next */ []));
360
+ /** Whether this is a multi-line skeleton */
361
+ isMultiLine = computed(() => this.count() > 1, ...(ngDevMode ? [{ debugName: "isMultiLine" }] : /* istanbul ignore next */ []));
362
+ /** Lines derived from count, each carrying whether it is the shrinking last line */
363
+ lines = computed(() => {
364
+ const count = this.count();
365
+ const shrinksLast = this.shrinkLastLine() && this.isMultiLine();
366
+ return Array.from({ length: count }, (_, index) => ({
367
+ index,
368
+ isLast: shrinksLast && index === count - 1,
369
+ }));
370
+ }, ...(ngDevMode ? [{ debugName: "lines" }] : /* istanbul ignore next */ []));
371
+ /** Computed inline styles for the skeleton element */
372
+ skeletonStyles = computed(() => {
373
+ const styles = {};
374
+ const w = this.width();
375
+ const h = this.height();
376
+ const br = this.borderRadius();
377
+ const ar = this.aspectRatio();
378
+ if (w) {
379
+ styles['width'] = w;
380
+ }
381
+ if (h) {
382
+ styles['height'] = h;
383
+ }
384
+ if (br) {
385
+ styles['border-radius'] = br;
386
+ }
387
+ if (ar) {
388
+ styles['aspect-ratio'] = ar;
389
+ // Variants like `image`/`card` set a fixed height which would defeat the
390
+ // ratio — release it so the ratio drives the height unless one is given.
391
+ if (!h) {
392
+ styles['height'] = 'auto';
393
+ }
394
+ }
395
+ return styles;
396
+ }, ...(ngDevMode ? [{ debugName: "skeletonStyles" }] : /* istanbul ignore next */ []));
397
+ /** Computed container styles (gap for multi-line) */
398
+ containerStyles = computed(() => {
399
+ const styles = {};
400
+ if (this.isMultiLine()) {
401
+ styles['gap'] = this.gap();
402
+ }
403
+ return styles;
404
+ }, ...(ngDevMode ? [{ debugName: "containerStyles" }] : /* istanbul ignore next */ []));
405
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SceSkeleton, deps: [], target: i0.ɵɵFactoryTarget.Component });
406
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SceSkeleton, isStandalone: true, selector: "sce-skeleton", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, animation: { classPropertyName: "animation", publicName: "animation", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, borderRadius: { classPropertyName: "borderRadius", publicName: "borderRadius", isSignal: true, isRequired: false, transformFunction: null }, count: { classPropertyName: "count", publicName: "count", isSignal: true, isRequired: false, transformFunction: null }, gap: { classPropertyName: "gap", publicName: "gap", isSignal: true, isRequired: false, transformFunction: null }, shrinkLastLine: { classPropertyName: "shrinkLastLine", publicName: "shrinkLastLine", isSignal: true, isRequired: false, transformFunction: null }, aspectRatio: { classPropertyName: "aspectRatio", publicName: "aspectRatio", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "sce-skeleton" }, hostDirectives: [{ directive: i1.SceVariantDirective, inputs: ["sceVariant", "sceVariant", "sceDensity", "sceDensity"] }], ngImport: i0, template: "<div\n class=\"skeleton-container\"\n [class.skeleton-container--multi]=\"isMultiLine()\"\n [style]=\"containerStyles()\"\n role=\"status\"\n [attr.aria-label]=\"'ARIA.LOADING_CONTENT' | translate\"\n>\n @for (line of lines(); track line.index) {\n <span\n class=\"skeleton\"\n [class.skeleton--text]=\"variant() === 'text'\"\n [class.skeleton--circular]=\"variant() === 'circular'\"\n [class.skeleton--rectangular]=\"variant() === 'rectangular'\"\n [class.skeleton--rounded]=\"variant() === 'rounded'\"\n [class.skeleton--avatar]=\"variant() === 'avatar'\"\n [class.skeleton--button]=\"variant() === 'button'\"\n [class.skeleton--chip]=\"variant() === 'chip'\"\n [class.skeleton--card]=\"variant() === 'card'\"\n [class.skeleton--image]=\"variant() === 'image'\"\n [class.skeleton--header]=\"variant() === 'header'\"\n [class.skeleton--paragraph]=\"variant() === 'paragraph'\"\n [class.skeleton--divider]=\"variant() === 'divider'\"\n [class.skeleton--badge]=\"variant() === 'badge'\"\n [class.skeleton--input]=\"variant() === 'input'\"\n [class.skeleton--pulse]=\"animation() === 'pulse'\"\n [class.skeleton--wave]=\"animation() === 'wave'\"\n [class.skeleton--no-animation]=\"animation() === 'none'\"\n [class.skeleton--last]=\"line.isLast\"\n [style]=\"skeletonStyles()\"\n aria-hidden=\"true\"\n ></span>\n }\n <span class=\"sr-only\">{{ 'COMMON.LOADING' | translate }}</span>\n</div>\n", styles: [":host{display:block;--skeleton-bg: rgba(31, 42, 55, .08);--skeleton-wave-color-start: transparent;--skeleton-wave-color-mid-light: rgba(255, 255, 255, .35);--skeleton-wave-color-mid: rgba(255, 255, 255, .6);--skeleton-wave-color-mid-light-end: rgba(255, 255, 255, .35);--skeleton-wave-color-end: transparent;--skeleton-pulse-duration: 1.8s;--skeleton-pulse-opacity-min: .4;--skeleton-wave-duration: 2s;--skeleton-radius-xs: var(--radius-xs);--skeleton-radius-sm: var(--radius-sm);--skeleton-radius-md: var(--radius-sm);--skeleton-radius-lg: var(--radius-md);--skeleton-radius-full: 9999px;--skeleton-radius-circle: 50%;--skeleton-text-height: 1em;--skeleton-text-scale-y: .7;--skeleton-circular-size: 48px;--skeleton-rect-height: 120px;--skeleton-avatar-size: 40px;--skeleton-button-width: 120px;--skeleton-button-height: var(--space-40);--skeleton-chip-width: var(--space-72);--skeleton-chip-height: var(--space-28);--skeleton-card-height: 200px;--skeleton-image-height: 180px;--skeleton-header-width: 60%;--skeleton-header-height: 1.5em;--skeleton-paragraph-height: .9em;--skeleton-paragraph-scale-y: .8;--skeleton-divider-height: 1px;--skeleton-badge-width: var(--space-48);--skeleton-badge-height: var(--space-20);--skeleton-input-height: 44px;--skeleton-last-line-width: 65%}:host-context(html[data-theme=dark]){--skeleton-bg: rgba(255, 255, 255, .1);--skeleton-wave-color-mid-light: rgba(255, 255, 255, .06);--skeleton-wave-color-mid: rgba(255, 255, 255, .12);--skeleton-wave-color-mid-light-end: rgba(255, 255, 255, .06)}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.skeleton-container{display:contents}.skeleton-container--multi{display:flex;flex-direction:column}.skeleton{display:block;background-color:var(--skeleton-bg);overflow:hidden;position:relative}.skeleton--text{width:100%;height:var(--skeleton-text-height);border-radius:var(--skeleton-radius-xs);transform-origin:0 55%;transform:scaleY(var(--skeleton-text-scale-y))}.skeleton--circular{width:var(--skeleton-circular-size);height:var(--skeleton-circular-size);border-radius:var(--skeleton-radius-circle);flex-shrink:0}.skeleton--rectangular{width:100%;height:var(--skeleton-rect-height);border-radius:0}.skeleton--rounded{width:100%;height:var(--skeleton-rect-height);border-radius:var(--skeleton-radius-md)}.skeleton--avatar{width:var(--skeleton-avatar-size);height:var(--skeleton-avatar-size);border-radius:var(--skeleton-radius-circle);flex-shrink:0}.skeleton--button{width:var(--skeleton-button-width);height:var(--skeleton-button-height);border-radius:var(--skeleton-radius-sm);flex-shrink:0}.skeleton--chip{display:inline-block;width:var(--skeleton-chip-width);height:var(--skeleton-chip-height);border-radius:var(--skeleton-radius-full);flex-shrink:0}.skeleton--card{width:100%;height:var(--skeleton-card-height);border-radius:var(--skeleton-radius-lg)}.skeleton--image{width:100%;height:var(--skeleton-image-height);border-radius:var(--skeleton-radius-md)}.skeleton--header{width:var(--skeleton-header-width);height:var(--skeleton-header-height);border-radius:var(--skeleton-radius-xs)}.skeleton--paragraph{width:100%;height:var(--skeleton-paragraph-height);border-radius:var(--skeleton-radius-xs);transform-origin:0 55%;transform:scaleY(var(--skeleton-paragraph-scale-y))}.skeleton--divider{width:100%;height:var(--skeleton-divider-height);border-radius:0}.skeleton--badge{display:inline-block;width:var(--skeleton-badge-width);height:var(--skeleton-badge-height);border-radius:var(--skeleton-radius-xs);flex-shrink:0}.skeleton--input{width:100%;height:var(--skeleton-input-height);border-radius:var(--skeleton-radius-sm)}.skeleton--last{width:var(--skeleton-last-line-width)}.skeleton--pulse{animation:skeleton-pulse var(--skeleton-pulse-duration) ease-in-out infinite}.skeleton--wave:after{content:\"\";position:absolute;inset:0;transform:translate(-100%);background:linear-gradient(90deg,var(--skeleton-wave-color-start) 0%,var(--skeleton-wave-color-mid-light) 30%,var(--skeleton-wave-color-mid) 50%,var(--skeleton-wave-color-mid-light-end) 70%,var(--skeleton-wave-color-end) 100%);animation:skeleton-wave var(--skeleton-wave-duration) ease-in-out infinite}.skeleton--no-animation{animation:none}.skeleton--no-animation:after{display:none}@keyframes skeleton-pulse{0%{opacity:1}50%{opacity:var(--skeleton-pulse-opacity-min)}to{opacity:1}}@keyframes skeleton-wave{0%{transform:translate(-100%)}50%{transform:translate(100%)}to{transform:translate(100%)}}@media(prefers-reduced-motion:reduce){.skeleton--pulse{animation:none}.skeleton--wave:after{animation:none;display:none}}\n"], dependencies: [{ kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
407
+ }
408
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SceSkeleton, decorators: [{
409
+ type: Component,
410
+ args: [{ hostDirectives: [{ directive: SceVariantDirective, inputs: ['sceVariant', 'sceDensity'] }], host: { class: 'sce-skeleton' }, selector: 'sce-skeleton', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [TranslatePipe], template: "<div\n class=\"skeleton-container\"\n [class.skeleton-container--multi]=\"isMultiLine()\"\n [style]=\"containerStyles()\"\n role=\"status\"\n [attr.aria-label]=\"'ARIA.LOADING_CONTENT' | translate\"\n>\n @for (line of lines(); track line.index) {\n <span\n class=\"skeleton\"\n [class.skeleton--text]=\"variant() === 'text'\"\n [class.skeleton--circular]=\"variant() === 'circular'\"\n [class.skeleton--rectangular]=\"variant() === 'rectangular'\"\n [class.skeleton--rounded]=\"variant() === 'rounded'\"\n [class.skeleton--avatar]=\"variant() === 'avatar'\"\n [class.skeleton--button]=\"variant() === 'button'\"\n [class.skeleton--chip]=\"variant() === 'chip'\"\n [class.skeleton--card]=\"variant() === 'card'\"\n [class.skeleton--image]=\"variant() === 'image'\"\n [class.skeleton--header]=\"variant() === 'header'\"\n [class.skeleton--paragraph]=\"variant() === 'paragraph'\"\n [class.skeleton--divider]=\"variant() === 'divider'\"\n [class.skeleton--badge]=\"variant() === 'badge'\"\n [class.skeleton--input]=\"variant() === 'input'\"\n [class.skeleton--pulse]=\"animation() === 'pulse'\"\n [class.skeleton--wave]=\"animation() === 'wave'\"\n [class.skeleton--no-animation]=\"animation() === 'none'\"\n [class.skeleton--last]=\"line.isLast\"\n [style]=\"skeletonStyles()\"\n aria-hidden=\"true\"\n ></span>\n }\n <span class=\"sr-only\">{{ 'COMMON.LOADING' | translate }}</span>\n</div>\n", styles: [":host{display:block;--skeleton-bg: rgba(31, 42, 55, .08);--skeleton-wave-color-start: transparent;--skeleton-wave-color-mid-light: rgba(255, 255, 255, .35);--skeleton-wave-color-mid: rgba(255, 255, 255, .6);--skeleton-wave-color-mid-light-end: rgba(255, 255, 255, .35);--skeleton-wave-color-end: transparent;--skeleton-pulse-duration: 1.8s;--skeleton-pulse-opacity-min: .4;--skeleton-wave-duration: 2s;--skeleton-radius-xs: var(--radius-xs);--skeleton-radius-sm: var(--radius-sm);--skeleton-radius-md: var(--radius-sm);--skeleton-radius-lg: var(--radius-md);--skeleton-radius-full: 9999px;--skeleton-radius-circle: 50%;--skeleton-text-height: 1em;--skeleton-text-scale-y: .7;--skeleton-circular-size: 48px;--skeleton-rect-height: 120px;--skeleton-avatar-size: 40px;--skeleton-button-width: 120px;--skeleton-button-height: var(--space-40);--skeleton-chip-width: var(--space-72);--skeleton-chip-height: var(--space-28);--skeleton-card-height: 200px;--skeleton-image-height: 180px;--skeleton-header-width: 60%;--skeleton-header-height: 1.5em;--skeleton-paragraph-height: .9em;--skeleton-paragraph-scale-y: .8;--skeleton-divider-height: 1px;--skeleton-badge-width: var(--space-48);--skeleton-badge-height: var(--space-20);--skeleton-input-height: 44px;--skeleton-last-line-width: 65%}:host-context(html[data-theme=dark]){--skeleton-bg: rgba(255, 255, 255, .1);--skeleton-wave-color-mid-light: rgba(255, 255, 255, .06);--skeleton-wave-color-mid: rgba(255, 255, 255, .12);--skeleton-wave-color-mid-light-end: rgba(255, 255, 255, .06)}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.skeleton-container{display:contents}.skeleton-container--multi{display:flex;flex-direction:column}.skeleton{display:block;background-color:var(--skeleton-bg);overflow:hidden;position:relative}.skeleton--text{width:100%;height:var(--skeleton-text-height);border-radius:var(--skeleton-radius-xs);transform-origin:0 55%;transform:scaleY(var(--skeleton-text-scale-y))}.skeleton--circular{width:var(--skeleton-circular-size);height:var(--skeleton-circular-size);border-radius:var(--skeleton-radius-circle);flex-shrink:0}.skeleton--rectangular{width:100%;height:var(--skeleton-rect-height);border-radius:0}.skeleton--rounded{width:100%;height:var(--skeleton-rect-height);border-radius:var(--skeleton-radius-md)}.skeleton--avatar{width:var(--skeleton-avatar-size);height:var(--skeleton-avatar-size);border-radius:var(--skeleton-radius-circle);flex-shrink:0}.skeleton--button{width:var(--skeleton-button-width);height:var(--skeleton-button-height);border-radius:var(--skeleton-radius-sm);flex-shrink:0}.skeleton--chip{display:inline-block;width:var(--skeleton-chip-width);height:var(--skeleton-chip-height);border-radius:var(--skeleton-radius-full);flex-shrink:0}.skeleton--card{width:100%;height:var(--skeleton-card-height);border-radius:var(--skeleton-radius-lg)}.skeleton--image{width:100%;height:var(--skeleton-image-height);border-radius:var(--skeleton-radius-md)}.skeleton--header{width:var(--skeleton-header-width);height:var(--skeleton-header-height);border-radius:var(--skeleton-radius-xs)}.skeleton--paragraph{width:100%;height:var(--skeleton-paragraph-height);border-radius:var(--skeleton-radius-xs);transform-origin:0 55%;transform:scaleY(var(--skeleton-paragraph-scale-y))}.skeleton--divider{width:100%;height:var(--skeleton-divider-height);border-radius:0}.skeleton--badge{display:inline-block;width:var(--skeleton-badge-width);height:var(--skeleton-badge-height);border-radius:var(--skeleton-radius-xs);flex-shrink:0}.skeleton--input{width:100%;height:var(--skeleton-input-height);border-radius:var(--skeleton-radius-sm)}.skeleton--last{width:var(--skeleton-last-line-width)}.skeleton--pulse{animation:skeleton-pulse var(--skeleton-pulse-duration) ease-in-out infinite}.skeleton--wave:after{content:\"\";position:absolute;inset:0;transform:translate(-100%);background:linear-gradient(90deg,var(--skeleton-wave-color-start) 0%,var(--skeleton-wave-color-mid-light) 30%,var(--skeleton-wave-color-mid) 50%,var(--skeleton-wave-color-mid-light-end) 70%,var(--skeleton-wave-color-end) 100%);animation:skeleton-wave var(--skeleton-wave-duration) ease-in-out infinite}.skeleton--no-animation{animation:none}.skeleton--no-animation:after{display:none}@keyframes skeleton-pulse{0%{opacity:1}50%{opacity:var(--skeleton-pulse-opacity-min)}to{opacity:1}}@keyframes skeleton-wave{0%{transform:translate(-100%)}50%{transform:translate(100%)}to{transform:translate(100%)}}@media(prefers-reduced-motion:reduce){.skeleton--pulse{animation:none}.skeleton--wave:after{animation:none;display:none}}\n"] }]
411
+ }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], animation: [{ type: i0.Input, args: [{ isSignal: true, alias: "animation", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], borderRadius: [{ type: i0.Input, args: [{ isSignal: true, alias: "borderRadius", required: false }] }], count: [{ type: i0.Input, args: [{ isSignal: true, alias: "count", required: false }] }], gap: [{ type: i0.Input, args: [{ isSignal: true, alias: "gap", required: false }] }], shrinkLastLine: [{ type: i0.Input, args: [{ isSignal: true, alias: "shrinkLastLine", required: false }] }], aspectRatio: [{ type: i0.Input, args: [{ isSignal: true, alias: "aspectRatio", required: false }] }] } });
412
+
413
+ /** Location picker pairing an OSM Leaflet map with a Nominatim search box, which validates every clicked, dragged or searched position before placing a draggable pin and emitting it with its reverse-geocoded address. */
414
+ class SceOsmLocationPickerComponent {
415
+ initialLat = input(isGeoLookupRestricted() ? JEDDAH_MAP_CENTER.lat : 24.7136, ...(ngDevMode ? [{ debugName: "initialLat" }] : /* istanbul ignore next */ []));
416
+ initialLng = input(isGeoLookupRestricted() ? JEDDAH_MAP_CENTER.lng : 46.6753, ...(ngDevMode ? [{ debugName: "initialLng" }] : /* istanbul ignore next */ []));
417
+ zoom = input(6, ...(ngDevMode ? [{ debugName: "zoom" }] : /* istanbul ignore next */ []));
418
+ locale = input('ar', ...(ngDevMode ? [{ debugName: "locale" }] : /* istanbul ignore next */ []));
419
+ locationSelect = output();
420
+ nominatim = inject(NominatimService);
421
+ mapValidation = inject(GeoMapPositionValidationService);
422
+ toast = inject(ToastService);
423
+ destroyRef = inject(DestroyRef);
424
+ searchQuery = '';
425
+ isSearching = signal(false, ...(ngDevMode ? [{ debugName: "isSearching" }] : /* istanbul ignore next */ []));
426
+ currentLat = signal(isGeoLookupRestricted() ? JEDDAH_MAP_CENTER.lat : 24.7136, ...(ngDevMode ? [{ debugName: "currentLat" }] : /* istanbul ignore next */ []));
427
+ currentLng = signal(isGeoLookupRestricted() ? JEDDAH_MAP_CENTER.lng : 46.6753, ...(ngDevMode ? [{ debugName: "currentLng" }] : /* istanbul ignore next */ []));
428
+ selectedLocation = signal(null, ...(ngDevMode ? [{ debugName: "selectedLocation" }] : /* istanbul ignore next */ []));
429
+ selectedCoordsLabel = computed(() => {
430
+ const loc = this.selectedLocation();
431
+ return loc ? `${loc.lat.toFixed(6)}, ${loc.lng.toFixed(6)}` : '';
432
+ }, ...(ngDevMode ? [{ debugName: "selectedCoordsLabel" }] : /* istanbul ignore next */ []));
433
+ currentMarkers = signal([], ...(ngDevMode ? [{ debugName: "currentMarkers" }] : /* istanbul ignore next */ []));
434
+ onMapClick(coords) {
435
+ void this.validateAndPlacePin(coords.lat, coords.lng);
436
+ }
437
+ onMarkerDragEnd(event) {
438
+ void this.validateAndPlacePin(event.lat, event.lng);
439
+ }
440
+ async validateAndPlacePin(lat, lng) {
441
+ const validation = await this.mapValidation.validateMapPositionForGeoForms(lat, lng, this.locale());
442
+ if (!validation.ok) {
443
+ this.toast.warning('', mapPositionValidationMessage(this.locale(), validation.reason));
444
+ return false;
445
+ }
446
+ this.currentLat.set(lat);
447
+ this.currentLng.set(lng);
448
+ this.placePin(lat, lng);
449
+ return true;
450
+ }
451
+ placePin(lat, lng) {
452
+ const loc = { lat, lng };
453
+ this.selectedLocation.set(loc);
454
+ this.currentMarkers.set([{ lat, lng, draggable: true }]);
455
+ this.nominatim
456
+ .getDisplayAddress(lat, lng, this.locale())
457
+ .pipe(takeUntilDestroyed(this.destroyRef))
458
+ .subscribe({
459
+ next: address => {
460
+ const updated = { lat, lng, address };
461
+ this.selectedLocation.set(updated);
462
+ this.locationSelect.emit(updated);
463
+ },
464
+ error: () => {
465
+ this.locationSelect.emit(loc);
466
+ },
467
+ });
468
+ }
469
+ onSearch() {
470
+ if (!this.searchQuery.trim()) {
471
+ return;
472
+ }
473
+ this.isSearching.set(true);
474
+ this.nominatim
475
+ .search(this.searchQuery, this.locale(), 1)
476
+ .pipe(takeUntilDestroyed(this.destroyRef))
477
+ .subscribe({
478
+ next: results => {
479
+ if (results.length > 0) {
480
+ const result = results[0];
481
+ void this.validateAndPlacePin(result.lat, result.lng).finally(() => {
482
+ this.isSearching.set(false);
483
+ });
484
+ return;
485
+ }
486
+ this.isSearching.set(false);
487
+ },
488
+ error: () => {
489
+ this.isSearching.set(false);
490
+ },
491
+ });
492
+ }
493
+ bindHost = (() => {
494
+ const center = resolveInitialCenter(String(this.initialLat()), String(this.initialLng()));
495
+ this.currentLat.set(center.lat);
496
+ this.currentLng.set(center.lng);
497
+ })();
498
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SceOsmLocationPickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
499
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SceOsmLocationPickerComponent, isStandalone: true, selector: "sce-osm-location-picker", inputs: { initialLat: { classPropertyName: "initialLat", publicName: "initialLat", isSignal: true, isRequired: false, transformFunction: null }, initialLng: { classPropertyName: "initialLng", publicName: "initialLng", isSignal: true, isRequired: false, transformFunction: null }, zoom: { classPropertyName: "zoom", publicName: "zoom", isSignal: true, isRequired: false, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { locationSelect: "locationSelect" }, host: { classAttribute: "sce-osm-location-picker" }, hostDirectives: [{ directive: i1.SceVariantDirective, inputs: ["sceVariant", "sceVariant", "sceDensity", "sceDensity"] }], ngImport: i0, template: "<div>\n <div class=\"searchRow\">\n <input\n class=\"searchInput\"\n type=\"text\"\n [placeholder]=\"'MAPS.SEARCH_PLACEHOLDER' | translate\"\n [(ngModel)]=\"searchQuery\"\n (keydown.enter)=\"onSearch()\"\n />\n <button type=\"button\" class=\"searchButton\" [disabled]=\"isSearching()\" (click)=\"onSearch()\">\n @if (isSearching()) {\n <sce-skeleton variant=\"rounded\" width=\"1.25rem\" height=\"1.25rem\" animation=\"wave\" aria-hidden=\"true\" />\n } @else {\n {{ 'MAPS.SEARCH' | translate }}\n }\n </button>\n </div>\n <p class=\"providerNote\">{{ 'MAPS.PROVIDER_NOTE' | translate }}</p>\n\n <div style=\"height: 300px; margin-top: 0.75rem; border-radius: 0.75rem; overflow: hidden\">\n <sce-osm-leaflet-map\n [latitude]=\"currentLat()\"\n [longitude]=\"currentLng()\"\n [zoom]=\"zoom()\"\n [markers]=\"currentMarkers()\"\n [interactive]=\"true\"\n (mapClick)=\"onMapClick($event)\"\n (markerDragEnd)=\"onMarkerDragEnd($event)\"\n />\n </div>\n\n @if (selectedLocation()) {\n <p style=\"margin: 0.5rem 0 0; font-size: 0.8rem; color: var(--color-text-secondary)\">\n <span dir=\"ltr\">{{ selectedCoordsLabel() }}</span>\n @if (selectedLocation()!.address) {\n \u2014 {{ selectedLocation()!.address }}\n }\n </p>\n }\n</div>\n", styles: ["@charset \"UTF-8\";:host.sce-osm-location-picker{display:block}.providerNote{margin:var(--space-4) 0 0;font-size:var(--font-size-xs);color:var(--color-neutral-500)}.searchRow{display:flex;gap:var(--space-8);align-items:stretch}.searchRow .searchInput{flex:1;min-width:0}.searchButton{flex-shrink:0;min-height:var(--space-44, var(--size-44));padding:0 1rem;border:0;border-radius:var(--radius-sm);background:var(--button-background-primary-bg-primary);color:var(--color-base-white);font-size:.9rem;font-weight:600;cursor:pointer}.searchButton:disabled{opacity:.55;cursor:not-allowed}.searchFeedback{margin:0;font-size:.8rem;color:var(--color-neutral-600)}.searchResults{margin:0;padding:0;list-style:none;max-height:var(--size-160);overflow:auto;border:1px solid var(--color-neutral-200);border-radius:var(--radius-sm)}.searchResultButton{width:100%;padding:.65rem .85rem;border:0;border-block-end:1px solid var(--color-neutral-100);background:var(--color-base-white);text-align:start;font-size:.85rem;color:var(--text-text-primary-900);cursor:pointer}.searchResultButton:hover{background:var(--color-neutral-50)}.searchResults li:last-child .searchResultButton{border-block-end:0}.mapWrapper{position:relative;width:100%;height:var(--size-360);border-radius:var(--radius-lg);overflow:hidden}.inlineMapWrapper{position:relative;width:100%;border-radius:var(--radius-lg);overflow:hidden;border:1px solid var(--color-neutral-200);background:var(--color-neutral-200)}.mapHost{position:absolute;inset:0;z-index:1}.mapHost .leaflet-container{width:100%;height:100%;font-family:inherit;background:var(--color-neutral-200)}.mapHost .leaflet-container img.leaflet-tile.leaflet-tile,.mapHost .leaflet-container img.leaflet-marker-icon.leaflet-marker-icon,.mapHost .leaflet-container img.leaflet-marker-shadow.leaflet-marker-shadow{max-width:none;max-height:none;width:auto;height:auto}.mapHost .leaflet-pane,.mapHost .leaflet-tile-pane,.mapHost .leaflet-overlay-pane{z-index:auto}.mapLoadingOverlay{position:absolute;inset:0;z-index:2;pointer-events:none;background:linear-gradient(90deg,var(--color-neutral-100) 25%,var(--color-neutral-300) 50%,var(--color-neutral-100) 75%);background-size:200% 100%;animation:mapLoadingShimmer 1.2s ease-in-out infinite}.inlineMapShell{position:relative;width:100%}.fullscreenButton{position:absolute;top:.65rem;inset-inline-start:.65rem;z-index:1001;display:inline-flex;align-items:center;justify-content:center;gap:.35rem;min-width:2.25rem;height:2.25rem;padding:0 .55rem;border:0;border-radius:var(--radius-sm);background:#fffffff2;color:var(--text-text-primary-900);box-shadow:0 2px 10px #0000002e;cursor:pointer;pointer-events:auto}.fullscreenButton:hover{background:var(--color-base-white)}.fullscreenButton:focus-visible{outline:2px solid var(--button-background-primary-bg-primary);outline-offset:2px}.fullscreenButtonLabel{font-size:var(--font-size-xs);font-weight:600;line-height:1}.expandedMapWrapper{position:relative;width:100%;height:min(70vb,var(--size-520));border-radius:var(--radius-lg);overflow:hidden}@keyframes mapLoadingShimmer{0%{background-position:100% 0}to{background-position:-100% 0}}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: SceSkeleton, selector: "sce-skeleton", inputs: ["variant", "animation", "width", "height", "borderRadius", "count", "gap", "shrinkLastLine", "aspectRatio"] }, { kind: "component", type: SceOsmLeafletMapComponent, selector: "sce-osm-leaflet-map", inputs: ["latitude", "longitude", "zoom", "markers", "interactive"], outputs: ["mapClick", "markerDragEnd"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
500
+ }
501
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SceOsmLocationPickerComponent, decorators: [{
502
+ type: Component,
503
+ args: [{ hostDirectives: [{ directive: SceVariantDirective, inputs: ['sceVariant', 'sceDensity'] }], host: { class: 'sce-osm-location-picker' }, selector: 'sce-osm-location-picker', standalone: true, imports: [FormsModule, TranslatePipe, SceSkeleton, SceOsmLeafletMapComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div>\n <div class=\"searchRow\">\n <input\n class=\"searchInput\"\n type=\"text\"\n [placeholder]=\"'MAPS.SEARCH_PLACEHOLDER' | translate\"\n [(ngModel)]=\"searchQuery\"\n (keydown.enter)=\"onSearch()\"\n />\n <button type=\"button\" class=\"searchButton\" [disabled]=\"isSearching()\" (click)=\"onSearch()\">\n @if (isSearching()) {\n <sce-skeleton variant=\"rounded\" width=\"1.25rem\" height=\"1.25rem\" animation=\"wave\" aria-hidden=\"true\" />\n } @else {\n {{ 'MAPS.SEARCH' | translate }}\n }\n </button>\n </div>\n <p class=\"providerNote\">{{ 'MAPS.PROVIDER_NOTE' | translate }}</p>\n\n <div style=\"height: 300px; margin-top: 0.75rem; border-radius: 0.75rem; overflow: hidden\">\n <sce-osm-leaflet-map\n [latitude]=\"currentLat()\"\n [longitude]=\"currentLng()\"\n [zoom]=\"zoom()\"\n [markers]=\"currentMarkers()\"\n [interactive]=\"true\"\n (mapClick)=\"onMapClick($event)\"\n (markerDragEnd)=\"onMarkerDragEnd($event)\"\n />\n </div>\n\n @if (selectedLocation()) {\n <p style=\"margin: 0.5rem 0 0; font-size: 0.8rem; color: var(--color-text-secondary)\">\n <span dir=\"ltr\">{{ selectedCoordsLabel() }}</span>\n @if (selectedLocation()!.address) {\n \u2014 {{ selectedLocation()!.address }}\n }\n </p>\n }\n</div>\n", styles: ["@charset \"UTF-8\";:host.sce-osm-location-picker{display:block}.providerNote{margin:var(--space-4) 0 0;font-size:var(--font-size-xs);color:var(--color-neutral-500)}.searchRow{display:flex;gap:var(--space-8);align-items:stretch}.searchRow .searchInput{flex:1;min-width:0}.searchButton{flex-shrink:0;min-height:var(--space-44, var(--size-44));padding:0 1rem;border:0;border-radius:var(--radius-sm);background:var(--button-background-primary-bg-primary);color:var(--color-base-white);font-size:.9rem;font-weight:600;cursor:pointer}.searchButton:disabled{opacity:.55;cursor:not-allowed}.searchFeedback{margin:0;font-size:.8rem;color:var(--color-neutral-600)}.searchResults{margin:0;padding:0;list-style:none;max-height:var(--size-160);overflow:auto;border:1px solid var(--color-neutral-200);border-radius:var(--radius-sm)}.searchResultButton{width:100%;padding:.65rem .85rem;border:0;border-block-end:1px solid var(--color-neutral-100);background:var(--color-base-white);text-align:start;font-size:.85rem;color:var(--text-text-primary-900);cursor:pointer}.searchResultButton:hover{background:var(--color-neutral-50)}.searchResults li:last-child .searchResultButton{border-block-end:0}.mapWrapper{position:relative;width:100%;height:var(--size-360);border-radius:var(--radius-lg);overflow:hidden}.inlineMapWrapper{position:relative;width:100%;border-radius:var(--radius-lg);overflow:hidden;border:1px solid var(--color-neutral-200);background:var(--color-neutral-200)}.mapHost{position:absolute;inset:0;z-index:1}.mapHost .leaflet-container{width:100%;height:100%;font-family:inherit;background:var(--color-neutral-200)}.mapHost .leaflet-container img.leaflet-tile.leaflet-tile,.mapHost .leaflet-container img.leaflet-marker-icon.leaflet-marker-icon,.mapHost .leaflet-container img.leaflet-marker-shadow.leaflet-marker-shadow{max-width:none;max-height:none;width:auto;height:auto}.mapHost .leaflet-pane,.mapHost .leaflet-tile-pane,.mapHost .leaflet-overlay-pane{z-index:auto}.mapLoadingOverlay{position:absolute;inset:0;z-index:2;pointer-events:none;background:linear-gradient(90deg,var(--color-neutral-100) 25%,var(--color-neutral-300) 50%,var(--color-neutral-100) 75%);background-size:200% 100%;animation:mapLoadingShimmer 1.2s ease-in-out infinite}.inlineMapShell{position:relative;width:100%}.fullscreenButton{position:absolute;top:.65rem;inset-inline-start:.65rem;z-index:1001;display:inline-flex;align-items:center;justify-content:center;gap:.35rem;min-width:2.25rem;height:2.25rem;padding:0 .55rem;border:0;border-radius:var(--radius-sm);background:#fffffff2;color:var(--text-text-primary-900);box-shadow:0 2px 10px #0000002e;cursor:pointer;pointer-events:auto}.fullscreenButton:hover{background:var(--color-base-white)}.fullscreenButton:focus-visible{outline:2px solid var(--button-background-primary-bg-primary);outline-offset:2px}.fullscreenButtonLabel{font-size:var(--font-size-xs);font-weight:600;line-height:1}.expandedMapWrapper{position:relative;width:100%;height:min(70vb,var(--size-520));border-radius:var(--radius-lg);overflow:hidden}@keyframes mapLoadingShimmer{0%{background-position:100% 0}to{background-position:-100% 0}}\n"] }]
504
+ }], propDecorators: { initialLat: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialLat", required: false }] }], initialLng: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialLng", required: false }] }], zoom: [{ type: i0.Input, args: [{ isSignal: true, alias: "zoom", required: false }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], locationSelect: [{ type: i0.Output, args: ["locationSelect"] }] } });
505
+
506
+ const TILE_URL = MAP_CONFIG.tileUrl;
507
+ function hasValidCoordinates(lat, lng) {
508
+ return (typeof lat === 'number' &&
509
+ typeof lng === 'number' &&
510
+ Number.isFinite(lat) &&
511
+ Number.isFinite(lng) &&
512
+ Math.abs(lat) <= 90 &&
513
+ Math.abs(lng) <= 180 &&
514
+ !(lat === 0 && lng === 0));
515
+ }
516
+ function buildGoogleEmbedUrl(lat, lng, address) {
517
+ if (hasValidCoordinates(lat, lng)) {
518
+ return `https://www.google.com/maps?q=${lat},${lng}&z=15&output=embed`;
519
+ }
520
+ const query = address?.trim();
521
+ if (query) {
522
+ return `https://www.google.com/maps?q=${encodeURIComponent(query)}&z=14&output=embed`;
523
+ }
524
+ return null;
525
+ }
526
+ /** Read-only map of an office's location: a Leaflet map with a marker when valid coordinates are given, a Google Maps embed built from the address when they are not, and a "not specified" fallback when neither is available. */
527
+ class SceOfficeLocationMapComponent {
528
+ sanitizer = inject(DomSanitizer);
529
+ latitude = input(...(ngDevMode ? [undefined, { debugName: "latitude" }] : /* istanbul ignore next */ []));
530
+ longitude = input(...(ngDevMode ? [undefined, { debugName: "longitude" }] : /* istanbul ignore next */ []));
531
+ address = input(...(ngDevMode ? [undefined, { debugName: "address" }] : /* istanbul ignore next */ []));
532
+ emptyLabel = input(...(ngDevMode ? [undefined, { debugName: "emptyLabel" }] : /* istanbul ignore next */ []));
533
+ mapContainer;
534
+ map = null;
535
+ cancelLeafletResize = null;
536
+ hasCoords = computed(() => hasValidCoordinates(this.latitude(), this.longitude()), ...(ngDevMode ? [{ debugName: "hasCoords" }] : /* istanbul ignore next */ []));
537
+ googleEmbedUrl = computed(() => {
538
+ const url = buildGoogleEmbedUrl(this.latitude(), this.longitude(), this.address());
539
+ return url ? this.sanitizer.bypassSecurityTrustResourceUrl(url) : null;
540
+ }, ...(ngDevMode ? [{ debugName: "googleEmbedUrl" }] : /* istanbul ignore next */ []));
541
+ ngAfterViewInit() {
542
+ if (this.hasCoords() && this.mapContainer) {
543
+ void this.initLeaflet();
544
+ }
545
+ }
546
+ ngOnDestroy() {
547
+ this.cancelLeafletResize?.();
548
+ this.cancelLeafletResize = null;
549
+ if (this.map && typeof this.map.remove === 'function') {
550
+ this.map.remove();
551
+ }
552
+ }
553
+ async initLeaflet() {
554
+ const container = this.mapContainer?.nativeElement;
555
+ if (!container) {
556
+ return;
557
+ }
558
+ const leafletModule = await import('leaflet');
559
+ // CJS/ESM interop: in the esbuild bundle the Leaflet API lands on .default, in dev it may not.
560
+ const L = (leafletModule.default ??
561
+ leafletModule);
562
+ if (!this.latitude() || !this.longitude()) {
563
+ return;
564
+ }
565
+ const lat = this.latitude();
566
+ const lng = this.longitude();
567
+ const map = L.map(container, {
568
+ center: [lat, lng],
569
+ zoom: 14,
570
+ scrollWheelZoom: false,
571
+ dragging: true,
572
+ zoomControl: true,
573
+ attributionControl: false,
574
+ });
575
+ L.tileLayer(TILE_URL, { attribution: '', maxZoom: MAP_CONFIG.maxZoom }).addTo(map);
576
+ L.marker([lat, lng], { icon: createLeafletDefaultIcon(L) }).addTo(map);
577
+ this.map = map;
578
+ this.cancelLeafletResize = scheduleLeafletResize(map);
579
+ }
580
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SceOfficeLocationMapComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
581
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SceOfficeLocationMapComponent, isStandalone: true, selector: "sce-office-location-map", inputs: { latitude: { classPropertyName: "latitude", publicName: "latitude", isSignal: true, isRequired: false, transformFunction: null }, longitude: { classPropertyName: "longitude", publicName: "longitude", isSignal: true, isRequired: false, transformFunction: null }, address: { classPropertyName: "address", publicName: "address", isSignal: true, isRequired: false, transformFunction: null }, emptyLabel: { classPropertyName: "emptyLabel", publicName: "emptyLabel", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "sce-office-location-map" }, viewQueries: [{ propertyName: "mapContainer", first: true, predicate: ["mapContainer"], descendants: true }], hostDirectives: [{ directive: i1.SceVariantDirective, inputs: ["sceVariant", "sceVariant", "sceDensity", "sceDensity"] }], ngImport: i0, template: "@if (hasCoords()) {\n <div class=\"mapShell\">\n <div #mapContainer class=\"mapRoot\" role=\"presentation\"></div>\n </div>\n} @else if (googleEmbedUrl()) {\n <div class=\"mapShell\">\n <iframe\n [title]=\"address()?.trim() || ('map.officeLocation' | translate)\"\n [src]=\"googleEmbedUrl()\"\n class=\"mapFrame\"\n loading=\"lazy\"\n referrerpolicy=\"no-referrer-when-downgrade\"\n allowfullscreen\n ></iframe>\n </div>\n} @else {\n <div class=\"mapShell mapFallback\">\n {{ emptyLabel() || ('common.notSpecified' | translate) }}\n </div>\n}\n", styles: ["@charset \"UTF-8\";.mapShell{width:100%;overflow:hidden;border-radius:var(--radius-lg);background:var(--color-neutral-200)}.mapFrame{display:block;width:100%;height:var(--size-220);border:0}.mapRoot{width:100%;height:var(--size-220);min-height:var(--size-220);background:var(--color-neutral-200)}.mapRoot.mapRoot.leaflet-container{width:100%;height:var(--size-220);font-family:inherit}.mapRoot.mapRoot img.leaflet-tile,.mapRoot.mapRoot img.leaflet-marker-icon,.mapRoot.mapRoot img.leaflet-marker-shadow{max-width:none;max-height:none}.mapRoot.mapRoot .leaflet-control-attribution,.mapRoot.mapRoot .leaflet-control-container.leaflet-bottom{display:none}.mapRoot .leaflet-pane,.mapRoot .leaflet-map-pane,.mapRoot .leaflet-tile-pane{width:100%;height:100%}.mapFallback{display:flex;align-items:center;justify-content:center;min-height:var(--size-220);padding:var(--space-16);text-align:center;font-family:var(--font-family-arabic-primary);font-size:var(--font-size-md);color:var(--text-text-secondary-700)}\n"], dependencies: [{ kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
582
+ }
583
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SceOfficeLocationMapComponent, decorators: [{
584
+ type: Component,
585
+ args: [{ hostDirectives: [{ directive: SceVariantDirective, inputs: ['sceVariant', 'sceDensity'] }], host: { class: 'sce-office-location-map' }, selector: 'sce-office-location-map', standalone: true, imports: [TranslatePipe], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (hasCoords()) {\n <div class=\"mapShell\">\n <div #mapContainer class=\"mapRoot\" role=\"presentation\"></div>\n </div>\n} @else if (googleEmbedUrl()) {\n <div class=\"mapShell\">\n <iframe\n [title]=\"address()?.trim() || ('map.officeLocation' | translate)\"\n [src]=\"googleEmbedUrl()\"\n class=\"mapFrame\"\n loading=\"lazy\"\n referrerpolicy=\"no-referrer-when-downgrade\"\n allowfullscreen\n ></iframe>\n </div>\n} @else {\n <div class=\"mapShell mapFallback\">\n {{ emptyLabel() || ('common.notSpecified' | translate) }}\n </div>\n}\n", styles: ["@charset \"UTF-8\";.mapShell{width:100%;overflow:hidden;border-radius:var(--radius-lg);background:var(--color-neutral-200)}.mapFrame{display:block;width:100%;height:var(--size-220);border:0}.mapRoot{width:100%;height:var(--size-220);min-height:var(--size-220);background:var(--color-neutral-200)}.mapRoot.mapRoot.leaflet-container{width:100%;height:var(--size-220);font-family:inherit}.mapRoot.mapRoot img.leaflet-tile,.mapRoot.mapRoot img.leaflet-marker-icon,.mapRoot.mapRoot img.leaflet-marker-shadow{max-width:none;max-height:none}.mapRoot.mapRoot .leaflet-control-attribution,.mapRoot.mapRoot .leaflet-control-container.leaflet-bottom{display:none}.mapRoot .leaflet-pane,.mapRoot .leaflet-map-pane,.mapRoot .leaflet-tile-pane{width:100%;height:100%}.mapFallback{display:flex;align-items:center;justify-content:center;min-height:var(--size-220);padding:var(--space-16);text-align:center;font-family:var(--font-family-arabic-primary);font-size:var(--font-size-md);color:var(--text-text-secondary-700)}\n"] }]
586
+ }], propDecorators: { latitude: [{ type: i0.Input, args: [{ isSignal: true, alias: "latitude", required: false }] }], longitude: [{ type: i0.Input, args: [{ isSignal: true, alias: "longitude", required: false }] }], address: [{ type: i0.Input, args: [{ isSignal: true, alias: "address", required: false }] }], emptyLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyLabel", required: false }] }], mapContainer: [{
587
+ type: ViewChild,
588
+ args: ['mapContainer']
589
+ }] } });
590
+
591
+ /** Titled card framing the OSM map of branch/office locations. */
592
+ class SceStoreLocationsComponent {
593
+ titleKey = input('storeLocations.title', ...(ngDevMode ? [{ debugName: "titleKey" }] : /* istanbul ignore next */ []));
594
+ showHeader = input(true, ...(ngDevMode ? [{ debugName: "showHeader" }] : /* istanbul ignore next */ []));
595
+ locations = input([], ...(ngDevMode ? [{ debugName: "locations" }] : /* istanbul ignore next */ []));
596
+ selectedLocationId = input(...(ngDevMode ? [undefined, { debugName: "selectedLocationId" }] : /* istanbul ignore next */ []));
597
+ /** Map viewport height, in px. */
598
+ height = input(400, ...(ngDevMode ? [{ debugName: "height" }] : /* istanbul ignore next */ []));
599
+ locationSelect = output();
600
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SceStoreLocationsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
601
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SceStoreLocationsComponent, isStandalone: true, selector: "sce-store-locations", inputs: { titleKey: { classPropertyName: "titleKey", publicName: "titleKey", isSignal: true, isRequired: false, transformFunction: null }, showHeader: { classPropertyName: "showHeader", publicName: "showHeader", isSignal: true, isRequired: false, transformFunction: null }, locations: { classPropertyName: "locations", publicName: "locations", isSignal: true, isRequired: false, transformFunction: null }, selectedLocationId: { classPropertyName: "selectedLocationId", publicName: "selectedLocationId", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { locationSelect: "locationSelect" }, host: { classAttribute: "sce-store-locations" }, hostDirectives: [{ directive: i1.SceVariantDirective, inputs: ["sceVariant", "sceVariant", "sceDensity", "sceDensity"] }], ngImport: i0, template: "@if (showHeader()) {\n <header class=\"sce-store-locations__header\">\n <h3 class=\"sce-store-locations__title\">{{ titleKey() | translate }}</h3>\n </header>\n}\n\n<div class=\"sce-store-locations__map\" [style.block-size.px]=\"height()\">\n <sce-osm-offices-map\n [offices]=\"locations()\"\n [selectedOfficeId]=\"selectedLocationId()\"\n [ariaLabelInput]=\"titleKey() | translate\"\n (selectOffice)=\"locationSelect.emit($event)\"\n />\n</div>\n", styles: [":host{display:flex;flex-direction:column;overflow:hidden;border:var(--border-width-1) solid var(--color-border-subtle);border-radius:var(--radius-lg);background:var(--color-surface-primary)}.sce-store-locations__header{padding:var(--space-16) var(--space-20);border-block-end:var(--border-width-1) solid var(--color-border-light)}.sce-store-locations__title{margin:0;font-size:var(--font-size-lg);font-weight:var(--font-weight-semibold);line-height:var(--line-height-snug);color:var(--color-text-primary);text-align:start}.sce-store-locations__map{position:relative;inline-size:100%}.sce-store-locations__map sce-osm-offices-map{display:block;inline-size:100%;block-size:100%}\n"], dependencies: [{ kind: "component", type: SceOsmOfficesMapComponent, selector: "sce-osm-offices-map", inputs: ["offices", "selectedOfficeId", "showTooltip", "ariaLabelInput"], outputs: ["selectOffice", "ready", "mapError"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
602
+ }
603
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SceStoreLocationsComponent, decorators: [{
604
+ type: Component,
605
+ args: [{ hostDirectives: [{ directive: SceVariantDirective, inputs: ['sceVariant', 'sceDensity'] }], selector: 'sce-store-locations', standalone: true, imports: [TranslatePipe, SceOsmOfficesMapComponent], changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'sce-store-locations' }, template: "@if (showHeader()) {\n <header class=\"sce-store-locations__header\">\n <h3 class=\"sce-store-locations__title\">{{ titleKey() | translate }}</h3>\n </header>\n}\n\n<div class=\"sce-store-locations__map\" [style.block-size.px]=\"height()\">\n <sce-osm-offices-map\n [offices]=\"locations()\"\n [selectedOfficeId]=\"selectedLocationId()\"\n [ariaLabelInput]=\"titleKey() | translate\"\n (selectOffice)=\"locationSelect.emit($event)\"\n />\n</div>\n", styles: [":host{display:flex;flex-direction:column;overflow:hidden;border:var(--border-width-1) solid var(--color-border-subtle);border-radius:var(--radius-lg);background:var(--color-surface-primary)}.sce-store-locations__header{padding:var(--space-16) var(--space-20);border-block-end:var(--border-width-1) solid var(--color-border-light)}.sce-store-locations__title{margin:0;font-size:var(--font-size-lg);font-weight:var(--font-weight-semibold);line-height:var(--line-height-snug);color:var(--color-text-primary);text-align:start}.sce-store-locations__map{position:relative;inline-size:100%}.sce-store-locations__map sce-osm-offices-map{display:block;inline-size:100%;block-size:100%}\n"] }]
606
+ }], propDecorators: { titleKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "titleKey", required: false }] }], showHeader: [{ type: i0.Input, args: [{ isSignal: true, alias: "showHeader", required: false }] }], locations: [{ type: i0.Input, args: [{ isSignal: true, alias: "locations", required: false }] }], selectedLocationId: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedLocationId", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], locationSelect: [{ type: i0.Output, args: ["locationSelect"] }] } });
607
+
608
+ /** Shared form-field wrapper that renders the label, required marker, projected control, help text and error message for the other sce field components; `id` and `label` are required. */
609
+ class SceFieldComponent {
610
+ id = input.required(...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
611
+ label = input.required(...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
612
+ required = input(false, ...(ngDevMode ? [{ debugName: "required" }] : /* istanbul ignore next */ []));
613
+ /**
614
+ * Hides the label visually while leaving it in the accessibility tree, for controls whose
615
+ * surroundings already name them — a chat composer, a cell inside a labelled table column, or a
616
+ * field sitting under its own section heading. It is never a licence to ship an unnamed control:
617
+ * `label` stays required precisely so the accessible name cannot be dropped.
618
+ */
619
+ hideLabel = input(false, ...(ngDevMode ? [{ debugName: "hideLabel" }] : /* istanbul ignore next */ []));
620
+ helpText = input(...(ngDevMode ? [undefined, { debugName: "helpText" }] : /* istanbul ignore next */ []));
621
+ error = input(...(ngDevMode ? [undefined, { debugName: "error" }] : /* istanbul ignore next */ []));
622
+ labelClassName = input(...(ngDevMode ? [undefined, { debugName: "labelClassName" }] : /* istanbul ignore next */ []));
623
+ fieldClassName = input(...(ngDevMode ? [undefined, { debugName: "fieldClassName" }] : /* istanbul ignore next */ []));
624
+ containerClasses = computed(() => {
625
+ const classes = ['sce-field'];
626
+ if (this.fieldClassName()) {
627
+ classes.push(this.fieldClassName());
628
+ }
629
+ return classes.join(' ');
630
+ }, ...(ngDevMode ? [{ debugName: "containerClasses" }] : /* istanbul ignore next */ []));
631
+ labelClasses = computed(() => {
632
+ const classes = ['sce-field__label'];
633
+ if (this.error()) {
634
+ classes.push('sce-field__label--invalid');
635
+ }
636
+ if (this.hideLabel()) {
637
+ classes.push('sce-field__label--hidden');
638
+ }
639
+ if (this.labelClassName()) {
640
+ classes.push(this.labelClassName());
641
+ }
642
+ return classes.join(' ');
643
+ }, ...(ngDevMode ? [{ debugName: "labelClasses" }] : /* istanbul ignore next */ []));
644
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SceFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
645
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SceFieldComponent, isStandalone: true, selector: "sce-field", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, hideLabel: { classPropertyName: "hideLabel", publicName: "hideLabel", isSignal: true, isRequired: false, transformFunction: null }, helpText: { classPropertyName: "helpText", publicName: "helpText", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, labelClassName: { classPropertyName: "labelClassName", publicName: "labelClassName", isSignal: true, isRequired: false, transformFunction: null }, fieldClassName: { classPropertyName: "fieldClassName", publicName: "fieldClassName", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "sce-field-host" }, hostDirectives: [{ directive: i1.SceVariantDirective, inputs: ["sceVariant", "sceVariant", "sceDensity", "sceDensity"] }], ngImport: i0, template: "<div [class]=\"containerClasses()\">\n <label [attr.id]=\"id() + '-label'\" [attr.for]=\"id()\" [class]=\"labelClasses()\">\n {{ label() }}\n @if (required()) {\n <span class=\"sce-field__asterisk\" aria-hidden=\"true\"> *</span>\n }\n </label>\n <ng-content />\n @if (error()) {\n <!-- id lets the paired control reference this text via aria-describedby. -->\n <div role=\"alert\" class=\"sce-field__error\" [attr.id]=\"id() + '-error'\">{{ error() }}</div>\n }\n @if (helpText()) {\n <div class=\"sce-field__help\">{{ helpText() }}</div>\n }\n</div>\n", styles: ["@charset \"UTF-8\";.sce-field,sce-field{display:flex;flex-direction:column;gap:var(--space-xs, var(--space-8));min-width:0;width:100%}.sce-field__label{display:flex;align-items:baseline;gap:var(--space-xs);font-size:var(--font-size-sm);font-weight:var(--font-weight-medium);color:var(--Form-field-text-label, var(--color-text-default))}.sce-field__label--invalid{color:var(--text-text-error-primary)}.sce-field__label--hidden{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border-width:0}.sce-field__asterisk{color:var(--text-text-error-primary)}.sce-field__error{font-size:var(--font-size-xs);color:var(--text-text-error-primary);overflow-wrap:anywhere}.sce-field__help{font-size:var(--font-size-2xs);color:var(--color-muted)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
646
+ }
647
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SceFieldComponent, decorators: [{
648
+ type: Component,
649
+ args: [{ hostDirectives: [{ directive: SceVariantDirective, inputs: ['sceVariant', 'sceDensity'] }], selector: 'sce-field', standalone: true, host: { class: 'sce-field-host' }, encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div [class]=\"containerClasses()\">\n <label [attr.id]=\"id() + '-label'\" [attr.for]=\"id()\" [class]=\"labelClasses()\">\n {{ label() }}\n @if (required()) {\n <span class=\"sce-field__asterisk\" aria-hidden=\"true\"> *</span>\n }\n </label>\n <ng-content />\n @if (error()) {\n <!-- id lets the paired control reference this text via aria-describedby. -->\n <div role=\"alert\" class=\"sce-field__error\" [attr.id]=\"id() + '-error'\">{{ error() }}</div>\n }\n @if (helpText()) {\n <div class=\"sce-field__help\">{{ helpText() }}</div>\n }\n</div>\n", styles: ["@charset \"UTF-8\";.sce-field,sce-field{display:flex;flex-direction:column;gap:var(--space-xs, var(--space-8));min-width:0;width:100%}.sce-field__label{display:flex;align-items:baseline;gap:var(--space-xs);font-size:var(--font-size-sm);font-weight:var(--font-weight-medium);color:var(--Form-field-text-label, var(--color-text-default))}.sce-field__label--invalid{color:var(--text-text-error-primary)}.sce-field__label--hidden{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border-width:0}.sce-field__asterisk{color:var(--text-text-error-primary)}.sce-field__error{font-size:var(--font-size-xs);color:var(--text-text-error-primary);overflow-wrap:anywhere}.sce-field__help{font-size:var(--font-size-2xs);color:var(--color-muted)}\n"] }]
650
+ }], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], hideLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideLabel", required: false }] }], helpText: [{ type: i0.Input, args: [{ isSignal: true, alias: "helpText", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], labelClassName: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelClassName", required: false }] }], fieldClassName: [{ type: i0.Input, args: [{ isSignal: true, alias: "fieldClassName", required: false }] }] } });
651
+
652
+ /**
653
+ * Thin, project-native wrapper around Hugeicons' `hugeicons-icon`.
654
+ *
655
+ * Renders an inline SVG synchronously from icon data imported out of
656
+ * `@hugeicons/core-free-icons` — no HTTP fetch, SSR-safe, and tree-shakeable
657
+ * (import only the icons you use, e.g. `import { Search01Icon } from '@hugeicons/core-free-icons'`).
658
+ *
659
+ * Prefer this wrapper over `hugeicons-icon` directly so project defaults
660
+ * (stroke width, `currentColor`, accessibility, RTL mirroring) stay consistent.
661
+ *
662
+ * @example
663
+ * ```html
664
+ * <!-- decorative -->
665
+ * <sce-hugeicon [icon]="Search01Icon" [size]="20" />
666
+ *
667
+ * <!-- meaningful (icon-only button) -->
668
+ * <button type="button" [attr.aria-label]="'COMMON.DELETE' | translate">
669
+ * <sce-hugeicon [icon]="Delete02Icon" [label]="'COMMON.DELETE' | translate" />
670
+ * </button>
671
+ *
672
+ * <!-- directional glyph mirrored in Arabic -->
673
+ * <sce-hugeicon [icon]="ArrowRight01Icon" [flipRtl]="true" />
674
+ * ```
675
+ */
676
+ class SceHugeicon {
677
+ /** Icon data to render. Import the constant from `@hugeicons/core-free-icons` (e.g. `Search01Icon`). */
678
+ icon = input.required(...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
679
+ /** Alternate icon revealed when `showAlt` is true — swap without remounting to keep sizing stable. */
680
+ altIcon = input(null, ...(ngDevMode ? [{ debugName: "altIcon" }] : /* istanbul ignore next */ []));
681
+ /** Show `altIcon` instead of `icon` (e.g. filled/active state, theme toggles). */
682
+ showAlt = input(false, ...(ngDevMode ? [{ debugName: "showAlt" }] : /* istanbul ignore next */ []));
683
+ /** Square size in pixels. */
684
+ size = input(24, ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
685
+ /** Any CSS colour. Defaults to `currentColor` so the glyph inherits the surrounding text colour. */
686
+ color = input('currentColor', ...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
687
+ /** Stroke thickness for outline glyphs. Keep consistent across a surface. */
688
+ strokeWidth = input(1.5, ...(ngDevMode ? [{ debugName: "strokeWidth" }] : /* istanbul ignore next */ []));
689
+ /**
690
+ * Keep the stroke visually constant when the icon is scaled up/down, instead of scaling with it
691
+ * (mirrors Hugeicons' `absoluteStrokeWidth`). Useful for very large or very small glyphs.
692
+ */
693
+ absoluteStrokeWidth = input(false, ...(ngDevMode ? [{ debugName: "absoluteStrokeWidth" }] : /* istanbul ignore next */ []));
694
+ /**
695
+ * Continuously rotate the glyph — for spinner/loading affordances. Honours
696
+ * `prefers-reduced-motion` (the animation is disabled when the user asks for reduced motion).
697
+ */
698
+ spin = input(false, ...(ngDevMode ? [{ debugName: "spin" }] : /* istanbul ignore next */ []));
699
+ /**
700
+ * Accessible name. Set for a *meaningful* icon (renders `role="img"` + `aria-label`);
701
+ * leave unset for a *decorative* icon (rendered `aria-hidden`, so screen readers skip it).
702
+ */
703
+ label = input(null, ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
704
+ /** Mirror horizontally under RTL. Use only for directional glyphs (arrows, chevrons, carets). */
705
+ flipRtl = input(false, ...(ngDevMode ? [{ debugName: "flipRtl" }] : /* istanbul ignore next */ []));
706
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SceHugeicon, deps: [], target: i0.ɵɵFactoryTarget.Component });
707
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.17", type: SceHugeicon, isStandalone: true, selector: "sce-hugeicon", inputs: { icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: true, transformFunction: null }, altIcon: { classPropertyName: "altIcon", publicName: "altIcon", isSignal: true, isRequired: false, transformFunction: null }, showAlt: { classPropertyName: "showAlt", publicName: "showAlt", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, strokeWidth: { classPropertyName: "strokeWidth", publicName: "strokeWidth", isSignal: true, isRequired: false, transformFunction: null }, absoluteStrokeWidth: { classPropertyName: "absoluteStrokeWidth", publicName: "absoluteStrokeWidth", isSignal: true, isRequired: false, transformFunction: null }, spin: { classPropertyName: "spin", publicName: "spin", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, flipRtl: { classPropertyName: "flipRtl", publicName: "flipRtl", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.sce-hugeicon--flip-rtl": "flipRtl()", "class.sce-hugeicon--spin": "spin()", "attr.role": "label() ? 'img' : null", "attr.aria-label": "label() || null", "attr.aria-hidden": "label() ? null : \"true\"" }, classAttribute: "sce-hugeicon" }, hostDirectives: [{ directive: i1.SceVariantDirective, inputs: ["sceVariant", "sceVariant", "sceDensity", "sceDensity"] }], ngImport: i0, template: "<span class=\"sce-hugeicon__glyph\" [class.sce-hugeicon__glyph--flip]=\"flipRtl()\">\n <hugeicons-icon\n [icon]=\"icon()\"\n [altIcon]=\"altIcon() ?? icon()\"\n [showAlt]=\"showAlt()\"\n [size]=\"size()\"\n [color]=\"color()\"\n [strokeWidth]=\"strokeWidth()\"\n [absoluteStrokeWidth]=\"absoluteStrokeWidth()\"\n />\n</span>\n", styles: [":host{display:inline-flex;align-items:center;justify-content:center;line-height:0;color:inherit}.sce-hugeicon__glyph{display:inline-flex;align-items:center;justify-content:center}:host-context([dir=rtl]) .sce-hugeicon__glyph--flip{transform:scaleX(-1)}@keyframes sce-hugeicon-spin{to{transform:rotate(360deg)}}:host(.sce-hugeicon--spin) .sce-hugeicon__glyph{animation:sce-hugeicon-spin 1s linear infinite}@media(prefers-reduced-motion:reduce){:host(.sce-hugeicon--spin) .sce-hugeicon__glyph{animation:none}}\n"], dependencies: [{ kind: "component", type: HugeiconsIconComponent, selector: "hugeicons-icon", inputs: ["size", "strokeWidth", "absoluteStrokeWidth", "icon", "altIcon", "color", "class", "showAlt"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
708
+ }
709
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SceHugeicon, decorators: [{
710
+ type: Component,
711
+ args: [{ hostDirectives: [{ directive: SceVariantDirective, inputs: ['sceVariant', 'sceDensity'] }], selector: 'sce-hugeicon', standalone: true, imports: [HugeiconsIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, host: {
712
+ class: 'sce-hugeicon',
713
+ '[class.sce-hugeicon--flip-rtl]': 'flipRtl()',
714
+ '[class.sce-hugeicon--spin]': 'spin()',
715
+ '[attr.role]': "label() ? 'img' : null",
716
+ '[attr.aria-label]': 'label() || null',
717
+ '[attr.aria-hidden]': 'label() ? null : "true"',
718
+ }, template: "<span class=\"sce-hugeicon__glyph\" [class.sce-hugeicon__glyph--flip]=\"flipRtl()\">\n <hugeicons-icon\n [icon]=\"icon()\"\n [altIcon]=\"altIcon() ?? icon()\"\n [showAlt]=\"showAlt()\"\n [size]=\"size()\"\n [color]=\"color()\"\n [strokeWidth]=\"strokeWidth()\"\n [absoluteStrokeWidth]=\"absoluteStrokeWidth()\"\n />\n</span>\n", styles: [":host{display:inline-flex;align-items:center;justify-content:center;line-height:0;color:inherit}.sce-hugeicon__glyph{display:inline-flex;align-items:center;justify-content:center}:host-context([dir=rtl]) .sce-hugeicon__glyph--flip{transform:scaleX(-1)}@keyframes sce-hugeicon-spin{to{transform:rotate(360deg)}}:host(.sce-hugeicon--spin) .sce-hugeicon__glyph{animation:sce-hugeicon-spin 1s linear infinite}@media(prefers-reduced-motion:reduce){:host(.sce-hugeicon--spin) .sce-hugeicon__glyph{animation:none}}\n"] }]
719
+ }], propDecorators: { icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: true }] }], altIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "altIcon", required: false }] }], showAlt: [{ type: i0.Input, args: [{ isSignal: true, alias: "showAlt", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], strokeWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "strokeWidth", required: false }] }], absoluteStrokeWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "absoluteStrokeWidth", required: false }] }], spin: [{ type: i0.Input, args: [{ isSignal: true, alias: "spin", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], flipRtl: [{ type: i0.Input, args: [{ isSignal: true, alias: "flipRtl", required: false }] }] } });
720
+
721
+ /** Every `SceSelectFieldVariant`, in gallery order. */
722
+ const SCE_SELECT_FIELD_VARIANTS = [
723
+ 'default',
724
+ 'outlined',
725
+ 'filled',
726
+ 'underline',
727
+ 'soft',
728
+ 'bordered',
729
+ 'elevated',
730
+ 'compact',
731
+ 'minimal',
732
+ 'pill',
733
+ 'addon',
734
+ ];
735
+ class SceSelectFieldComponent {
736
+ dropdownIcon = ArrowDown01Icon;
737
+ /** Visual treatment. */
738
+ variant = input('default', ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
739
+ mirrorLabelAsPlaceholder = inject(SCE_MIRROR_LABEL_AS_PLACEHOLDER, {
740
+ optional: true,
741
+ });
742
+ id = input.required(...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
743
+ label = input('', ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
744
+ options = input([], ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
745
+ group = input(false, ...(ngDevMode ? [{ debugName: "group" }] : /* istanbul ignore next */ []));
746
+ optionGroupLabel = input('label', ...(ngDevMode ? [{ debugName: "optionGroupLabel" }] : /* istanbul ignore next */ []));
747
+ optionGroupChildren = input('items', ...(ngDevMode ? [{ debugName: "optionGroupChildren" }] : /* istanbul ignore next */ []));
748
+ value = input(null, ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
749
+ placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
750
+ required = input(false, ...(ngDevMode ? [{ debugName: "required" }] : /* istanbul ignore next */ []));
751
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
752
+ helpText = input(...(ngDevMode ? [undefined, { debugName: "helpText" }] : /* istanbul ignore next */ []));
753
+ error = input(...(ngDevMode ? [undefined, { debugName: "error" }] : /* istanbul ignore next */ []));
754
+ filter = input(false, ...(ngDevMode ? [{ debugName: "filter" }] : /* istanbul ignore next */ []));
755
+ /**
756
+ * Caps characters in the dropdown filter/search box (PrimeNG has no built-in
757
+ * `maxlength` on that input). Applied when the overlay opens.
758
+ */
759
+ filterMaxLength = input(...(ngDevMode ? [undefined, { debugName: "filterMaxLength" }] : /* istanbul ignore next */ []));
760
+ showClear = input(false, ...(ngDevMode ? [{ debugName: "showClear" }] : /* istanbul ignore next */ []));
761
+ /**
762
+ * Explicit text direction. Default `null` infers from the content/document
763
+ * locale — a hardcoded `'rtl'` default rendered every unbound select RTL on
764
+ * English pages (and flipped direction when a disabled select became enabled).
765
+ */
766
+ dir = input(null, ...(ngDevMode ? [{ debugName: "dir" }] : /* istanbul ignore next */ []));
767
+ validationMode = input('onBlur', ...(ngDevMode ? [{ debugName: "validationMode" }] : /* istanbul ignore next */ []));
768
+ validate = input(...(ngDevMode ? [undefined, { debugName: "validate" }] : /* istanbul ignore next */ []));
769
+ validationMessages = input({}, ...(ngDevMode ? [{ debugName: "validationMessages" }] : /* istanbul ignore next */ []));
770
+ fieldClassName = input('', ...(ngDevMode ? [{ debugName: "fieldClassName" }] : /* istanbul ignore next */ []));
771
+ labelClassName = input('', ...(ngDevMode ? [{ debugName: "labelClassName" }] : /* istanbul ignore next */ []));
772
+ inputClassName = input('', ...(ngDevMode ? [{ debugName: "inputClassName" }] : /* istanbul ignore next */ []));
773
+ panelClassName = input('', ...(ngDevMode ? [{ debugName: "panelClassName" }] : /* istanbul ignore next */ []));
774
+ /** When true, hides the trigger border. Default false so `.p-select` keeps a visible border. */
775
+ borderless = input(false, ...(ngDevMode ? [{ debugName: "borderless" }] : /* istanbul ignore next */ []));
776
+ filterPlaceholder = input(...(ngDevMode ? [undefined, { debugName: "filterPlaceholder" }] : /* istanbul ignore next */ []));
777
+ showError = input(true, ...(ngDevMode ? [{ debugName: "showError" }] : /* istanbul ignore next */ []));
778
+ valueChange = output();
779
+ /** Optional wrapper class. Do not use `root` — file-upload and other None-encap components leak a global `.root`. */
780
+ containerClassName = input('', ...(ngDevMode ? [{ debugName: "containerClassName" }] : /* istanbul ignore next */ []));
781
+ /**
782
+ * Change this value to clear the field's interaction state (touched / blurred).
783
+ * Bump it when a parent form resets programmatically, so a cleared required
784
+ * field doesn't instantly show its "required" error next to a success banner.
785
+ */
786
+ resetToken = input(undefined, ...(ngDevMode ? [{ debugName: "resetToken" }] : /* istanbul ignore next */ []));
787
+ /** True after any user value change. Prevents required errors on open-and-close. */
788
+ dirty = signal(false, ...(ngDevMode ? [{ debugName: "dirty" }] : /* istanbul ignore next */ []));
789
+ /** True after any interaction (change or blur). Drives `'onTouched'` / `'touched'` mode. */
790
+ touched = signal(false, ...(ngDevMode ? [{ debugName: "touched" }] : /* istanbul ignore next */ []));
791
+ /** True only after the first blur. Drives the pure `'onBlur'` mode. */
792
+ blurredOnce = signal(false, ...(ngDevMode ? [{ debugName: "blurredOnce" }] : /* istanbul ignore next */ []));
793
+ clearInteractionOnReset = effect(() => {
794
+ this.resetToken();
795
+ this.dirty.set(false);
796
+ this.touched.set(false);
797
+ this.blurredOnce.set(false);
798
+ }, ...(ngDevMode ? [{ debugName: "clearInteractionOnReset" }] : /* istanbul ignore next */ []));
799
+ requiredMessage = computed(() => this.validationMessages()['required'] ?? (this.dir() === 'rtl' ? 'هذا الحقل مطلوب' : 'This field is required'), ...(ngDevMode ? [{ debugName: "requiredMessage" }] : /* istanbul ignore next */ []));
800
+ validationError = computed(() => {
801
+ const externalError = this.error();
802
+ if (externalError) {
803
+ return externalError;
804
+ }
805
+ const mode = this.validationMode();
806
+ if (mode === 'never') {
807
+ return null;
808
+ }
809
+ if (mode === 'onBlur' && (!this.blurredOnce() || !this.dirty())) {
810
+ return null;
811
+ }
812
+ if ((mode === 'onTouched' || mode === 'touched') && (!this.touched() || !this.dirty())) {
813
+ return null;
814
+ }
815
+ const value = this.boundValue();
816
+ if (this.required() && value == null) {
817
+ return this.requiredMessage();
818
+ }
819
+ const validateFn = this.validate();
820
+ if (validateFn) {
821
+ return validateFn(value);
822
+ }
823
+ return null;
824
+ }, ...(ngDevMode ? [{ debugName: "validationError" }] : /* istanbul ignore next */ []));
825
+ displayError = computed(() => (this.showError() ? this.validationError() : null), ...(ngDevMode ? [{ debugName: "displayError" }] : /* istanbul ignore next */ []));
826
+ resolvedPlaceholder = computed(() => {
827
+ const explicit = this.placeholder();
828
+ if (explicit) {
829
+ return explicit;
830
+ }
831
+ if (this.mirrorLabelAsPlaceholder === false) {
832
+ return '';
833
+ }
834
+ return this.label();
835
+ }, ...(ngDevMode ? [{ debugName: "resolvedPlaceholder" }] : /* istanbul ignore next */ []));
836
+ /**
837
+ * Local mirror so clear updates the UI immediately (before the parent round-trips).
838
+ * Empty/`undefined` stay `null` — PrimeNG keeps the clear icon when value is `''`.
839
+ */
840
+ boundValue = signal(null, ...(ngDevMode ? [{ debugName: "boundValue" }] : /* istanbul ignore next */ []));
841
+ syncBoundValueFromInput = effect(() => {
842
+ this.boundValue.set(this.normalizeValue(this.value()));
843
+ }, ...(ngDevMode ? [{ debugName: "syncBoundValueFromInput" }] : /* istanbul ignore next */ []));
844
+ selectedLabel = computed(() => {
845
+ const current = this.boundValue();
846
+ if (current == null) {
847
+ return '';
848
+ }
849
+ return this.options().find(option => String(option.value) === String(current))?.label ?? '';
850
+ }, ...(ngDevMode ? [{ debugName: "selectedLabel" }] : /* istanbul ignore next */ []));
851
+ /** Clear only when there is a real, matched selection — never for placeholder/orphan ids. */
852
+ clearVisible = computed(() => {
853
+ if (!this.showClear()) {
854
+ return false;
855
+ }
856
+ const current = this.boundValue();
857
+ if (current == null) {
858
+ return false;
859
+ }
860
+ return this.options().some(option => String(option.value) === String(current));
861
+ }, ...(ngDevMode ? [{ debugName: "clearVisible" }] : /* istanbul ignore next */ []));
862
+ selectRef = viewChild(Select, ...(ngDevMode ? [{ debugName: "selectRef" }] : /* istanbul ignore next */ []));
863
+ /**
864
+ * Body-portaled option panels must sit above `sce-popup` / `sce-dialog-shell`
865
+ * (`--z-modal` 2100). `OVERLAY_BASE_Z_INDEX` (1200) paints the menu under the mask.
866
+ */
867
+ overlayOptions = {
868
+ autoZIndex: true,
869
+ baseZIndex: DIALOG_SELECT_OVERLAY_Z_INDEX,
870
+ };
871
+ resolvedDir = computed(() => {
872
+ const explicit = this.dir();
873
+ if (this.disabled() || explicit === null) {
874
+ return inferInputTextDirection(this.selectedLabel(), { placeholder: this.resolvedPlaceholder() }, { preferDocumentLocale: true });
875
+ }
876
+ return explicit;
877
+ }, ...(ngDevMode ? [{ debugName: "resolvedDir" }] : /* istanbul ignore next */ []));
878
+ onBlur() {
879
+ this.touched.set(true);
880
+ this.blurredOnce.set(true);
881
+ }
882
+ /** PrimeNG Select filter input has no maxlength input — set it on open. */
883
+ onOverlayShow() {
884
+ const max = this.filterMaxLength();
885
+ if (max == null || max <= 0) {
886
+ return;
887
+ }
888
+ const filterEl = this.selectRef()?.filterViewChild?.nativeElement;
889
+ if (filterEl) {
890
+ filterEl.setAttribute('maxlength', String(max));
891
+ }
892
+ }
893
+ onValueChange(next) {
894
+ const normalized = this.normalizeValue(next);
895
+ this.boundValue.set(normalized);
896
+ this.dirty.set(true);
897
+ if (!this.touched()) {
898
+ this.touched.set(true);
899
+ }
900
+ this.valueChange.emit(normalized);
901
+ }
902
+ /** PrimeNG clear path — force null even if ngModelChange is skipped/racy. */
903
+ onClear() {
904
+ this.boundValue.set(null);
905
+ this.dirty.set(true);
906
+ if (!this.touched()) {
907
+ this.touched.set(true);
908
+ }
909
+ this.valueChange.emit(null);
910
+ }
911
+ normalizeValue(value) {
912
+ return value === '' || value === undefined || value === null ? null : value;
913
+ }
914
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SceSelectFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
915
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.17", type: SceSelectFieldComponent, isStandalone: true, selector: "sce-select-field", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, group: { classPropertyName: "group", publicName: "group", isSignal: true, isRequired: false, transformFunction: null }, optionGroupLabel: { classPropertyName: "optionGroupLabel", publicName: "optionGroupLabel", isSignal: true, isRequired: false, transformFunction: null }, optionGroupChildren: { classPropertyName: "optionGroupChildren", publicName: "optionGroupChildren", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, helpText: { classPropertyName: "helpText", publicName: "helpText", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: false, transformFunction: null }, filterMaxLength: { classPropertyName: "filterMaxLength", publicName: "filterMaxLength", isSignal: true, isRequired: false, transformFunction: null }, showClear: { classPropertyName: "showClear", publicName: "showClear", isSignal: true, isRequired: false, transformFunction: null }, dir: { classPropertyName: "dir", publicName: "dir", isSignal: true, isRequired: false, transformFunction: null }, validationMode: { classPropertyName: "validationMode", publicName: "validationMode", isSignal: true, isRequired: false, transformFunction: null }, validate: { classPropertyName: "validate", publicName: "validate", isSignal: true, isRequired: false, transformFunction: null }, validationMessages: { classPropertyName: "validationMessages", publicName: "validationMessages", isSignal: true, isRequired: false, transformFunction: null }, fieldClassName: { classPropertyName: "fieldClassName", publicName: "fieldClassName", isSignal: true, isRequired: false, transformFunction: null }, labelClassName: { classPropertyName: "labelClassName", publicName: "labelClassName", isSignal: true, isRequired: false, transformFunction: null }, inputClassName: { classPropertyName: "inputClassName", publicName: "inputClassName", isSignal: true, isRequired: false, transformFunction: null }, panelClassName: { classPropertyName: "panelClassName", publicName: "panelClassName", isSignal: true, isRequired: false, transformFunction: null }, borderless: { classPropertyName: "borderless", publicName: "borderless", isSignal: true, isRequired: false, transformFunction: null }, filterPlaceholder: { classPropertyName: "filterPlaceholder", publicName: "filterPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, showError: { classPropertyName: "showError", publicName: "showError", isSignal: true, isRequired: false, transformFunction: null }, containerClassName: { classPropertyName: "containerClassName", publicName: "containerClassName", isSignal: true, isRequired: false, transformFunction: null }, resetToken: { classPropertyName: "resetToken", publicName: "resetToken", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, host: { properties: { "class.sce-select-field--default": "variant() === \"default\"", "class.sce-select-field--outlined": "variant() === \"outlined\"", "class.sce-select-field--filled": "variant() === \"filled\"", "class.sce-select-field--underline": "variant() === \"underline\"", "class.sce-select-field--soft": "variant() === \"soft\"", "class.sce-select-field--bordered": "variant() === \"bordered\"", "class.sce-select-field--elevated": "variant() === \"elevated\"", "class.sce-select-field--compact": "variant() === \"compact\"", "class.sce-select-field--minimal": "variant() === \"minimal\"", "class.sce-select-field--pill": "variant() === \"pill\"", "class.sce-select-field--addon": "variant() === \"addon\"", "class.sce-select-field--locked": "disabled()" }, classAttribute: "sce-select-field" }, viewQueries: [{ propertyName: "selectRef", first: true, predicate: Select, descendants: true, isSignal: true }], hostDirectives: [{ directive: i1.SceVariantDirective, inputs: ["sceVariant", "sceVariant", "sceDensity", "sceDensity"] }], ngImport: i0, template: "<div class=\"sce-select-field__wrap\" [class]=\"containerClassName() || null\" [attr.dir]=\"resolvedDir()\">\n <sce-field\n [id]=\"id()\"\n [label]=\"label()\"\n [required]=\"required()\"\n [helpText]=\"helpText()\"\n [error]=\"displayError()\"\n [labelClassName]=\"labelClassName()\"\n [fieldClassName]=\"fieldClassName()\"\n >\n <p-select\n [inputId]=\"id()\"\n [ariaLabel]=\"label() || resolvedPlaceholder()\"\n [options]=\"options()\"\n optionLabel=\"label\"\n optionValue=\"value\"\n optionDisabled=\"disabled\"\n [group]=\"group()\"\n [optionGroupLabel]=\"optionGroupLabel()\"\n [optionGroupChildren]=\"optionGroupChildren()\"\n [placeholder]=\"resolvedPlaceholder()\"\n [disabled]=\"disabled()\"\n [filter]=\"filter()\"\n [filterPlaceholder]=\"filterPlaceholder() ?? ''\"\n [showClear]=\"clearVisible()\"\n [ngModel]=\"boundValue()\"\n (ngModelChange)=\"onValueChange($event)\"\n (onClear)=\"onClear()\"\n (onBlur)=\"onBlur()\"\n (onShow)=\"onOverlayShow()\"\n [class.p-invalid]=\"displayError()\"\n [class.sce-select--borderless]=\"borderless()\"\n [styleClass]=\"'w-full ' + inputClassName()\"\n [panelStyleClass]=\"panelClassName()\"\n dropdownIcon=\"pi pi-chevron-down\"\n [overlayOptions]=\"overlayOptions\"\n appendTo=\"body\"\n >\n <ng-template #dropdownicon>\n <sce-hugeicon [icon]=\"dropdownIcon\" [size]=\"16\" />\n </ng-template>\n </p-select>\n </sce-field>\n</div>\n", styles: ["@charset \"UTF-8\";.sce-select-field .p-select{position:relative;width:100%;height:var(--size-40);min-height:var(--size-40);max-height:var(--size-40);border-radius:var(--radius-lg);border:1px solid var(--color-input-border, var(--color-neutral-200));box-sizing:border-box;transition:border-color .15s ease;display:flex;align-items:center}.sce-select-field .p-select:after{content:\"\";position:absolute;inset-block-end:0;inset-inline:50%;z-index:1;height:2px;background:var(--color-neutral-950);border-end-start-radius:var(--radius-sm);border-end-end-radius:var(--radius-sm);pointer-events:none;transition:inset-inline .15s ease}.sce-select-field .p-select:focus-within:not(:has(:disabled)):not(:has([readonly])):not(.p-disabled):not(.disabled):not(.readonly):after,.sce-select-field .p-select:active:not(:has(:disabled)):not(:has([readonly])):not(.p-disabled):not(.disabled):not(.readonly):after{inset-inline:0}.sce-select-field .p-select.p-focus:not(.p-disabled):after{inset-inline:0}.sce-select-field .p-select:not(.sce-select--borderless){border:1px solid var(--color-input-border, var(--color-neutral-200))}.sce-select-field .p-select:not(.p-disabled):hover{border-color:var(--field-border-hovered)}.sce-select-field .p-select.sce-select--borderless:not(.p-invalid){border-color:transparent;border-radius:var(--radius-sm)}.sce-select-field .p-select.p-select.p-invalid{border-color:var(--color-input-border, var(--color-neutral-200))}.sce-select-field .p-select.p-select.p-invalid:not(.p-disabled).p-focus{border-color:var(--color-input-border, var(--color-neutral-200));box-shadow:none}.sce-select-field .p-select .p-select-dropdown{position:relative;inset:auto;display:flex;align-items:center;justify-content:center;align-self:stretch;flex-shrink:0;width:var(--space-40);margin:0;background:transparent;border:none;color:var(--form-field-icon-color, var(--form-field-text-placeholder))}.sce-select-field .p-select .p-select-dropdown-icon,.sce-select-field .p-select .p-select-dropdown .pi{font-size:var(--font-size-xs);line-height:1;display:inline-flex;align-items:center;justify-content:center}.sce-select-field .p-select .p-select-clear-icon{position:relative;inset:auto;flex-shrink:0;align-self:center;margin-inline-start:0;margin-inline-end:var(--space-4);color:var(--form-field-icon-color, var(--form-field-text-placeholder))}.sce-select-field .p-select .p-select-label{display:flex;align-items:center;flex:1 1 auto;min-width:0;direction:inherit;text-align:start;justify-content:flex-start;width:auto;margin:0;font-family:var(--font-family-arabic-primary);font-size:var(--font-size-md);color:var(--details-value-color);padding-block:0;padding-inline:var(--space-14)}.sce-select-field .sce-select-field__wrap[dir=rtl] .p-select .p-select-label,.sce-select-field .sce-select-field__wrap[dir=rtl] .p-select .p-select-label.p-placeholder{direction:rtl;text-align:start}.sce-select-field .sce-select-field__wrap[dir=ltr] .p-select .p-select-label,.sce-select-field .sce-select-field__wrap[dir=ltr] .p-select .p-select-label.p-placeholder{direction:ltr;text-align:start}.sce-select-field .p-select .p-select-label.p-placeholder{color:var(--form-input-placeholder-color);opacity:1}sce-select-field.sce-select-field--locked .sce-select-field__wrap .p-select.p-disabled{opacity:1;background-color:var(--form-input-disabled-bg, var(--pdf-field-bg-alt))}sce-select-field.sce-select-field--locked .sce-select-field__wrap .p-select.p-disabled .p-select-label:not(.p-placeholder){color:var(--form-input-disabled-text, var(--text-text-disabled))}sce-select-field.sce-select-field--locked .sce-select-field__wrap[dir=rtl] .p-select.p-disabled .p-select-label:not(.p-placeholder){direction:rtl;text-align:start;justify-content:flex-start}sce-select-field.sce-select-field--locked .sce-select-field__wrap[dir=ltr] .p-select.p-disabled .p-select-label:not(.p-placeholder){direction:ltr;text-align:start;justify-content:flex-start}.sce-select-field .p-select-overlay,.p-select-overlay{z-index:var(--z-select-overlay);border-radius:var(--radius-sm);overflow:hidden;background:var(--dropdown-panel-bg);box-shadow:var(--dropdown-panel-shadow);color:var(--details-value-color)}.p-select-overlay .p-select-list .p-select-option{transition:background-color .12s ease;text-align:start}.p-select-overlay .p-select-list .p-select-option-group{font-weight:var(--font-weight-bold);font-size:var(--font-size-xs);color:var(--color-text-secondary);background-color:var(--color-surface-muted, var(--color-neutral-50));padding-block:var(--space-6);padding-inline:var(--space-12);-webkit-user-select:none;user-select:none;cursor:default;border-block-end:1px solid var(--color-border-light, var(--color-neutral-100))}.p-select-overlay .p-select-header .p-select-filter.p-select-filter{margin-inline-end:unset}.sce-select-field--outlined{border:1px solid var(--color-border-light);background:transparent}.sce-select-field--filled{background:var(--color-surface-muted)}.sce-select-field--underline{border:0;border-block-end:1px solid var(--color-border-light);border-radius:0}.sce-select-field--soft{background:var(--color-brand-primary-25)}.sce-select-field--bordered{border-inline-start:var(--space-3) solid var(--color-brand-primary-600)}.sce-select-field--elevated{border-radius:var(--radius-lg);background:var(--color-surface);box-shadow:var(--shadow-card-soft-subtle)}.sce-select-field--compact{padding-block:var(--space-4);font-size:var(--font-size-sm)}.sce-select-field--minimal{background:transparent;border:0;box-shadow:none}.sce-select-field--pill{border-radius:var(--radius-full)}.sce-select-field--addon .p-select{overflow:hidden}.sce-select-field--addon .p-select .p-select-dropdown{width:var(--size-53);isolation:isolate}.sce-select-field--addon .p-select .p-select-dropdown:before{content:\"\";position:absolute;inset:0;z-index:-1;background:var(--color-neutral-100)}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "component", type: SceFieldComponent, selector: "sce-field", inputs: ["id", "label", "required", "hideLabel", "helpText", "error", "labelClassName", "fieldClassName"] }, { kind: "component", type: SceHugeicon, selector: "sce-hugeicon", inputs: ["icon", "altIcon", "showAlt", "size", "color", "strokeWidth", "absoluteStrokeWidth", "spin", "label", "flipRtl"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
916
+ }
917
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SceSelectFieldComponent, decorators: [{
918
+ type: Component,
919
+ args: [{ hostDirectives: [{ directive: SceVariantDirective, inputs: ['sceVariant', 'sceDensity'] }], selector: 'sce-select-field', standalone: true, imports: [FormsModule, Select, SceFieldComponent, SceHugeicon], encapsulation: ViewEncapsulation.None, host: {
920
+ class: 'sce-select-field',
921
+ '[class.sce-select-field--default]': 'variant() === "default"',
922
+ '[class.sce-select-field--outlined]': 'variant() === "outlined"',
923
+ '[class.sce-select-field--filled]': 'variant() === "filled"',
924
+ '[class.sce-select-field--underline]': 'variant() === "underline"',
925
+ '[class.sce-select-field--soft]': 'variant() === "soft"',
926
+ '[class.sce-select-field--bordered]': 'variant() === "bordered"',
927
+ '[class.sce-select-field--elevated]': 'variant() === "elevated"',
928
+ '[class.sce-select-field--compact]': 'variant() === "compact"',
929
+ '[class.sce-select-field--minimal]': 'variant() === "minimal"',
930
+ '[class.sce-select-field--pill]': 'variant() === "pill"',
931
+ '[class.sce-select-field--addon]': 'variant() === "addon"',
932
+ '[class.sce-select-field--locked]': 'disabled()',
933
+ }, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"sce-select-field__wrap\" [class]=\"containerClassName() || null\" [attr.dir]=\"resolvedDir()\">\n <sce-field\n [id]=\"id()\"\n [label]=\"label()\"\n [required]=\"required()\"\n [helpText]=\"helpText()\"\n [error]=\"displayError()\"\n [labelClassName]=\"labelClassName()\"\n [fieldClassName]=\"fieldClassName()\"\n >\n <p-select\n [inputId]=\"id()\"\n [ariaLabel]=\"label() || resolvedPlaceholder()\"\n [options]=\"options()\"\n optionLabel=\"label\"\n optionValue=\"value\"\n optionDisabled=\"disabled\"\n [group]=\"group()\"\n [optionGroupLabel]=\"optionGroupLabel()\"\n [optionGroupChildren]=\"optionGroupChildren()\"\n [placeholder]=\"resolvedPlaceholder()\"\n [disabled]=\"disabled()\"\n [filter]=\"filter()\"\n [filterPlaceholder]=\"filterPlaceholder() ?? ''\"\n [showClear]=\"clearVisible()\"\n [ngModel]=\"boundValue()\"\n (ngModelChange)=\"onValueChange($event)\"\n (onClear)=\"onClear()\"\n (onBlur)=\"onBlur()\"\n (onShow)=\"onOverlayShow()\"\n [class.p-invalid]=\"displayError()\"\n [class.sce-select--borderless]=\"borderless()\"\n [styleClass]=\"'w-full ' + inputClassName()\"\n [panelStyleClass]=\"panelClassName()\"\n dropdownIcon=\"pi pi-chevron-down\"\n [overlayOptions]=\"overlayOptions\"\n appendTo=\"body\"\n >\n <ng-template #dropdownicon>\n <sce-hugeicon [icon]=\"dropdownIcon\" [size]=\"16\" />\n </ng-template>\n </p-select>\n </sce-field>\n</div>\n", styles: ["@charset \"UTF-8\";.sce-select-field .p-select{position:relative;width:100%;height:var(--size-40);min-height:var(--size-40);max-height:var(--size-40);border-radius:var(--radius-lg);border:1px solid var(--color-input-border, var(--color-neutral-200));box-sizing:border-box;transition:border-color .15s ease;display:flex;align-items:center}.sce-select-field .p-select:after{content:\"\";position:absolute;inset-block-end:0;inset-inline:50%;z-index:1;height:2px;background:var(--color-neutral-950);border-end-start-radius:var(--radius-sm);border-end-end-radius:var(--radius-sm);pointer-events:none;transition:inset-inline .15s ease}.sce-select-field .p-select:focus-within:not(:has(:disabled)):not(:has([readonly])):not(.p-disabled):not(.disabled):not(.readonly):after,.sce-select-field .p-select:active:not(:has(:disabled)):not(:has([readonly])):not(.p-disabled):not(.disabled):not(.readonly):after{inset-inline:0}.sce-select-field .p-select.p-focus:not(.p-disabled):after{inset-inline:0}.sce-select-field .p-select:not(.sce-select--borderless){border:1px solid var(--color-input-border, var(--color-neutral-200))}.sce-select-field .p-select:not(.p-disabled):hover{border-color:var(--field-border-hovered)}.sce-select-field .p-select.sce-select--borderless:not(.p-invalid){border-color:transparent;border-radius:var(--radius-sm)}.sce-select-field .p-select.p-select.p-invalid{border-color:var(--color-input-border, var(--color-neutral-200))}.sce-select-field .p-select.p-select.p-invalid:not(.p-disabled).p-focus{border-color:var(--color-input-border, var(--color-neutral-200));box-shadow:none}.sce-select-field .p-select .p-select-dropdown{position:relative;inset:auto;display:flex;align-items:center;justify-content:center;align-self:stretch;flex-shrink:0;width:var(--space-40);margin:0;background:transparent;border:none;color:var(--form-field-icon-color, var(--form-field-text-placeholder))}.sce-select-field .p-select .p-select-dropdown-icon,.sce-select-field .p-select .p-select-dropdown .pi{font-size:var(--font-size-xs);line-height:1;display:inline-flex;align-items:center;justify-content:center}.sce-select-field .p-select .p-select-clear-icon{position:relative;inset:auto;flex-shrink:0;align-self:center;margin-inline-start:0;margin-inline-end:var(--space-4);color:var(--form-field-icon-color, var(--form-field-text-placeholder))}.sce-select-field .p-select .p-select-label{display:flex;align-items:center;flex:1 1 auto;min-width:0;direction:inherit;text-align:start;justify-content:flex-start;width:auto;margin:0;font-family:var(--font-family-arabic-primary);font-size:var(--font-size-md);color:var(--details-value-color);padding-block:0;padding-inline:var(--space-14)}.sce-select-field .sce-select-field__wrap[dir=rtl] .p-select .p-select-label,.sce-select-field .sce-select-field__wrap[dir=rtl] .p-select .p-select-label.p-placeholder{direction:rtl;text-align:start}.sce-select-field .sce-select-field__wrap[dir=ltr] .p-select .p-select-label,.sce-select-field .sce-select-field__wrap[dir=ltr] .p-select .p-select-label.p-placeholder{direction:ltr;text-align:start}.sce-select-field .p-select .p-select-label.p-placeholder{color:var(--form-input-placeholder-color);opacity:1}sce-select-field.sce-select-field--locked .sce-select-field__wrap .p-select.p-disabled{opacity:1;background-color:var(--form-input-disabled-bg, var(--pdf-field-bg-alt))}sce-select-field.sce-select-field--locked .sce-select-field__wrap .p-select.p-disabled .p-select-label:not(.p-placeholder){color:var(--form-input-disabled-text, var(--text-text-disabled))}sce-select-field.sce-select-field--locked .sce-select-field__wrap[dir=rtl] .p-select.p-disabled .p-select-label:not(.p-placeholder){direction:rtl;text-align:start;justify-content:flex-start}sce-select-field.sce-select-field--locked .sce-select-field__wrap[dir=ltr] .p-select.p-disabled .p-select-label:not(.p-placeholder){direction:ltr;text-align:start;justify-content:flex-start}.sce-select-field .p-select-overlay,.p-select-overlay{z-index:var(--z-select-overlay);border-radius:var(--radius-sm);overflow:hidden;background:var(--dropdown-panel-bg);box-shadow:var(--dropdown-panel-shadow);color:var(--details-value-color)}.p-select-overlay .p-select-list .p-select-option{transition:background-color .12s ease;text-align:start}.p-select-overlay .p-select-list .p-select-option-group{font-weight:var(--font-weight-bold);font-size:var(--font-size-xs);color:var(--color-text-secondary);background-color:var(--color-surface-muted, var(--color-neutral-50));padding-block:var(--space-6);padding-inline:var(--space-12);-webkit-user-select:none;user-select:none;cursor:default;border-block-end:1px solid var(--color-border-light, var(--color-neutral-100))}.p-select-overlay .p-select-header .p-select-filter.p-select-filter{margin-inline-end:unset}.sce-select-field--outlined{border:1px solid var(--color-border-light);background:transparent}.sce-select-field--filled{background:var(--color-surface-muted)}.sce-select-field--underline{border:0;border-block-end:1px solid var(--color-border-light);border-radius:0}.sce-select-field--soft{background:var(--color-brand-primary-25)}.sce-select-field--bordered{border-inline-start:var(--space-3) solid var(--color-brand-primary-600)}.sce-select-field--elevated{border-radius:var(--radius-lg);background:var(--color-surface);box-shadow:var(--shadow-card-soft-subtle)}.sce-select-field--compact{padding-block:var(--space-4);font-size:var(--font-size-sm)}.sce-select-field--minimal{background:transparent;border:0;box-shadow:none}.sce-select-field--pill{border-radius:var(--radius-full)}.sce-select-field--addon .p-select{overflow:hidden}.sce-select-field--addon .p-select .p-select-dropdown{width:var(--size-53);isolation:isolate}.sce-select-field--addon .p-select .p-select-dropdown:before{content:\"\";position:absolute;inset:0;z-index:-1;background:var(--color-neutral-100)}\n"] }]
934
+ }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], group: [{ type: i0.Input, args: [{ isSignal: true, alias: "group", required: false }] }], optionGroupLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionGroupLabel", required: false }] }], optionGroupChildren: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionGroupChildren", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], helpText: [{ type: i0.Input, args: [{ isSignal: true, alias: "helpText", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: false }] }], filterMaxLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterMaxLength", required: false }] }], showClear: [{ type: i0.Input, args: [{ isSignal: true, alias: "showClear", required: false }] }], dir: [{ type: i0.Input, args: [{ isSignal: true, alias: "dir", required: false }] }], validationMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "validationMode", required: false }] }], validate: [{ type: i0.Input, args: [{ isSignal: true, alias: "validate", required: false }] }], validationMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "validationMessages", required: false }] }], fieldClassName: [{ type: i0.Input, args: [{ isSignal: true, alias: "fieldClassName", required: false }] }], labelClassName: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelClassName", required: false }] }], inputClassName: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputClassName", required: false }] }], panelClassName: [{ type: i0.Input, args: [{ isSignal: true, alias: "panelClassName", required: false }] }], borderless: [{ type: i0.Input, args: [{ isSignal: true, alias: "borderless", required: false }] }], filterPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterPlaceholder", required: false }] }], showError: [{ type: i0.Input, args: [{ isSignal: true, alias: "showError", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], containerClassName: [{ type: i0.Input, args: [{ isSignal: true, alias: "containerClassName", required: false }] }], resetToken: [{ type: i0.Input, args: [{ isSignal: true, alias: "resetToken", required: false }] }], selectRef: [{ type: i0.ViewChild, args: [i0.forwardRef(() => Select), { isSignal: true }] }] } });
935
+
936
+ /**
937
+ * React source: feedback/inline-api-error.tsx
938
+ */
939
+ /** Inline error banner for an already-resolved `message`, showing a retry button only when `retryable` is opted into (it defaults to false so a failed save cannot re-trigger a load). */
940
+ class SceInlineApiErrorComponent {
941
+ errorIcon = AlertCircleIcon;
942
+ retryIcon = RefreshIcon;
943
+ message = input.required(...(ngDevMode ? [{ debugName: "message" }] : /* istanbul ignore next */ []));
944
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
945
+ /**
946
+ * Shows a retry button. Opt in only where the banner reports a *load* failure — on a form
947
+ * page the same banner reports a failed save, where re-running the load would discard input.
948
+ */
949
+ retryable = input(false, ...(ngDevMode ? [{ debugName: "retryable" }] : /* istanbul ignore next */ []));
950
+ /** The host re-runs the request that failed. */
951
+ retry = output();
952
+ rootClass = computed(() => {
953
+ const cls = this.className();
954
+ return cls ? `inlineApiErrorRoot ${cls}` : 'inlineApiErrorRoot';
955
+ }, ...(ngDevMode ? [{ debugName: "rootClass" }] : /* istanbul ignore next */ []));
956
+ onRetry() {
957
+ this.retry.emit();
958
+ }
959
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SceInlineApiErrorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
960
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SceInlineApiErrorComponent, isStandalone: true, selector: "sce-inline-api-error", inputs: { message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: true, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, retryable: { classPropertyName: "retryable", publicName: "retryable", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { retry: "retry" }, host: { classAttribute: "sce-inline-api-error" }, hostDirectives: [{ directive: i1.SceVariantDirective, inputs: ["sceVariant", "sceVariant", "sceDensity", "sceDensity"] }], ngImport: i0, template: "@if (message()) {\n <!-- role=alert already implies an assertive live region; adding aria-live=polite contradicts it -->\n <div [class]=\"rootClass()\" role=\"alert\">\n <sce-hugeicon class=\"inlineApiErrorIcon\" [icon]=\"errorIcon\" [size]=\"20\" />\n <p class=\"inlineApiErrorMessage\">{{ message() }}</p>\n @if (retryable()) {\n <button type=\"button\" class=\"inlineApiErrorRetry\" (click)=\"onRetry()\">\n <sce-hugeicon [icon]=\"retryIcon\" [size]=\"16\" />\n {{ 'STATUS_PAGES.RETRY' | translate }}\n </button>\n }\n </div>\n}\n", styles: [".inlineApiErrorRoot{display:flex;align-items:center;gap:var(--space-12);margin-block-start:var(--space-12);padding:var(--space-12) var(--space-16);border-radius:var(--radius-sm);border:var(--border-width-1) solid var(--feedback-inline-error-border);border-inline-start-width:var(--border-width-2);border-inline-start-color:var(--feedback-inline-error-text);background:var(--feedback-inline-error-bg);color:var(--feedback-inline-error-text);min-width:0;max-width:100%;box-shadow:var(--shadow-sm)}.inlineApiErrorIcon{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:var(--space-32);height:var(--space-32);border-radius:var(--radius-full);background:var(--color-attention-200);font-size:var(--font-size-md);line-height:var(--line-height-none)}.inlineApiErrorMessage{margin:0;font-size:var(--font-size-md);font-weight:var(--font-weight-semibold);line-height:var(--line-height-snug);min-width:0;max-width:100%;overflow-wrap:anywhere;overflow-wrap:break-word;flex:1 1 auto}.inlineApiErrorRetry{display:inline-flex;align-items:center;justify-content:center;gap:var(--space-6);flex-shrink:0;min-height:var(--space-32);padding-block:var(--space-4);padding-inline:var(--space-12);border:var(--border-width-1) solid currentcolor;border-radius:var(--radius-sm);background:var(--app-surface);color:inherit;font-size:var(--font-size-sm);font-weight:var(--font-weight-semibold);cursor:pointer;transition:background-color var(--transition-base),box-shadow var(--transition-base)}.inlineApiErrorRetry:hover{background:var(--feedback-inline-error-border);box-shadow:var(--shadow-sm)}.inlineApiErrorRetry:focus-visible{outline:var(--border-width-2) solid currentcolor;outline-offset:var(--space-2)}@media(max-width:480px){.inlineApiErrorRoot{flex-wrap:wrap}.inlineApiErrorRetry{inline-size:100%}}@media(prefers-reduced-motion:reduce){.inlineApiErrorRetry{transition:none}}\n"], dependencies: [{ kind: "component", type: SceHugeicon, selector: "sce-hugeicon", inputs: ["icon", "altIcon", "showAlt", "size", "color", "strokeWidth", "absoluteStrokeWidth", "spin", "label", "flipRtl"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
961
+ }
962
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SceInlineApiErrorComponent, decorators: [{
963
+ type: Component,
964
+ args: [{ hostDirectives: [{ directive: SceVariantDirective, inputs: ['sceVariant', 'sceDensity'] }], host: { class: 'sce-inline-api-error' }, selector: 'sce-inline-api-error', standalone: true, imports: [TranslatePipe, SceHugeicon], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (message()) {\n <!-- role=alert already implies an assertive live region; adding aria-live=polite contradicts it -->\n <div [class]=\"rootClass()\" role=\"alert\">\n <sce-hugeicon class=\"inlineApiErrorIcon\" [icon]=\"errorIcon\" [size]=\"20\" />\n <p class=\"inlineApiErrorMessage\">{{ message() }}</p>\n @if (retryable()) {\n <button type=\"button\" class=\"inlineApiErrorRetry\" (click)=\"onRetry()\">\n <sce-hugeicon [icon]=\"retryIcon\" [size]=\"16\" />\n {{ 'STATUS_PAGES.RETRY' | translate }}\n </button>\n }\n </div>\n}\n", styles: [".inlineApiErrorRoot{display:flex;align-items:center;gap:var(--space-12);margin-block-start:var(--space-12);padding:var(--space-12) var(--space-16);border-radius:var(--radius-sm);border:var(--border-width-1) solid var(--feedback-inline-error-border);border-inline-start-width:var(--border-width-2);border-inline-start-color:var(--feedback-inline-error-text);background:var(--feedback-inline-error-bg);color:var(--feedback-inline-error-text);min-width:0;max-width:100%;box-shadow:var(--shadow-sm)}.inlineApiErrorIcon{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:var(--space-32);height:var(--space-32);border-radius:var(--radius-full);background:var(--color-attention-200);font-size:var(--font-size-md);line-height:var(--line-height-none)}.inlineApiErrorMessage{margin:0;font-size:var(--font-size-md);font-weight:var(--font-weight-semibold);line-height:var(--line-height-snug);min-width:0;max-width:100%;overflow-wrap:anywhere;overflow-wrap:break-word;flex:1 1 auto}.inlineApiErrorRetry{display:inline-flex;align-items:center;justify-content:center;gap:var(--space-6);flex-shrink:0;min-height:var(--space-32);padding-block:var(--space-4);padding-inline:var(--space-12);border:var(--border-width-1) solid currentcolor;border-radius:var(--radius-sm);background:var(--app-surface);color:inherit;font-size:var(--font-size-sm);font-weight:var(--font-weight-semibold);cursor:pointer;transition:background-color var(--transition-base),box-shadow var(--transition-base)}.inlineApiErrorRetry:hover{background:var(--feedback-inline-error-border);box-shadow:var(--shadow-sm)}.inlineApiErrorRetry:focus-visible{outline:var(--border-width-2) solid currentcolor;outline-offset:var(--space-2)}@media(max-width:480px){.inlineApiErrorRoot{flex-wrap:wrap}.inlineApiErrorRetry{inline-size:100%}}@media(prefers-reduced-motion:reduce){.inlineApiErrorRetry{transition:none}}\n"] }]
965
+ }], propDecorators: { message: [{ type: i0.Input, args: [{ isSignal: true, alias: "message", required: true }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], retryable: [{ type: i0.Input, args: [{ isSignal: true, alias: "retryable", required: false }] }], retry: [{ type: i0.Output, args: ["retry"] }] } });
966
+
967
+ const PARCEL_NOT_FOUND_MESSAGE = 'لم يتم العثور على أرض محددة في هذه النقطة. يرجى اختيار موقع أرض داخل مخطط.';
968
+ const RATE_LIMITED_MESSAGE = 'تم تجاوز عدد الطلبات المسموح به. يرجى الانتظار قليلًا ثم المحاولة مرة أخرى.';
969
+ /** Hold-off after a 429 that carried no `Retry-After`. */
970
+ const DEFAULT_RATE_LIMIT_BACKOFF_SECONDS = 5;
971
+ /** Fallback center coordinates for Saudi Arabia regions */
972
+ const SAUDI_REGION_CENTERS = {
973
+ '001': { lat: 24.7136, lng: 46.6753, zoom: 8 }, // Riyadh
974
+ '002': { lat: 21.3891, lng: 39.8579, zoom: 8 }, // Makkah
975
+ '003': { lat: 24.5247, lng: 39.5692, zoom: 8 }, // Madinah
976
+ '004': { lat: 26.326, lng: 43.975, zoom: 8 }, // Al Qassim
977
+ '005': { lat: 26.4207, lng: 50.0888, zoom: 7 }, // Eastern
978
+ '006': { lat: 18.2164, lng: 42.5053, zoom: 8 }, // Asir
979
+ '007': { lat: 28.3835, lng: 36.5662, zoom: 8 }, // Tabuk
980
+ '008': { lat: 27.5236, lng: 41.6961, zoom: 8 }, // Hail
981
+ '009': { lat: 30.9753, lng: 41.0381, zoom: 8 }, // Northern Borders
982
+ '010': { lat: 16.8894, lng: 42.5511, zoom: 9 }, // Jazan
983
+ '011': { lat: 17.4924, lng: 44.1277, zoom: 8 }, // Najran
984
+ '012': { lat: 20.0129, lng: 41.4676, zoom: 9 }, // Al Baha
985
+ '013': { lat: 29.9697, lng: 40.1963, zoom: 8 }, // Al Jouf
986
+ };
987
+ function calculateRingsCenter(rings) {
988
+ if (!rings || !rings.length || !rings[0].length) {
989
+ return null;
990
+ }
991
+ const points = rings[0];
992
+ let sumLng = 0;
993
+ let sumLat = 0;
994
+ let count = 0;
995
+ for (const pt of points) {
996
+ if (Array.isArray(pt) && pt.length >= 2) {
997
+ sumLng += pt[0];
998
+ sumLat += pt[1];
999
+ count++;
1000
+ }
1001
+ }
1002
+ if (count === 0) {
1003
+ return null;
1004
+ }
1005
+ return { lat: sumLat / count, lng: sumLng / count };
1006
+ }
1007
+ class SceBaladyMapSelectorComponent {
1008
+ locale = input('ar', ...(ngDevMode ? [{ debugName: "locale" }] : /* istanbul ignore next */ []));
1009
+ dir = input('rtl', ...(ngDevMode ? [{ debugName: "dir" }] : /* istanbul ignore next */ []));
1010
+ initialPropertyData = input(null, ...(ngDevMode ? [{ debugName: "initialPropertyData" }] : /* istanbul ignore next */ []));
1011
+ propertySelected = output();
1012
+ mapClick = output();
1013
+ gisService = inject(BaladyGisService);
1014
+ toast = inject(ToastService);
1015
+ lastQueriedCoords = null;
1016
+ /** Epoch ms until which point queries are held back after a 429 from the GIS proxy. */
1017
+ pointQueryBlockedUntil = 0;
1018
+ constructor() {
1019
+ effect(() => {
1020
+ const initData = this.initialPropertyData();
1021
+ if (initData && (initData.x != null || initData.y != null || initData.parcelLatitude != null)) {
1022
+ const lat = Number(initData.y ?? initData.parcelLatitude ?? initData.lat);
1023
+ const lng = Number(initData.x ?? initData.parcelLongitude ?? initData.lng);
1024
+ if (!isNaN(lat) && !isNaN(lng) && lat !== 0 && lng !== 0) {
1025
+ this.currentLat.set(lat);
1026
+ this.currentLng.set(lng);
1027
+ this.zoomLevel.set(18);
1028
+ if (!this.selectedPropertyData()) {
1029
+ this.selectedPropertyData.set(initData);
1030
+ }
1031
+ const popupHtml = `
1032
+ <div style="font-family: inherit; font-size: 13px; line-height: 1.5; padding: 4px;">
1033
+ <strong>رقم القطعة:</strong> ${initData.parcelNo ?? '-'}<br/>
1034
+ <strong>رقم المخطط:</strong> ${initData.planNO ?? '-'}<br/>
1035
+ <strong>الاستخدام:</strong> ${initData.landuse ?? '-'}<br/>
1036
+ <strong>المساحة:</strong> ${this.formatArea(initData.area)}
1037
+ </div>
1038
+ `;
1039
+ this.currentMarkers.set([
1040
+ {
1041
+ draggable: false,
1042
+ lat,
1043
+ lng,
1044
+ popupHtml,
1045
+ },
1046
+ ]);
1047
+ this.queryPointData(lng, lat, { emit: false });
1048
+ }
1049
+ }
1050
+ });
1051
+ }
1052
+ // In-flight API Subscriptions for cancellation
1053
+ regionsSub;
1054
+ citiesSub;
1055
+ districtsSub;
1056
+ pointDataSub;
1057
+ // Filter Signals
1058
+ regions = signal([], ...(ngDevMode ? [{ debugName: "regions" }] : /* istanbul ignore next */ []));
1059
+ cities = signal([], ...(ngDevMode ? [{ debugName: "cities" }] : /* istanbul ignore next */ []));
1060
+ districts = signal([], ...(ngDevMode ? [{ debugName: "districts" }] : /* istanbul ignore next */ []));
1061
+ selectedRegionCode = signal('', ...(ngDevMode ? [{ debugName: "selectedRegionCode" }] : /* istanbul ignore next */ []));
1062
+ selectedCityCode = signal('', ...(ngDevMode ? [{ debugName: "selectedCityCode" }] : /* istanbul ignore next */ []));
1063
+ selectedDistrictCode = signal('', ...(ngDevMode ? [{ debugName: "selectedDistrictCode" }] : /* istanbul ignore next */ []));
1064
+ formatArea(area) {
1065
+ if (!area) {
1066
+ return '-';
1067
+ }
1068
+ const num = parseFloat(area);
1069
+ if (isNaN(num)) {
1070
+ return area;
1071
+ }
1072
+ return `${num.toFixed(2)} م²`;
1073
+ }
1074
+ // Map Signals
1075
+ currentLat = signal(24.7136, ...(ngDevMode ? [{ debugName: "currentLat" }] : /* istanbul ignore next */ []));
1076
+ currentLng = signal(46.6753, ...(ngDevMode ? [{ debugName: "currentLng" }] : /* istanbul ignore next */ []));
1077
+ zoomLevel = signal(6, ...(ngDevMode ? [{ debugName: "zoomLevel" }] : /* istanbul ignore next */ []));
1078
+ currentMarkers = signal([], ...(ngDevMode ? [{ debugName: "currentMarkers" }] : /* istanbul ignore next */ []));
1079
+ // State Signals
1080
+ isLoadingFilterData = signal(false, ...(ngDevMode ? [{ debugName: "isLoadingFilterData" }] : /* istanbul ignore next */ []));
1081
+ isQueryingPointData = signal(false, ...(ngDevMode ? [{ debugName: "isQueryingPointData" }] : /* istanbul ignore next */ []));
1082
+ loadError = signal(false, ...(ngDevMode ? [{ debugName: "loadError" }] : /* istanbul ignore next */ []));
1083
+ selectedPropertyData = signal(null, ...(ngDevMode ? [{ debugName: "selectedPropertyData" }] : /* istanbul ignore next */ []));
1084
+ // Options for Dropdowns
1085
+ regionOptions = computed(() => this.regions().map(r => ({
1086
+ label: r.regionName,
1087
+ value: r.regionCode,
1088
+ })), ...(ngDevMode ? [{ debugName: "regionOptions" }] : /* istanbul ignore next */ []));
1089
+ cityOptions = computed(() => this.cities().map(c => ({
1090
+ label: c.name,
1091
+ value: c.code,
1092
+ })), ...(ngDevMode ? [{ debugName: "cityOptions" }] : /* istanbul ignore next */ []));
1093
+ districtOptions = computed(() => this.districts().map(d => ({
1094
+ label: d.districtName,
1095
+ value: d.districtCode,
1096
+ })), ...(ngDevMode ? [{ debugName: "districtOptions" }] : /* istanbul ignore next */ []));
1097
+ ngOnInit() {
1098
+ this.loadRegions();
1099
+ }
1100
+ ngOnDestroy() {
1101
+ this.regionsSub?.unsubscribe();
1102
+ this.citiesSub?.unsubscribe();
1103
+ this.districtsSub?.unsubscribe();
1104
+ this.pointDataSub?.unsubscribe();
1105
+ }
1106
+ loadRegions() {
1107
+ this.regionsSub?.unsubscribe();
1108
+ this.isLoadingFilterData.set(true);
1109
+ this.loadError.set(false);
1110
+ this.regionsSub = this.gisService.getRegions().subscribe({
1111
+ next: data => {
1112
+ this.regions.set(data);
1113
+ this.isLoadingFilterData.set(false);
1114
+ },
1115
+ error: () => {
1116
+ this.isLoadingFilterData.set(false);
1117
+ this.loadError.set(true);
1118
+ },
1119
+ });
1120
+ }
1121
+ /** Filter -> Map Sync: User selects Region */
1122
+ onRegionChange(code) {
1123
+ // Cancel in-flight cities and districts requests
1124
+ this.citiesSub?.unsubscribe();
1125
+ this.districtsSub?.unsubscribe();
1126
+ const validCode = code ?? '';
1127
+ this.selectedRegionCode.set(validCode);
1128
+ this.selectedCityCode.set('');
1129
+ this.selectedDistrictCode.set('');
1130
+ this.cities.set([]);
1131
+ this.districts.set([]);
1132
+ if (!validCode) {
1133
+ this.isLoadingFilterData.set(false);
1134
+ return;
1135
+ }
1136
+ // Move map to region center
1137
+ const regionCenter = SAUDI_REGION_CENTERS[validCode];
1138
+ if (regionCenter) {
1139
+ this.currentLat.set(regionCenter.lat);
1140
+ this.currentLng.set(regionCenter.lng);
1141
+ this.zoomLevel.set(regionCenter.zoom);
1142
+ }
1143
+ this.isLoadingFilterData.set(true);
1144
+ this.citiesSub = this.gisService.getCities(validCode).subscribe({
1145
+ next: data => {
1146
+ this.cities.set(data);
1147
+ this.isLoadingFilterData.set(false);
1148
+ },
1149
+ error: () => {
1150
+ this.isLoadingFilterData.set(false);
1151
+ },
1152
+ });
1153
+ }
1154
+ /** Filter -> Map Sync: User selects City */
1155
+ onCityChange(code) {
1156
+ // Cancel in-flight districts request
1157
+ this.districtsSub?.unsubscribe();
1158
+ const validCode = code ?? '';
1159
+ this.selectedCityCode.set(validCode);
1160
+ this.selectedDistrictCode.set('');
1161
+ this.districts.set([]);
1162
+ if (!validCode) {
1163
+ this.isLoadingFilterData.set(false);
1164
+ return;
1165
+ }
1166
+ // Move map to city center if rings exist
1167
+ const selectedCity = this.cities().find(c => c.code === validCode);
1168
+ if (selectedCity?.rings) {
1169
+ const center = calculateRingsCenter(selectedCity.rings);
1170
+ if (center) {
1171
+ this.currentLat.set(center.lat);
1172
+ this.currentLng.set(center.lng);
1173
+ this.zoomLevel.set(12);
1174
+ }
1175
+ }
1176
+ const regionCode = this.selectedRegionCode();
1177
+ this.isLoadingFilterData.set(true);
1178
+ this.districtsSub = this.gisService.getDistricts(regionCode, validCode).subscribe({
1179
+ next: data => {
1180
+ this.districts.set(data);
1181
+ this.isLoadingFilterData.set(false);
1182
+ },
1183
+ error: () => {
1184
+ this.isLoadingFilterData.set(false);
1185
+ },
1186
+ });
1187
+ }
1188
+ /** Filter -> Map Sync: User selects District */
1189
+ onDistrictChange(code) {
1190
+ const validCode = code ?? '';
1191
+ this.selectedDistrictCode.set(validCode);
1192
+ if (!validCode) {
1193
+ return;
1194
+ }
1195
+ // Move map to district center if rings exist
1196
+ const selectedDistrict = this.districts().find(d => d.districtCode === validCode);
1197
+ if (selectedDistrict?.rings) {
1198
+ const center = calculateRingsCenter(selectedDistrict.rings);
1199
+ if (center) {
1200
+ this.currentLat.set(center.lat);
1201
+ this.currentLng.set(center.lng);
1202
+ this.zoomLevel.set(14);
1203
+ }
1204
+ }
1205
+ else {
1206
+ this.zoomLevel.set(14);
1207
+ }
1208
+ }
1209
+ /** Query Balady GIS API for point data (x=lng, y=lat) */
1210
+ queryPointData(lng, lat, options = {}) {
1211
+ const emit = options.emit ?? true;
1212
+ if (this.lastQueriedCoords && this.lastQueriedCoords.lat === lat && this.lastQueriedCoords.lng === lng) {
1213
+ return;
1214
+ }
1215
+ if (Date.now() < this.pointQueryBlockedUntil) {
1216
+ if (emit) {
1217
+ this.toast.warning('', RATE_LIMITED_MESSAGE);
1218
+ }
1219
+ return;
1220
+ }
1221
+ this.lastQueriedCoords = { lat, lng };
1222
+ // Cancel any in-flight requests before querying new point
1223
+ this.pointDataSub?.unsubscribe();
1224
+ this.citiesSub?.unsubscribe();
1225
+ this.districtsSub?.unsubscribe();
1226
+ this.currentLat.set(lat);
1227
+ this.currentLng.set(lng);
1228
+ this.zoomLevel.set(18);
1229
+ this.isQueryingPointData.set(true);
1230
+ this.pointDataSub = this.gisService.getPointData(lng, lat).subscribe({
1231
+ next: result => {
1232
+ this.isQueryingPointData.set(false);
1233
+ const hasPointDetails = Boolean(result?.parcelNo || result?.planNO || result?.region || result?.city);
1234
+ if (result && hasPointDetails) {
1235
+ // The saved coordinates are the clicked point — never a vertex of the parcel rings.
1236
+ result.x = lng;
1237
+ result.y = lat;
1238
+ this.zoomLevel.set(18);
1239
+ this.selectedPropertyData.set(result);
1240
+ if (emit) {
1241
+ this.propertySelected.emit(result);
1242
+ }
1243
+ // Update Map Marker
1244
+ const popupHtml = `
1245
+ <div style="font-family: inherit; font-size: 13px; line-height: 1.5; padding: 4px;">
1246
+ <strong>رقم القطعة:</strong> ${result.parcelNo ?? '-'}<br/>
1247
+ <strong>رقم المخطط:</strong> ${result.planNO ?? '-'}<br/>
1248
+ <strong>الاستخدام:</strong> ${result.landuse ?? '-'}<br/>
1249
+ <strong>المساحة:</strong> ${this.formatArea(result.area)}
1250
+ </div>
1251
+ `;
1252
+ this.currentMarkers.set([
1253
+ {
1254
+ draggable: false,
1255
+ lat,
1256
+ lng,
1257
+ popupHtml,
1258
+ },
1259
+ ]);
1260
+ }
1261
+ else {
1262
+ if (emit) {
1263
+ this.toast.info('', PARCEL_NOT_FOUND_MESSAGE);
1264
+ }
1265
+ }
1266
+ },
1267
+ error: (err) => {
1268
+ this.isQueryingPointData.set(false);
1269
+ // Let the user retry the same point once the failure is resolved.
1270
+ this.lastQueriedCoords = null;
1271
+ const kind = err instanceof BaladyGisError ? err.kind : 'unknown';
1272
+ if (kind === 'rateLimited') {
1273
+ const retryAfter = err.retryAfterSeconds ?? DEFAULT_RATE_LIMIT_BACKOFF_SECONDS;
1274
+ this.pointQueryBlockedUntil = Date.now() + retryAfter * 1000;
1275
+ }
1276
+ if (emit) {
1277
+ this.showPointQueryError(kind);
1278
+ }
1279
+ },
1280
+ });
1281
+ }
1282
+ showPointQueryError(kind) {
1283
+ switch (kind) {
1284
+ case 'notFound':
1285
+ case 'invalid':
1286
+ this.toast.info('', PARCEL_NOT_FOUND_MESSAGE);
1287
+ return;
1288
+ case 'rateLimited':
1289
+ this.toast.warning('', RATE_LIMITED_MESSAGE);
1290
+ return;
1291
+ case 'unavailable':
1292
+ this.toast.error('', 'خدمة بلدي غير متاحة حاليًا. يرجى المحاولة لاحقًا.');
1293
+ return;
1294
+ default:
1295
+ this.toast.error('', 'تعذر الاستعلام عن بيانات العقار. يرجى المحاولة مرة أخرى.');
1296
+ }
1297
+ }
1298
+ /** Map Click -> Query Balady & Sync Filter Dropdowns */
1299
+ onMapClicked(coords) {
1300
+ // Leaflet reports longitudes past ±180 once the map is panned across the antimeridian.
1301
+ const lng = ((((coords.lng + 180) % 360) + 360) % 360) - 180;
1302
+ this.mapClick.emit({ lat: coords.lat, lng });
1303
+ this.queryPointData(lng, coords.lat, { emit: true });
1304
+ }
1305
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SceBaladyMapSelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1306
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SceBaladyMapSelectorComponent, isStandalone: true, selector: "sce-balady-map-selector", inputs: { locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: false, transformFunction: null }, dir: { classPropertyName: "dir", publicName: "dir", isSignal: true, isRequired: false, transformFunction: null }, initialPropertyData: { classPropertyName: "initialPropertyData", publicName: "initialPropertyData", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { propertySelected: "propertySelected", mapClick: "mapClick" }, host: { classAttribute: "sce-balady-map-selector" }, hostDirectives: [{ directive: i1.SceVariantDirective, inputs: ["sceVariant", "sceVariant", "sceDensity", "sceDensity"] }], ngImport: i0, template: "<div class=\"baladyMapSelector\">\n <div class=\"baladyMapSelector__filterHeader\">\n <div class=\"baladyMapSelector__filterTitle\">\n <h4>{{ '\u062A\u062D\u062F\u064A\u062F \u0627\u0644\u0639\u0642\u0627\u0631 \u0645\u0646 \u0627\u0644\u062E\u0631\u064A\u0637\u0629 \u0627\u0644\u062A\u0641\u0627\u0639\u0644\u064A\u0629' }}</h4>\n </div>\n <p class=\"baladyMapSelector__filterSubtitle\">\n \u0627\u062E\u062A\u0631 \u0627\u0644\u0645\u0646\u0637\u0642\u0629 \u0648\u0627\u0644\u0645\u062F\u064A\u0646\u0629 \u0648\u0627\u0644\u062D\u064A \u0644\u062A\u062D\u062F\u064A\u062F \u0627\u0644\u0646\u0637\u0627\u0642\u060C \u062B\u0645 \u0627\u0636\u063A\u0637 \u0639\u0644\u0649 \u0645\u0648\u0642\u0639 \u0627\u0644\u0623\u0631\u0636 \u0645\u0628\u0627\u0634\u0631\u0629 \u0641\u064A \u0627\u0644\u062E\u0631\u064A\u0637\u0629 \u0644\u0627\u0633\u062A\u062E\u0631\u0627\u062C \u062A\u0641\u0627\u0635\u064A\u0644 \u0627\u0644\u0645\u062E\u0637\u0637 \u0648\u0627\u0644\u0642\u0637\u0639\u0629 \u0648\u0627\u0644\u0645\u0633\u0627\u062D\u0629 \u0648\u0627\u0644\u062D\u062F\u0648\u062F.\n </p>\n\n @if (loadError()) {\n <sce-inline-api-error [message]=\"'\u062A\u0639\u0630\u0631 \u062A\u062D\u0645\u064A\u0644 \u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0645\u0646\u0627\u0637\u0642'\" [retryable]=\"true\" (retry)=\"loadRegions()\" />\n }\n </div>\n\n <div class=\"baladyMapSelector__mapContainer\">\n <!-- Floating Filters Bar over Map -->\n <div class=\"baladyMapSelector__floatingFilters\">\n @if (isLoadingFilterData()) {\n <div class=\"baladyMapSelector__filterLoader\" role=\"status\" aria-label=\"\u062C\u0627\u0631\u064A \u062A\u062D\u0645\u064A\u0644 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A\">\n <div class=\"baladyMapSelector__miniSpinner\"></div>\n <span>\u062C\u0627\u0631\u064A \u062C\u0644\u0628 \u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u062A\u0635\u0641\u064A\u0629...</span>\n </div>\n }\n\n <div class=\"baladyMapSelector__grid\">\n <sce-select-field\n id=\"balady-filter-region\"\n [label]=\"'\u0627\u0644\u0645\u0646\u0637\u0642\u0629'\"\n [options]=\"regionOptions()\"\n [value]=\"selectedRegionCode()\"\n [disabled]=\"isLoadingFilterData()\"\n [dir]=\"dir()\"\n [showClear]=\"true\"\n (valueChange)=\"onRegionChange($event)\"\n />\n <sce-select-field\n id=\"balady-filter-city\"\n [label]=\"'\u0627\u0644\u0645\u062F\u064A\u0646\u0629'\"\n [options]=\"cityOptions()\"\n [value]=\"selectedCityCode()\"\n [disabled]=\"isLoadingFilterData() || !selectedRegionCode()\"\n [dir]=\"dir()\"\n [showClear]=\"true\"\n (valueChange)=\"onCityChange($event)\"\n />\n <sce-select-field\n id=\"balady-filter-district\"\n [label]=\"'\u0627\u0644\u062D\u064A'\"\n [options]=\"districtOptions()\"\n [value]=\"selectedDistrictCode()\"\n [disabled]=\"isLoadingFilterData() || !selectedCityCode()\"\n [dir]=\"dir()\"\n [showClear]=\"true\"\n (valueChange)=\"onDistrictChange($event)\"\n />\n </div>\n </div>\n\n @if (isQueryingPointData()) {\n <div class=\"baladyMapSelector__overlay\" role=\"status\" aria-label=\"\u062C\u0627\u0631\u064A \u0627\u0644\u0627\u0633\u062A\u0639\u0644\u0627\u0645 \u0639\u0646 \u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0623\u0631\u0636\">\n <div class=\"baladyMapSelector__spinner\"></div>\n <span>\u062C\u0627\u0631\u064A \u0627\u0644\u0627\u0633\u062A\u0639\u0644\u0627\u0645 \u0639\u0646 \u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0639\u0642\u0627\u0631 \u0648\u0627\u0644\u0642\u0637\u0639\u0629...</span>\n </div>\n }\n\n <sce-osm-leaflet-map\n [latitude]=\"currentLat()\"\n [longitude]=\"currentLng()\"\n [zoom]=\"zoomLevel()\"\n [markers]=\"currentMarkers()\"\n [interactive]=\"true\"\n (mapClick)=\"onMapClicked($event)\"\n />\n </div>\n\n @if (selectedPropertyData(); as prop) {\n <div class=\"baladyMapSelector__summaryCard\" role=\"region\" aria-label=\"\u062A\u0641\u0627\u0635\u064A\u0644 \u0627\u0644\u0639\u0642\u0627\u0631 \u0627\u0644\u0645Selected\">\n <div class=\"baladyMapSelector__summaryHeader\">\n <span class=\"baladyMapSelector__statusIcon\">\u2713</span>\n <strong>\u062A\u0645 \u062A\u062D\u062F\u064A\u062F \u0627\u0644\u0639\u0642\u0627\u0631 \u0628\u0646\u062C\u0627\u062D</strong>\n </div>\n <div class=\"baladyMapSelector__summaryGrid\">\n <div class=\"baladyMapSelector__infoItem\">\n <span class=\"label\">\u0627\u0644\u0645\u0646\u0637\u0642\u0629 \u0648\u0627\u0644\u0645\u062F\u064A\u0646\u0629:</span>\n <span class=\"val\">{{ prop.region }} - {{ prop.city }} ({{ prop.districtName }})</span>\n </div>\n <div class=\"baladyMapSelector__infoItem\">\n <span class=\"label\">\u0631\u0642\u0645 \u0627\u0644\u0645\u062E\u0637\u0637:</span>\n <span class=\"val\">{{ prop.planNO ?? '-' }}</span>\n </div>\n <div class=\"baladyMapSelector__infoItem\">\n <span class=\"label\">\u0631\u0642\u0645 \u0627\u0644\u0642\u0637\u0639\u0629:</span>\n <span class=\"val\">{{ prop.parcelNo ?? '-' }}</span>\n </div>\n <div class=\"baladyMapSelector__infoItem\">\n <span class=\"label\">\u0627\u0633\u062A\u062E\u062F\u0627\u0645 \u0627\u0644\u0623\u0631\u0636:</span>\n <span class=\"val\">{{ prop.landuse ?? '-' }}</span>\n </div>\n <div class=\"baladyMapSelector__infoItem\">\n <span class=\"label\">\u0627\u0644\u0645\u0633\u0627\u062D\u0629:</span>\n <span class=\"val\">{{ formatArea(prop.area) }}</span>\n </div>\n @if (prop.directionParcel && prop.directionParcel.length) {\n <!-- Track by index: a parcel can face the same direction twice, and duplicate keys throw NG0955. -->\n @for (dir of prop.directionParcel; track $index) {\n <div class=\"baladyMapSelector__infoItem\">\n <span class=\"label\">\u062D\u062F\u0648\u062F \u0627\u0644\u0639\u0642\u0627\u0631 ({{ dir.direction }}):</span>\n <span class=\"val\">{{ dir.length }} \u0645</span>\n </div>\n }\n }\n </div>\n </div>\n }\n</div>\n", styles: [":host.sce-balady-map-selector{display:block;width:100%}.baladyMapSelector{display:flex;flex-direction:column;gap:1rem;background-color:var(--color-neutral-50);border:1px solid var(--color-neutral-200);border-radius:var(--border-radius-lg, 12px);padding:1.25rem}.baladyMapSelector__filterHeader{display:flex;flex-direction:column;gap:.75rem}.baladyMapSelector__filterTitle{display:flex;align-items:center;gap:.5rem}.baladyMapSelector__filterTitle h4{margin:0;font-size:1.05rem;font-weight:700;color:var(--color-neutral-900)}.baladyMapSelector__badge{display:inline-flex;align-items:center;padding:.2rem .6rem;font-size:.75rem;font-weight:700;border-radius:9999px;background-color:#10b9811a;color:#059669}.baladyMapSelector__filterSubtitle{margin:0;font-size:.875rem;color:var(--color-neutral-600);line-height:1.5}.baladyMapSelector__filterLoader{display:flex;align-items:center;gap:.6rem;padding:.5rem .875rem;border-radius:var(--border-radius-md, 8px);background-color:#10b98114;border:1px solid rgba(16,185,129,.2);color:#047857;font-size:.8125rem;font-weight:600;animation:baladyFadeIn .25s ease-in-out}.baladyMapSelector__miniSpinner{width:16px;height:16px;border:2px solid rgba(16,185,129,.25);border-block-start-color:#059669;border-radius:50%;animation:baladySpin .7s linear infinite}.baladyMapSelector__grid{display:grid;grid-template-columns:repeat(1,1fr);gap:.75rem}@media(min-width:768px){.baladyMapSelector__grid{grid-template-columns:repeat(3,1fr)}}.baladyMapSelector__floatingFilters{position:absolute;top:.75rem;inset-inline-start:.75rem;inset-inline-end:.75rem;z-index:500;display:flex;flex-direction:column;gap:.5rem;background-color:#fffffff0;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);border:1px solid var(--color-neutral-200);border-radius:var(--border-radius-md, 8px);padding:.75rem;box-shadow:0 4px 16px #00000014}.dark-theme .baladyMapSelector__floatingFilters{background-color:#0f172af0;border-color:var(--color-neutral-700)}.baladyMapSelector__mapContainer{position:relative;width:100%;height:480px;border-radius:var(--border-radius-md, 8px);overflow:hidden;border:1px solid var(--color-neutral-200)}.baladyMapSelector__mapContainer sce-osm-leaflet-map{width:100%;height:100%;display:block}.baladyMapSelector__overlay{position:absolute;inset:0;z-index:1000;background-color:#ffffffd9;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.75rem;color:var(--color-neutral-900);font-size:.9rem;font-weight:600}.dark-theme .baladyMapSelector__overlay{background-color:#0f172ad9;color:var(--color-neutral-100)}.baladyMapSelector__spinner{width:32px;height:32px;border:3px solid rgba(16,185,129,.2);border-block-start-color:#059669;border-radius:50%;animation:baladySpin .8s linear infinite}.baladyMapSelector__summaryCard{background-color:#10b9810d;border:1px solid rgba(16,185,129,.2);border-radius:var(--border-radius-md, 8px);padding:.875rem 1rem;display:flex;flex-direction:column;gap:.5rem}.baladyMapSelector__summaryHeader{display:flex;align-items:center;gap:.5rem;color:#047857;font-size:.95rem}.baladyMapSelector__summaryHeader strong{font-weight:700}.baladyMapSelector__statusIcon{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;border-radius:50%;background-color:#059669;color:#fff;font-size:.75rem;font-weight:700}.baladyMapSelector__summaryGrid{display:grid;grid-template-columns:repeat(1,1fr);gap:.5rem}@media(min-width:640px){.baladyMapSelector__summaryGrid{grid-template-columns:repeat(2,1fr)}}@media(min-width:1024px){.baladyMapSelector__summaryGrid{grid-template-columns:repeat(3,1fr)}}.baladyMapSelector__infoItem{display:flex;flex-direction:column}.baladyMapSelector__infoItem .label{font-size:.75rem;color:var(--color-neutral-600)}.baladyMapSelector__infoItem .val{font-size:.875rem;font-weight:600;color:var(--color-neutral-900)}@keyframes baladySpin{to{transform:rotate(360deg)}}@keyframes baladyFadeIn{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: SceSelectFieldComponent, selector: "sce-select-field", inputs: ["variant", "id", "label", "options", "group", "optionGroupLabel", "optionGroupChildren", "value", "placeholder", "required", "disabled", "helpText", "error", "filter", "filterMaxLength", "showClear", "dir", "validationMode", "validate", "validationMessages", "fieldClassName", "labelClassName", "inputClassName", "panelClassName", "borderless", "filterPlaceholder", "showError", "containerClassName", "resetToken"], outputs: ["valueChange"] }, { kind: "component", type: SceInlineApiErrorComponent, selector: "sce-inline-api-error", inputs: ["message", "className", "retryable"], outputs: ["retry"] }, { kind: "component", type: SceOsmLeafletMapComponent, selector: "sce-osm-leaflet-map", inputs: ["latitude", "longitude", "zoom", "markers", "interactive"], outputs: ["mapClick", "markerDragEnd"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1307
+ }
1308
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SceBaladyMapSelectorComponent, decorators: [{
1309
+ type: Component,
1310
+ args: [{ hostDirectives: [{ directive: SceVariantDirective, inputs: ['sceVariant', 'sceDensity'] }], host: { class: 'sce-balady-map-selector' }, selector: 'sce-balady-map-selector', standalone: true, imports: [CommonModule, SceSelectFieldComponent, SceInlineApiErrorComponent, SceOsmLeafletMapComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"baladyMapSelector\">\n <div class=\"baladyMapSelector__filterHeader\">\n <div class=\"baladyMapSelector__filterTitle\">\n <h4>{{ '\u062A\u062D\u062F\u064A\u062F \u0627\u0644\u0639\u0642\u0627\u0631 \u0645\u0646 \u0627\u0644\u062E\u0631\u064A\u0637\u0629 \u0627\u0644\u062A\u0641\u0627\u0639\u0644\u064A\u0629' }}</h4>\n </div>\n <p class=\"baladyMapSelector__filterSubtitle\">\n \u0627\u062E\u062A\u0631 \u0627\u0644\u0645\u0646\u0637\u0642\u0629 \u0648\u0627\u0644\u0645\u062F\u064A\u0646\u0629 \u0648\u0627\u0644\u062D\u064A \u0644\u062A\u062D\u062F\u064A\u062F \u0627\u0644\u0646\u0637\u0627\u0642\u060C \u062B\u0645 \u0627\u0636\u063A\u0637 \u0639\u0644\u0649 \u0645\u0648\u0642\u0639 \u0627\u0644\u0623\u0631\u0636 \u0645\u0628\u0627\u0634\u0631\u0629 \u0641\u064A \u0627\u0644\u062E\u0631\u064A\u0637\u0629 \u0644\u0627\u0633\u062A\u062E\u0631\u0627\u062C \u062A\u0641\u0627\u0635\u064A\u0644 \u0627\u0644\u0645\u062E\u0637\u0637 \u0648\u0627\u0644\u0642\u0637\u0639\u0629 \u0648\u0627\u0644\u0645\u0633\u0627\u062D\u0629 \u0648\u0627\u0644\u062D\u062F\u0648\u062F.\n </p>\n\n @if (loadError()) {\n <sce-inline-api-error [message]=\"'\u062A\u0639\u0630\u0631 \u062A\u062D\u0645\u064A\u0644 \u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0645\u0646\u0627\u0637\u0642'\" [retryable]=\"true\" (retry)=\"loadRegions()\" />\n }\n </div>\n\n <div class=\"baladyMapSelector__mapContainer\">\n <!-- Floating Filters Bar over Map -->\n <div class=\"baladyMapSelector__floatingFilters\">\n @if (isLoadingFilterData()) {\n <div class=\"baladyMapSelector__filterLoader\" role=\"status\" aria-label=\"\u062C\u0627\u0631\u064A \u062A\u062D\u0645\u064A\u0644 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A\">\n <div class=\"baladyMapSelector__miniSpinner\"></div>\n <span>\u062C\u0627\u0631\u064A \u062C\u0644\u0628 \u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u062A\u0635\u0641\u064A\u0629...</span>\n </div>\n }\n\n <div class=\"baladyMapSelector__grid\">\n <sce-select-field\n id=\"balady-filter-region\"\n [label]=\"'\u0627\u0644\u0645\u0646\u0637\u0642\u0629'\"\n [options]=\"regionOptions()\"\n [value]=\"selectedRegionCode()\"\n [disabled]=\"isLoadingFilterData()\"\n [dir]=\"dir()\"\n [showClear]=\"true\"\n (valueChange)=\"onRegionChange($event)\"\n />\n <sce-select-field\n id=\"balady-filter-city\"\n [label]=\"'\u0627\u0644\u0645\u062F\u064A\u0646\u0629'\"\n [options]=\"cityOptions()\"\n [value]=\"selectedCityCode()\"\n [disabled]=\"isLoadingFilterData() || !selectedRegionCode()\"\n [dir]=\"dir()\"\n [showClear]=\"true\"\n (valueChange)=\"onCityChange($event)\"\n />\n <sce-select-field\n id=\"balady-filter-district\"\n [label]=\"'\u0627\u0644\u062D\u064A'\"\n [options]=\"districtOptions()\"\n [value]=\"selectedDistrictCode()\"\n [disabled]=\"isLoadingFilterData() || !selectedCityCode()\"\n [dir]=\"dir()\"\n [showClear]=\"true\"\n (valueChange)=\"onDistrictChange($event)\"\n />\n </div>\n </div>\n\n @if (isQueryingPointData()) {\n <div class=\"baladyMapSelector__overlay\" role=\"status\" aria-label=\"\u062C\u0627\u0631\u064A \u0627\u0644\u0627\u0633\u062A\u0639\u0644\u0627\u0645 \u0639\u0646 \u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0623\u0631\u0636\">\n <div class=\"baladyMapSelector__spinner\"></div>\n <span>\u062C\u0627\u0631\u064A \u0627\u0644\u0627\u0633\u062A\u0639\u0644\u0627\u0645 \u0639\u0646 \u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0639\u0642\u0627\u0631 \u0648\u0627\u0644\u0642\u0637\u0639\u0629...</span>\n </div>\n }\n\n <sce-osm-leaflet-map\n [latitude]=\"currentLat()\"\n [longitude]=\"currentLng()\"\n [zoom]=\"zoomLevel()\"\n [markers]=\"currentMarkers()\"\n [interactive]=\"true\"\n (mapClick)=\"onMapClicked($event)\"\n />\n </div>\n\n @if (selectedPropertyData(); as prop) {\n <div class=\"baladyMapSelector__summaryCard\" role=\"region\" aria-label=\"\u062A\u0641\u0627\u0635\u064A\u0644 \u0627\u0644\u0639\u0642\u0627\u0631 \u0627\u0644\u0645Selected\">\n <div class=\"baladyMapSelector__summaryHeader\">\n <span class=\"baladyMapSelector__statusIcon\">\u2713</span>\n <strong>\u062A\u0645 \u062A\u062D\u062F\u064A\u062F \u0627\u0644\u0639\u0642\u0627\u0631 \u0628\u0646\u062C\u0627\u062D</strong>\n </div>\n <div class=\"baladyMapSelector__summaryGrid\">\n <div class=\"baladyMapSelector__infoItem\">\n <span class=\"label\">\u0627\u0644\u0645\u0646\u0637\u0642\u0629 \u0648\u0627\u0644\u0645\u062F\u064A\u0646\u0629:</span>\n <span class=\"val\">{{ prop.region }} - {{ prop.city }} ({{ prop.districtName }})</span>\n </div>\n <div class=\"baladyMapSelector__infoItem\">\n <span class=\"label\">\u0631\u0642\u0645 \u0627\u0644\u0645\u062E\u0637\u0637:</span>\n <span class=\"val\">{{ prop.planNO ?? '-' }}</span>\n </div>\n <div class=\"baladyMapSelector__infoItem\">\n <span class=\"label\">\u0631\u0642\u0645 \u0627\u0644\u0642\u0637\u0639\u0629:</span>\n <span class=\"val\">{{ prop.parcelNo ?? '-' }}</span>\n </div>\n <div class=\"baladyMapSelector__infoItem\">\n <span class=\"label\">\u0627\u0633\u062A\u062E\u062F\u0627\u0645 \u0627\u0644\u0623\u0631\u0636:</span>\n <span class=\"val\">{{ prop.landuse ?? '-' }}</span>\n </div>\n <div class=\"baladyMapSelector__infoItem\">\n <span class=\"label\">\u0627\u0644\u0645\u0633\u0627\u062D\u0629:</span>\n <span class=\"val\">{{ formatArea(prop.area) }}</span>\n </div>\n @if (prop.directionParcel && prop.directionParcel.length) {\n <!-- Track by index: a parcel can face the same direction twice, and duplicate keys throw NG0955. -->\n @for (dir of prop.directionParcel; track $index) {\n <div class=\"baladyMapSelector__infoItem\">\n <span class=\"label\">\u062D\u062F\u0648\u062F \u0627\u0644\u0639\u0642\u0627\u0631 ({{ dir.direction }}):</span>\n <span class=\"val\">{{ dir.length }} \u0645</span>\n </div>\n }\n }\n </div>\n </div>\n }\n</div>\n", styles: [":host.sce-balady-map-selector{display:block;width:100%}.baladyMapSelector{display:flex;flex-direction:column;gap:1rem;background-color:var(--color-neutral-50);border:1px solid var(--color-neutral-200);border-radius:var(--border-radius-lg, 12px);padding:1.25rem}.baladyMapSelector__filterHeader{display:flex;flex-direction:column;gap:.75rem}.baladyMapSelector__filterTitle{display:flex;align-items:center;gap:.5rem}.baladyMapSelector__filterTitle h4{margin:0;font-size:1.05rem;font-weight:700;color:var(--color-neutral-900)}.baladyMapSelector__badge{display:inline-flex;align-items:center;padding:.2rem .6rem;font-size:.75rem;font-weight:700;border-radius:9999px;background-color:#10b9811a;color:#059669}.baladyMapSelector__filterSubtitle{margin:0;font-size:.875rem;color:var(--color-neutral-600);line-height:1.5}.baladyMapSelector__filterLoader{display:flex;align-items:center;gap:.6rem;padding:.5rem .875rem;border-radius:var(--border-radius-md, 8px);background-color:#10b98114;border:1px solid rgba(16,185,129,.2);color:#047857;font-size:.8125rem;font-weight:600;animation:baladyFadeIn .25s ease-in-out}.baladyMapSelector__miniSpinner{width:16px;height:16px;border:2px solid rgba(16,185,129,.25);border-block-start-color:#059669;border-radius:50%;animation:baladySpin .7s linear infinite}.baladyMapSelector__grid{display:grid;grid-template-columns:repeat(1,1fr);gap:.75rem}@media(min-width:768px){.baladyMapSelector__grid{grid-template-columns:repeat(3,1fr)}}.baladyMapSelector__floatingFilters{position:absolute;top:.75rem;inset-inline-start:.75rem;inset-inline-end:.75rem;z-index:500;display:flex;flex-direction:column;gap:.5rem;background-color:#fffffff0;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);border:1px solid var(--color-neutral-200);border-radius:var(--border-radius-md, 8px);padding:.75rem;box-shadow:0 4px 16px #00000014}.dark-theme .baladyMapSelector__floatingFilters{background-color:#0f172af0;border-color:var(--color-neutral-700)}.baladyMapSelector__mapContainer{position:relative;width:100%;height:480px;border-radius:var(--border-radius-md, 8px);overflow:hidden;border:1px solid var(--color-neutral-200)}.baladyMapSelector__mapContainer sce-osm-leaflet-map{width:100%;height:100%;display:block}.baladyMapSelector__overlay{position:absolute;inset:0;z-index:1000;background-color:#ffffffd9;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.75rem;color:var(--color-neutral-900);font-size:.9rem;font-weight:600}.dark-theme .baladyMapSelector__overlay{background-color:#0f172ad9;color:var(--color-neutral-100)}.baladyMapSelector__spinner{width:32px;height:32px;border:3px solid rgba(16,185,129,.2);border-block-start-color:#059669;border-radius:50%;animation:baladySpin .8s linear infinite}.baladyMapSelector__summaryCard{background-color:#10b9810d;border:1px solid rgba(16,185,129,.2);border-radius:var(--border-radius-md, 8px);padding:.875rem 1rem;display:flex;flex-direction:column;gap:.5rem}.baladyMapSelector__summaryHeader{display:flex;align-items:center;gap:.5rem;color:#047857;font-size:.95rem}.baladyMapSelector__summaryHeader strong{font-weight:700}.baladyMapSelector__statusIcon{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;border-radius:50%;background-color:#059669;color:#fff;font-size:.75rem;font-weight:700}.baladyMapSelector__summaryGrid{display:grid;grid-template-columns:repeat(1,1fr);gap:.5rem}@media(min-width:640px){.baladyMapSelector__summaryGrid{grid-template-columns:repeat(2,1fr)}}@media(min-width:1024px){.baladyMapSelector__summaryGrid{grid-template-columns:repeat(3,1fr)}}.baladyMapSelector__infoItem{display:flex;flex-direction:column}.baladyMapSelector__infoItem .label{font-size:.75rem;color:var(--color-neutral-600)}.baladyMapSelector__infoItem .val{font-size:.875rem;font-weight:600;color:var(--color-neutral-900)}@keyframes baladySpin{to{transform:rotate(360deg)}}@keyframes baladyFadeIn{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}\n"] }]
1311
+ }], ctorParameters: () => [], propDecorators: { locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], dir: [{ type: i0.Input, args: [{ isSignal: true, alias: "dir", required: false }] }], initialPropertyData: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialPropertyData", required: false }] }], propertySelected: [{ type: i0.Output, args: ["propertySelected"] }], mapClick: [{ type: i0.Output, args: ["mapClick"] }] } });
1312
+
1313
+ /**
1314
+ * `@moatamed-sce/ui-components/maps` — OSM / Leaflet map organisms.
1315
+ *
1316
+ * Kept off the main ui-components barrel so portal shells that import chrome
1317
+ * (sidebar, layout, notifications) do not pull map/Leaflet code into the
1318
+ * initial shared chunk. Marketplace / contracts / miyar import from here.
1319
+ */
1320
+
1321
+ /**
1322
+ * Generated bundle index. Do not edit.
1323
+ */
1324
+
1325
+ export { SceBaladyMapSelectorComponent, SceOfficeLocationMapComponent, SceOsmLeafletMapComponent, SceOsmLocationPickerComponent, SceOsmOfficesMapComponent, SceStoreLocationsComponent };
1326
+ //# sourceMappingURL=moatamed-sce-ui-components-maps.mjs.map