@acorex-platform/extension-weather-core-client 0.0.21

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,803 @@
1
+ import { AXP_EXPERIENCE_SETTINGS_GROUP, AXP_EXPERIENCE_SETTINGS_SECTIONS, AXP_SETTING_DEFINITION_PROVIDER, AXP_INTEGRATION_DOMAIN_PROVIDER } from '@acorex-platform/framework-client/common';
2
+ import { AXP_MODULE_MANIFEST_PROVIDER } from '@acorex-platform/framework-client/core';
3
+ import { AXP_WIDGET_DEFINITION_PROVIDER } from '@acorex-platform/framework-client/layout/widget-core';
4
+ import * as i0 from '@angular/core';
5
+ import { Injectable, inject, DestroyRef, signal, computed, effect, ChangeDetectionStrategy, Component, NgModule } from '@angular/core';
6
+ import { AXP_INTEGRATION_CATEGORY, AXPWidgetsList, AXPWidgetsCatalog } from '@acorex-platform/framework-shared/core';
7
+ import { RootConfig, AXE_WEATHER_DOMAIN_ID, WEATHER_PROVIDER_IDS, AXEWeatherSettingKeys, getWeatherCondition } from '@acorex-platform/extension-weather-core-client/contracts';
8
+ import { AXButtonModule } from '@acorex/components/button';
9
+ import * as i1 from '@acorex/components/skeleton';
10
+ import { AXSkeletonModule } from '@acorex/components/skeleton';
11
+ import * as i3 from '@acorex/core/translation';
12
+ import { AXTranslationModule } from '@acorex/core/translation';
13
+ import { AXPStateMessageComponent } from '@acorex-platform/framework-client/layout/components';
14
+ import { AXPDashboardWidgetComponent, AXP_UTILITY_WEATHER_DIMENSIONS, AXP_DASHBOARD_SHARED_SUB_UTILITY_WEATHER, AXP_WIDGETS_UTILITY_CATEGORY } from '@acorex-platform/module-dashboard-management-client';
15
+ import * as i2 from '@angular/common';
16
+ import { CommonModule } from '@angular/common';
17
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
18
+ import { catchError, of, finalize, interval, switchMap } from 'rxjs';
19
+ import { AXPWeatherApiAbstract, AXEWeatherGatewayService } from '@acorex-platform/extension-weather-core-gateway';
20
+ import { AXPWidgetGroupEnum } from '@acorex-platform/framework-shared/widget-core';
21
+ import { AXP_DATA_PROPERTY_GROUP, AXP_APPEARANCE_PROPERTY_GROUP, AXP_BEHAVIOR_PROPERTY_GROUP } from '@acorex-platform/framework-client/layout/widgets';
22
+ import { defineWidget } from '@acorex-platform/framework-shared/runtime';
23
+
24
+ const I18N$1 = `@${RootConfig.config.i18n}:integration.domain`;
25
+ class AXEWeatherIntegrationDomainProvider {
26
+ async provide(context) {
27
+ context.register({
28
+ id: AXE_WEATHER_DOMAIN_ID,
29
+ module: RootConfig.module.name,
30
+ displayName: `${I18N$1}.title`,
31
+ description: `${I18N$1}.description`,
32
+ icon: RootConfig.module.icon,
33
+ category: AXP_INTEGRATION_CATEGORY.Weather,
34
+ capabilities: [],
35
+ settingsGroup: 'experience',
36
+ sortOrder: 10,
37
+ });
38
+ }
39
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEWeatherIntegrationDomainProvider, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
40
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEWeatherIntegrationDomainProvider }); }
41
+ }
42
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEWeatherIntegrationDomainProvider, decorators: [{
43
+ type: Injectable
44
+ }] });
45
+
46
+ const WeatherExtensionManifest = {
47
+ name: RootConfig.module.name,
48
+ version: '1.0.0',
49
+ title: RootConfig.module.title,
50
+ icon: RootConfig.module.icon,
51
+ i18n: RootConfig.config.i18n,
52
+ type: 'integration',
53
+ domain: 'weather',
54
+ dependencies: [],
55
+ };
56
+
57
+ const I18N = `@${RootConfig.config.i18n}:settings`;
58
+ class AXEWeatherSettingsProvider {
59
+ async provide(context) {
60
+ if (!context.hasGroup(AXP_EXPERIENCE_SETTINGS_GROUP)) {
61
+ return;
62
+ }
63
+ const experience = context.group(AXP_EXPERIENCE_SETTINGS_GROUP);
64
+ if (!experience) {
65
+ return;
66
+ }
67
+ const widgets = experience.section(AXP_EXPERIENCE_SETTINGS_SECTIONS.Widgets);
68
+ if (!widgets) {
69
+ return;
70
+ }
71
+ widgets.addSetting({
72
+ key: AXEWeatherSettingKeys.ActiveProvider,
73
+ title: `${I18N}.provider.active-provider.title`,
74
+ scope: 'T',
75
+ overridableAt: [],
76
+ isInherited: true,
77
+ defaultValue: WEATHER_PROVIDER_IDS.WeatherApi,
78
+ widget: {
79
+ type: AXPWidgetsList.Editors.SelectBox,
80
+ options: {
81
+ dataSource: [
82
+ { id: WEATHER_PROVIDER_IDS.WeatherApi, title: 'WeatherAPI.com' },
83
+ { id: WEATHER_PROVIDER_IDS.OpenWeatherMap, title: 'OpenWeatherMap' },
84
+ ],
85
+ },
86
+ },
87
+ description: `${I18N}.provider.active-provider.description`,
88
+ });
89
+ }
90
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEWeatherSettingsProvider, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
91
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEWeatherSettingsProvider }); }
92
+ }
93
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEWeatherSettingsProvider, decorators: [{
94
+ type: Injectable
95
+ }] });
96
+
97
+ const GRID_CELL_HEIGHT = 75;
98
+ const GRID_GAP = 5;
99
+ const CONDITION_HUES = {
100
+ sunny: 38,
101
+ clearNight: 250,
102
+ partlyCloudy: 210,
103
+ partlyCloudyNight: 250,
104
+ cloudy: 215,
105
+ rain: 220,
106
+ showers: 215,
107
+ thunderstorm: 270,
108
+ snow: 200,
109
+ mist: 215,
110
+ unknown: 210,
111
+ };
112
+ function normalizeHue(hue) {
113
+ return ((hue % 360) + 360) % 360;
114
+ }
115
+ /** Snap pixel size to the nearest dashboard grid unit count (1–4). */
116
+ function gridUnitsFromPixels(size, cellSize) {
117
+ let bestUnits = 1;
118
+ let bestDelta = Infinity;
119
+ for (let units = 1; units <= 4; units++) {
120
+ const expected = units * cellSize + (units - 1) * GRID_GAP;
121
+ const delta = Math.abs(size - expected);
122
+ if (delta < bestDelta) {
123
+ bestDelta = delta;
124
+ bestUnits = units;
125
+ }
126
+ }
127
+ return bestUnits;
128
+ }
129
+ function resolveWeatherGridUnits(width, height) {
130
+ return {
131
+ w: gridUnitsFromPixels(width, 112),
132
+ h: gridUnitsFromPixels(height, GRID_CELL_HEIGHT),
133
+ };
134
+ }
135
+ function resolveWeatherLayoutVariant(width, height) {
136
+ if (width <= 0 || height <= 0) {
137
+ return '2x2';
138
+ }
139
+ const { w, h } = resolveWeatherGridUnits(width, height);
140
+ // Snap disallowed sizes to nearest allowed layout.
141
+ if (w === 1 && h === 1) {
142
+ return '2x2';
143
+ }
144
+ if (w === 1 && h >= 4) {
145
+ return '1x2';
146
+ }
147
+ if (w === 1 && h === 3) {
148
+ return '1x2';
149
+ }
150
+ if (w >= 4 && h >= 4) {
151
+ return '4x4';
152
+ }
153
+ if (w >= 4 && h >= 2) {
154
+ return '4x2';
155
+ }
156
+ if (w >= 4 && h === 1) {
157
+ return '2x1';
158
+ }
159
+ if (w >= 2 && h >= 2) {
160
+ return '2x2';
161
+ }
162
+ if (w >= 2 && h === 1) {
163
+ return '2x1';
164
+ }
165
+ if (w === 1 && h >= 2) {
166
+ return '1x2';
167
+ }
168
+ return '2x2';
169
+ }
170
+ function getWeatherConditionHue(conditionId) {
171
+ const normalized = String(conditionId || 'unknown').replace(/\s+/g, '');
172
+ return CONDITION_HUES[normalized] ?? CONDITION_HUES[getWeatherCondition(normalized).id] ?? CONDITION_HUES['unknown'];
173
+ }
174
+ function buildWeatherVisualTheme(conditionId) {
175
+ const condition = getWeatherCondition(conditionId || 'partlyCloudy');
176
+ const hue = getWeatherConditionHue(condition.id);
177
+ const accent = condition.color || '#6ba4e8';
178
+ const h = normalizeHue(hue);
179
+ const isWarm = ['sunny', 'clearNight'].includes(condition.id);
180
+ const high = isWarm ? '#f59e0b' : '#3b82f6';
181
+ const low = '#3b82f6';
182
+ return {
183
+ background: `linear-gradient(135deg, hsl(${h}, 84%, 97%) 0%, hsl(${h}, 68%, 98%) 52%, #fff 100%)`,
184
+ blob: `radial-gradient(circle at 100% 100%, hsla(${h}, 85%, 72%, 0.45) 0%, hsla(${h}, 70%, 80%, 0.18) 42%, transparent 72%)`,
185
+ high,
186
+ low,
187
+ accent,
188
+ };
189
+ }
190
+ function shouldShowWeatherStats(_variant) {
191
+ return true;
192
+ }
193
+ function shouldShowWeatherFeelsLike(variant) {
194
+ return variant !== '1x2';
195
+ }
196
+ function shouldShowWeatherHourly(variant) {
197
+ return variant === '2x1' || variant === '4x2' || variant === '4x4';
198
+ }
199
+ function shouldShowWeatherMetrics(variant) {
200
+ return variant === '4x2' || variant === '4x4';
201
+ }
202
+ function shouldShowWeatherDaily(variant, showForecast) {
203
+ return showForecast && (variant === '4x2' || variant === '4x4');
204
+ }
205
+ function isWeatherWideDashboard(variant) {
206
+ return variant === '4x2' || variant === '4x4';
207
+ }
208
+ /** Cap hourly slots so wide tiles fill evenly without a horizontal scrollbar. */
209
+ function resolveWeatherHourlyCount(variant, configuredHours) {
210
+ const configured = Number.isFinite(configuredHours) ? Math.max(1, Math.floor(configuredHours)) : 6;
211
+ switch (variant) {
212
+ case '2x1':
213
+ return Math.min(configured, 4);
214
+ case '4x2':
215
+ return Math.min(configured, 4);
216
+ case '4x4':
217
+ return Math.min(configured, 6);
218
+ default:
219
+ return configured;
220
+ }
221
+ }
222
+
223
+ class AXPWeatherWidgetViewComponent extends AXPDashboardWidgetComponent {
224
+ constructor() {
225
+ super();
226
+ this.weatherService = inject(AXPWeatherApiAbstract);
227
+ this.destroyRef = inject(DestroyRef);
228
+ this.containerWidth = signal(0, /* @ts-ignore */
229
+ ...(ngDevMode ? [{ debugName: "containerWidth" }] : /* istanbul ignore next */ []));
230
+ this.containerHeight = signal(0, /* @ts-ignore */
231
+ ...(ngDevMode ? [{ debugName: "containerHeight" }] : /* istanbul ignore next */ []));
232
+ this.refreshSubscription = null;
233
+ this.iconCache = new Map();
234
+ this.weatherData = signal(null, /* @ts-ignore */
235
+ ...(ngDevMode ? [{ debugName: "weatherData" }] : /* istanbul ignore next */ []));
236
+ this.isLoading = signal(true, /* @ts-ignore */
237
+ ...(ngDevMode ? [{ debugName: "isLoading" }] : /* istanbul ignore next */ []));
238
+ this.hasError = signal(false, /* @ts-ignore */
239
+ ...(ngDevMode ? [{ debugName: "hasError" }] : /* istanbul ignore next */ []));
240
+ this.errorMessage = signal('', /* @ts-ignore */
241
+ ...(ngDevMode ? [{ debugName: "errorMessage" }] : /* istanbul ignore next */ []));
242
+ this.city = computed(() => this.options()['city'] || 'New York', /* @ts-ignore */
243
+ ...(ngDevMode ? [{ debugName: "city" }] : /* istanbul ignore next */ []));
244
+ this.temperatureUnit = computed(() => this.options()['temperatureUnit']?.id || '°C', /* @ts-ignore */
245
+ ...(ngDevMode ? [{ debugName: "temperatureUnit" }] : /* istanbul ignore next */ []));
246
+ this.windSpeedUnit = computed(() => this.options()['windSpeedUnit']?.id || 'km/h', /* @ts-ignore */
247
+ ...(ngDevMode ? [{ debugName: "windSpeedUnit" }] : /* istanbul ignore next */ []));
248
+ this.showHumidity = computed(() => this.options()['showHumidity'] !== false, /* @ts-ignore */
249
+ ...(ngDevMode ? [{ debugName: "showHumidity" }] : /* istanbul ignore next */ []));
250
+ this.showWind = computed(() => this.options()['showWind'] !== false, /* @ts-ignore */
251
+ ...(ngDevMode ? [{ debugName: "showWind" }] : /* istanbul ignore next */ []));
252
+ this.showForecast = computed(() => this.options()['showForecast'] !== false, /* @ts-ignore */
253
+ ...(ngDevMode ? [{ debugName: "showForecast" }] : /* istanbul ignore next */ []));
254
+ this.forecastDays = computed(() => this.options()['forecastDays'] ?? 5, /* @ts-ignore */
255
+ ...(ngDevMode ? [{ debugName: "forecastDays" }] : /* istanbul ignore next */ []));
256
+ this.forecastHours = computed(() => this.options()['forecastHours'] ?? 6, /* @ts-ignore */
257
+ ...(ngDevMode ? [{ debugName: "forecastHours" }] : /* istanbul ignore next */ []));
258
+ this.autoRefresh = computed(() => this.options()['autoRefresh'] !== false, /* @ts-ignore */
259
+ ...(ngDevMode ? [{ debugName: "autoRefresh" }] : /* istanbul ignore next */ []));
260
+ this.refreshInterval = computed(() => this.options()['refreshInterval']?.id ?? 15, /* @ts-ignore */
261
+ ...(ngDevMode ? [{ debugName: "refreshInterval" }] : /* istanbul ignore next */ []));
262
+ this.layoutVariant = computed(() => resolveWeatherLayoutVariant(this.containerWidth(), this.containerHeight()), /* @ts-ignore */
263
+ ...(ngDevMode ? [{ debugName: "layoutVariant" }] : /* istanbul ignore next */ []));
264
+ this.visualTheme = computed(() => buildWeatherVisualTheme(this.weatherData()?.current.condition || 'partlyCloudy'), /* @ts-ignore */
265
+ ...(ngDevMode ? [{ debugName: "visualTheme" }] : /* istanbul ignore next */ []));
266
+ this.showStats = computed(() => shouldShowWeatherStats(this.layoutVariant()), /* @ts-ignore */
267
+ ...(ngDevMode ? [{ debugName: "showStats" }] : /* istanbul ignore next */ []));
268
+ this.showFeelsLike = computed(() => shouldShowWeatherFeelsLike(this.layoutVariant()), /* @ts-ignore */
269
+ ...(ngDevMode ? [{ debugName: "showFeelsLike" }] : /* istanbul ignore next */ []));
270
+ this.showHourly = computed(() => shouldShowWeatherHourly(this.layoutVariant()), /* @ts-ignore */
271
+ ...(ngDevMode ? [{ debugName: "showHourly" }] : /* istanbul ignore next */ []));
272
+ this.showMetrics = computed(() => shouldShowWeatherMetrics(this.layoutVariant()), /* @ts-ignore */
273
+ ...(ngDevMode ? [{ debugName: "showMetrics" }] : /* istanbul ignore next */ []));
274
+ this.showDaily = computed(() => shouldShowWeatherDaily(this.layoutVariant(), this.showForecast()), /* @ts-ignore */
275
+ ...(ngDevMode ? [{ debugName: "showDaily" }] : /* istanbul ignore next */ []));
276
+ this.effectiveForecastHours = computed(() => resolveWeatherHourlyCount(this.layoutVariant(), this.forecastHours()), /* @ts-ignore */
277
+ ...(ngDevMode ? [{ debugName: "effectiveForecastHours" }] : /* istanbul ignore next */ []));
278
+ this.displayedDailyForecast = computed(() => {
279
+ const weatherData = this.weatherData();
280
+ if (!weatherData?.forecast) {
281
+ return [];
282
+ }
283
+ return weatherData.forecast.slice(1, this.forecastDays());
284
+ }, /* @ts-ignore */
285
+ ...(ngDevMode ? [{ debugName: "displayedDailyForecast" }] : /* istanbul ignore next */ []));
286
+ this.displayedHourlyForecast = computed(() => {
287
+ const maxHours = this.effectiveForecastHours();
288
+ const weatherData = this.weatherData();
289
+ if (!weatherData?.forecast?.[0]?.hour) {
290
+ return [];
291
+ }
292
+ const now = new Date();
293
+ const currentHour = now.getHours();
294
+ const todayHours = weatherData.forecast[0].hour;
295
+ const remainingTodayHours = todayHours.filter((hour) => {
296
+ const hourTime = new Date(hour.time);
297
+ return hourTime.getHours() > currentHour;
298
+ });
299
+ let result = [...remainingTodayHours];
300
+ if (result.length < maxHours && weatherData.forecast.length > 1) {
301
+ const tomorrowHours = weatherData.forecast[1].hour;
302
+ const neededFromTomorrow = maxHours - result.length;
303
+ result = [...result, ...tomorrowHours.slice(0, neededFromTomorrow)];
304
+ }
305
+ return result.slice(0, maxHours);
306
+ }, /* @ts-ignore */
307
+ ...(ngDevMode ? [{ debugName: "displayedHourlyForecast" }] : /* istanbul ignore next */ []));
308
+ effect(() => {
309
+ this.options();
310
+ this.loadWeatherData();
311
+ this.setupRefreshTimer();
312
+ });
313
+ this.destroyRef.onDestroy(() => this.clearRefreshTimer());
314
+ }
315
+ onContainerSizeChanged(size) {
316
+ this.containerWidth.set(size.width);
317
+ this.containerHeight.set(size.height);
318
+ }
319
+ refreshWeather() {
320
+ this.loadWeatherData();
321
+ }
322
+ getConditionIcon(conditionId) {
323
+ const id = conditionId || 'unknown';
324
+ if (this.iconCache.has(id)) {
325
+ return this.iconCache.get(id);
326
+ }
327
+ const iconPath = this.weatherService.resolveConditionIcon(id);
328
+ this.iconCache.set(id, iconPath);
329
+ return iconPath;
330
+ }
331
+ getConditionName(conditionId) {
332
+ const condition = this.weatherService.getCondition(conditionId);
333
+ if (!condition?.name) {
334
+ return '@dashboard:weather.conditions.unknown';
335
+ }
336
+ const formattedName = condition.name.toLowerCase().replace(/\s+/g, '-');
337
+ return `@dashboard:weather.conditions.${formattedName}`;
338
+ }
339
+ getCurrentTemperature() {
340
+ const data = this.weatherData();
341
+ if (!data) {
342
+ return 0;
343
+ }
344
+ return this.temperatureUnit() === '°C' ? data.current.tempC : data.current.tempF;
345
+ }
346
+ getFeelsLikeTemperature() {
347
+ const data = this.weatherData();
348
+ if (!data) {
349
+ return 0;
350
+ }
351
+ return this.temperatureUnit() === '°C' ? data.current.feelsLikeC : data.current.feelsLikeF;
352
+ }
353
+ getMaxTemp() {
354
+ const day = this.weatherData()?.forecast?.[0];
355
+ if (!day) {
356
+ return 0;
357
+ }
358
+ return this.temperatureUnit() === '°C' ? day.maxTempC : day.maxTempF;
359
+ }
360
+ getMinTemp() {
361
+ const day = this.weatherData()?.forecast?.[0];
362
+ if (!day) {
363
+ return 0;
364
+ }
365
+ return this.temperatureUnit() === '°C' ? day.minTempC : day.minTempF;
366
+ }
367
+ getHumidity() {
368
+ return this.weatherData()?.current.humidity ?? 0;
369
+ }
370
+ getWindSpeed() {
371
+ const data = this.weatherData();
372
+ if (!data) {
373
+ return 0;
374
+ }
375
+ return this.windSpeedUnit() === 'km/h' ? data.current.windKph : data.current.windMph;
376
+ }
377
+ getUvIndex() {
378
+ return this.weatherData()?.current.uv ?? 0;
379
+ }
380
+ getWindSpeedUnitKey() {
381
+ return this.windSpeedUnit() === 'km/h' ? '@dashboard:weather.units.kmh' : '@dashboard:weather.units.mph';
382
+ }
383
+ getForecastDayTemp(day) {
384
+ return this.temperatureUnit() === '°C' ? day.maxTempC : day.maxTempF;
385
+ }
386
+ getForecastDayMinTemp(day) {
387
+ return this.temperatureUnit() === '°C' ? day.minTempC : day.minTempF;
388
+ }
389
+ getHourLabel(timeStr) {
390
+ const time = new Date(timeStr);
391
+ return time.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', hour12: false });
392
+ }
393
+ getHourTemp(hour) {
394
+ return this.temperatureUnit() === '°C' ? hour.tempC : hour.tempF;
395
+ }
396
+ getDayName(date) {
397
+ const today = new Date();
398
+ today.setHours(0, 0, 0, 0);
399
+ const tomorrow = new Date(today);
400
+ tomorrow.setDate(tomorrow.getDate() + 1);
401
+ const checkDate = new Date(date);
402
+ checkDate.setHours(0, 0, 0, 0);
403
+ if (checkDate.getTime() === today.getTime()) {
404
+ return '@dashboard:weather.days.today';
405
+ }
406
+ if (checkDate.getTime() === tomorrow.getTime()) {
407
+ return '@dashboard:weather.days.tomorrow';
408
+ }
409
+ const days = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];
410
+ return `@dashboard:weather.days.${days[checkDate.getDay()]}`;
411
+ }
412
+ parseDate(dateStr) {
413
+ return new Date(dateStr);
414
+ }
415
+ locationShortLabel() {
416
+ const location = this.weatherData()?.location;
417
+ const raw = (location?.country || location?.city || this.city()).trim();
418
+ if (/^[A-Za-z]{2,3}$/.test(raw)) {
419
+ return raw.toUpperCase();
420
+ }
421
+ const words = raw.split(/\s+/).filter(Boolean);
422
+ if (words.length >= 2) {
423
+ return `${words[0][0] ?? ''}${words[1][0] ?? ''}`.toUpperCase();
424
+ }
425
+ return raw.slice(0, 2).toUpperCase();
426
+ }
427
+ loadWeatherData() {
428
+ if (!this.city()) {
429
+ this.isLoading.set(false);
430
+ return;
431
+ }
432
+ this.isLoading.set(true);
433
+ this.hasError.set(false);
434
+ this.errorMessage.set('');
435
+ const requestDays = Math.max(this.forecastDays(), 2);
436
+ this.weatherService
437
+ .getForecast({
438
+ location: this.city(),
439
+ tempUnit: this.temperatureUnit(),
440
+ days: requestDays,
441
+ })
442
+ .pipe(catchError((error) => {
443
+ this.handleError(error);
444
+ return of(null);
445
+ }), finalize(() => this.isLoading.set(false)), takeUntilDestroyed(this.destroyRef))
446
+ .subscribe((data) => {
447
+ if (data) {
448
+ this.weatherData.set(data);
449
+ }
450
+ });
451
+ }
452
+ setupRefreshTimer() {
453
+ this.clearRefreshTimer();
454
+ if (!this.autoRefresh()) {
455
+ return;
456
+ }
457
+ const intervalValue = Math.max(this.refreshInterval(), 5);
458
+ const intervalMs = intervalValue * 60 * 1000;
459
+ this.refreshSubscription = interval(intervalMs)
460
+ .pipe(switchMap(() => {
461
+ this.loadWeatherData();
462
+ return of(null);
463
+ }), takeUntilDestroyed(this.destroyRef))
464
+ .subscribe();
465
+ }
466
+ clearRefreshTimer() {
467
+ if (this.refreshSubscription) {
468
+ this.refreshSubscription.unsubscribe();
469
+ this.refreshSubscription = null;
470
+ }
471
+ }
472
+ handleError(error) {
473
+ console.error('Weather widget error:', error);
474
+ this.hasError.set(true);
475
+ const status = error?.status;
476
+ if (status === 0) {
477
+ this.errorMessage.set('@dashboard:weather.error.network');
478
+ return;
479
+ }
480
+ if (status !== undefined && status >= 400 && status < 500) {
481
+ this.errorMessage.set('@dashboard:weather.error.api');
482
+ return;
483
+ }
484
+ this.errorMessage.set('@dashboard:weather.error.general');
485
+ }
486
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPWeatherWidgetViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
487
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: AXPWeatherWidgetViewComponent, isStandalone: true, selector: "ng-component", providers: [
488
+ {
489
+ provide: AXPWeatherApiAbstract,
490
+ useClass: AXEWeatherGatewayService,
491
+ },
492
+ ], usesInheritance: true, ngImport: i0, template: "<div\n class=\"weather-card weather-card--{{ layoutVariant() }}\"\n [style.background]=\"visualTheme().background\"\n [style.--weather-high]=\"visualTheme().high\"\n [style.--weather-low]=\"visualTheme().low\"\n [style.--weather-accent]=\"visualTheme().accent\"\n [attr.aria-label]=\"'Current weather for ' + city()\"\n>\n @if (isLoading()) {\n <div class=\"weather-card__state\" [attr.aria-busy]=\"true\">\n <ax-skeleton class=\"weather-card__skeleton-icon\"></ax-skeleton>\n <div class=\"weather-card__skeleton-lines\">\n <ax-skeleton class=\"weather-card__skeleton-line weather-card__skeleton-line--lg\"></ax-skeleton>\n <ax-skeleton class=\"weather-card__skeleton-line\"></ax-skeleton>\n </div>\n </div>\n } @else if (hasError()) {\n <div class=\"weather-card__state\">\n <axp-state-message\n mode=\"error\"\n icon=\"fa-solid fa-exclamation-triangle\"\n [title]=\"'@dashboard:weather.error.title'\"\n [description]=\"errorMessage()\"\n >\n <button slot=\"actions\" type=\"button\" class=\"weather-card__retry\" (click)=\"refreshWeather()\">\n <i class=\"fa-solid fa-sync-alt\" aria-hidden=\"true\"></i>\n <span>{{ '@dashboard:weather.retry' | translate | async }}</span>\n </button>\n </axp-state-message>\n </div>\n } @else if (weatherData()) {\n <div class=\"weather-card__blob\" [style.background]=\"visualTheme().blob\" aria-hidden=\"true\"></div>\n\n @if (layoutVariant() === '1x2') {\n <div class=\"weather-card__tall\">\n <div class=\"weather-card__hero\">\n <div class=\"weather-card__hero-core\">\n <img\n [src]=\"getConditionIcon(weatherData()!.current.condition)\"\n class=\"weather-card__hero-icon\"\n [alt]=\"getConditionName(weatherData()!.current.condition) | translate | async\"\n loading=\"lazy\"\n />\n <span class=\"weather-card__temp\">{{ getCurrentTemperature() | number: '1.0-0' }}\u00B0</span>\n </div>\n <div class=\"weather-card__inline-location\">\n <i class=\"fa-solid fa-location-dot\" aria-hidden=\"true\"></i>\n <span>{{ locationShortLabel() }}</span>\n </div>\n </div>\n\n <p class=\"weather-card__condition weather-card__condition--center\">\n {{ getConditionName(weatherData()!.current.condition) | translate | async }}\n </p>\n\n @if (showStats()) {\n <div class=\"weather-card__footer-stats\">\n <span class=\"weather-card__stat weather-card__stat--high\">\n H {{ getMaxTemp() | number: '1.0-0' }}\u00B0\n </span>\n <span class=\"weather-card__stat weather-card__stat--low\">\n L {{ getMinTemp() | number: '1.0-0' }}\u00B0\n </span>\n </div>\n }\n </div>\n } @else {\n <header class=\"weather-card__header\">\n <img\n [src]=\"getConditionIcon(weatherData()!.current.condition)\"\n class=\"weather-card__icon\"\n [alt]=\"getConditionName(weatherData()!.current.condition) | translate | async\"\n loading=\"lazy\"\n />\n <div class=\"weather-card__location\">\n <span class=\"weather-card__city\">{{ city() }}</span>\n <i class=\"fa-solid fa-location-dot\" aria-hidden=\"true\"></i>\n </div>\n </header>\n\n <div class=\"weather-card__body\">\n <div class=\"weather-card__primary\">\n <div class=\"weather-card__temp\">{{ getCurrentTemperature() | number: '1.0-0' }}\u00B0</div>\n <p class=\"weather-card__condition\">\n {{ getConditionName(weatherData()!.current.condition) | translate | async }}\n </p>\n </div>\n\n @if (showStats()) {\n <div class=\"weather-card__stats\">\n <span class=\"weather-card__stat weather-card__stat--high\">\n H {{ getMaxTemp() | number: '1.0-0' }}\u00B0\n </span>\n <span class=\"weather-card__stat weather-card__stat--low\">\n L {{ getMinTemp() | number: '1.0-0' }}\u00B0\n </span>\n @if (showFeelsLike()) {\n <span class=\"weather-card__stat weather-card__stat--feels\">\n Feels {{ getFeelsLikeTemperature() | number: '1.0-0' }}\u00B0\n </span>\n }\n </div>\n }\n\n @if (showHourly()) {\n <section class=\"weather-card__hourly\">\n <h4 class=\"weather-card__section-title\">\n {{ '@dashboard:weather.hourly-forecast' | translate | async }}\n </h4>\n <div class=\"weather-card__hourly-row\">\n @for (hour of displayedHourlyForecast(); track hour.time) {\n <div class=\"weather-card__hour\">\n <span class=\"weather-card__hour-time\">{{ getHourLabel(hour.time) }}</span>\n <img\n [src]=\"getConditionIcon(hour.condition)\"\n class=\"weather-card__hour-icon\"\n [alt]=\"getConditionName(hour.condition) | translate | async\"\n loading=\"lazy\"\n />\n <span class=\"weather-card__hour-temp\">{{ getHourTemp(hour) | number: '1.0-0' }}\u00B0</span>\n </div>\n }\n </div>\n </section>\n }\n\n @if (showDaily()) {\n <section class=\"weather-card__daily\">\n <h4 class=\"weather-card__section-title\">\n {{ '@dashboard:weather.forecast' | translate | async }}\n </h4>\n <div class=\"weather-card__daily-list\">\n @for (day of displayedDailyForecast(); track day.date) {\n <div class=\"weather-card__daily-item\">\n <span class=\"weather-card__daily-name\">\n {{ getDayName(parseDate(day.date)) | translate | async }}\n </span>\n <img\n [src]=\"getConditionIcon(day.condition)\"\n class=\"weather-card__daily-icon\"\n [alt]=\"getConditionName(day.condition) | translate | async\"\n loading=\"lazy\"\n />\n <span class=\"weather-card__daily-high\">{{ getForecastDayTemp(day) | number: '1.0-0' }}\u00B0</span>\n <span class=\"weather-card__daily-low\">{{ getForecastDayMinTemp(day) | number: '1.0-0' }}\u00B0</span>\n </div>\n }\n </div>\n </section>\n }\n\n @if (showMetrics() && (showHumidity() || showWind() || getUvIndex() > 0)) {\n <section class=\"weather-card__metrics\">\n @if (showHumidity()) {\n <div class=\"weather-card__metric\">\n <i class=\"fa-solid fa-droplet\" aria-hidden=\"true\"></i>\n <span class=\"weather-card__metric-label\">{{ '@dashboard:weather.humidity' | translate | async }}</span>\n <span class=\"weather-card__metric-value\">{{ getHumidity() }}%</span>\n </div>\n }\n @if (showWind()) {\n <div class=\"weather-card__metric\">\n <i class=\"fa-solid fa-wind\" aria-hidden=\"true\"></i>\n <span class=\"weather-card__metric-label\">{{ '@dashboard:weather.wind' | translate | async }}</span>\n <span class=\"weather-card__metric-value\">\n {{ getWindSpeed() }} {{ getWindSpeedUnitKey() | translate | async }}\n </span>\n </div>\n }\n @if (getUvIndex() > 0) {\n <div class=\"weather-card__metric\">\n <i class=\"fa-solid fa-sun\" aria-hidden=\"true\"></i>\n <span class=\"weather-card__metric-label\">{{ '@dashboard:weather.uv-index' | translate | async }}</span>\n <span class=\"weather-card__metric-value\">{{ getUvIndex() }}</span>\n </div>\n }\n </section>\n }\n </div>\n }\n } @else {\n <div class=\"weather-card__state\">\n <axp-state-message\n icon=\"fa-solid fa-cloud-sun\"\n [title]=\"'@dashboard:weather.no-data.title'\"\n [description]=\"'@dashboard:weather.no-data.description'\"\n >\n <button slot=\"actions\" type=\"button\" class=\"weather-card__retry\" (click)=\"refreshWeather()\">\n <i class=\"fa-solid fa-sync-alt\" aria-hidden=\"true\"></i>\n <span>{{ '@dashboard:weather.load-data' | translate | async }}</span>\n </button>\n </axp-state-message>\n </div>\n }\n</div>\n", styles: ["@layer properties;:host{display:block;width:100%;height:100%}.weather-card{--weather-icon-size: 3rem;--weather-temp-size: 2.75rem;--weather-label-size: .875rem;--weather-meta-size: .8125rem;--weather-caption-size: .6875rem;position:relative;display:flex;width:100%;height:100%;min-height:0px;flex-direction:column;overflow:hidden;padding:1rem 1.125rem;border-radius:1rem;border:1px solid var(--weather-accent, #6ba4e8);color:#0f172a;box-shadow:0 1px 3px #0f172a0f}@supports (color: color-mix(in lab,red,red)){.weather-card{border:1px solid color-mix(in srgb,var(--weather-accent, #6ba4e8) 16%,white)}}.weather-card .weather-card__blob{pointer-events:none;position:absolute;inset:0}.weather-card .weather-card__state{position:relative;z-index:1;display:flex;width:100%;height:100%;flex-direction:column;align-items:center;justify-content:center;gap:.625rem}.weather-card .weather-card__retry{display:inline-flex;align-items:center;gap:.375rem}.weather-card .weather-card__header{position:relative;z-index:1;display:flex;flex-shrink:0;align-items:center;justify-content:space-between;gap:.75rem}.weather-card .weather-card__icon{width:var(--weather-icon-size);height:var(--weather-icon-size);flex-shrink:0;object-fit:contain}.weather-card .weather-card__location{display:inline-flex;min-width:0px;align-items:center;--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);gap:.3rem;font-size:var(--weather-meta-size);color:#334155}.weather-card .weather-card__location i{font-size:.6875rem;color:#94a3b8}.weather-card .weather-card__city{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-transform:capitalize}.weather-card .weather-card__body{position:relative;z-index:1;display:flex;min-height:0px;flex:1;flex-direction:column;gap:.625rem;margin-top:.375rem}.weather-card .weather-card__primary{display:flex;min-width:0px;flex-direction:column;gap:.25rem}.weather-card .weather-card__temp{font-size:var(--weather-temp-size);--tw-leading: 1;line-height:1;--tw-font-weight: var(--font-weight-bold, 700);font-weight:var(--font-weight-bold, 700);letter-spacing:-.04em;color:#0f172a}.weather-card .weather-card__condition{margin:0;--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500);font-size:var(--weather-label-size);line-height:1.35;color:#475569}.weather-card .weather-card__stats{display:flex;flex-wrap:wrap;align-items:center;--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);gap:.75rem 1rem;font-size:var(--weather-meta-size)}.weather-card .weather-card__stat--high{color:var(--weather-high, #f59e0b)}.weather-card .weather-card__stat--low{color:var(--weather-low, #3b82f6)}.weather-card .weather-card__stat--feels{--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500);color:#64748b}.weather-card .weather-card__section-title{margin:0 0 .5rem;font-size:var(--weather-caption-size);--tw-font-weight: var(--font-weight-bold, 700);font-weight:var(--font-weight-bold, 700);text-transform:uppercase;letter-spacing:.08em;color:#94a3b8}.weather-card .weather-card__hourly-row{display:flex;overflow-x:auto;gap:.375rem}.weather-card .weather-card__hour{display:flex;flex:1;flex-direction:column;align-items:center;gap:.25rem;min-width:2.75rem}.weather-card .weather-card__hour-time{font-size:var(--weather-caption-size);--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500);color:#94a3b8}.weather-card .weather-card__hour-icon{width:calc(var(--weather-icon-size) * .55);height:calc(var(--weather-icon-size) * .55);object-fit:contain}.weather-card .weather-card__hour-temp{font-size:var(--weather-meta-size);--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600)}.weather-card .weather-card__daily-list{display:flex;flex-direction:column;gap:.375rem}.weather-card .weather-card__daily-item{display:grid;align-items:center;grid-template-columns:minmax(4rem,1fr) auto auto auto;gap:.625rem;font-size:var(--weather-meta-size)}.weather-card .weather-card__daily-name{--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500);color:#334155}.weather-card .weather-card__daily-icon{width:calc(var(--weather-icon-size) * .5);height:calc(var(--weather-icon-size) * .5);object-fit:contain}.weather-card .weather-card__daily-high{--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600)}.weather-card .weather-card__daily-low{color:#64748b}.weather-card .weather-card__metrics{display:flex;flex-direction:column;gap:.625rem}.weather-card .weather-card__metric{display:grid;align-items:center;grid-template-columns:auto 1fr auto;gap:.5rem;font-size:var(--weather-meta-size);color:#64748b}.weather-card .weather-card__metric i{color:var(--weather-accent, #3b82f6);width:1rem;text-align:center;font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)))}.weather-card .weather-card__metric-value{--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);color:#0f172a}.weather-card .weather-card__skeleton-icon{width:var(--weather-icon-size);height:var(--weather-icon-size);border-radius:calc(infinity * 1px)}.weather-card .weather-card__skeleton-lines{display:flex;width:100%;flex-direction:column;gap:.375rem}.weather-card .weather-card__skeleton-line{width:55%;height:.75rem;border-radius:var(--radius-md, .375rem)}.weather-card .weather-card__skeleton-line--lg{width:35%;height:1.75rem}.weather-card--1x2{--weather-temp-size: 2.125rem;--weather-label-size: .875rem;--weather-meta-size: .8125rem;padding:.625rem .625rem .5rem}.weather-card--1x2 .weather-card__tall{position:relative;z-index:1;display:grid;width:100%;height:100%;min-height:0px;align-items:stretch;grid-template-rows:auto 1fr auto}.weather-card--1x2 .weather-card__hero{display:flex;width:100%;align-items:flex-start;justify-content:center;gap:.125rem;padding-top:.125rem}.weather-card--1x2 .weather-card__hero-core{position:relative;display:inline-flex;align-items:center;padding-left:1.375rem}.weather-card--1x2 .weather-card__hero-icon{pointer-events:none;position:absolute;top:50%;left:0;z-index:0;object-fit:contain;width:2.125rem;height:2.125rem;transform:translateY(-50%)}.weather-card--1x2 .weather-card__temp{position:relative;z-index:1;--tw-leading: 1;line-height:1;--tw-font-weight: var(--font-weight-bold, 700);font-weight:var(--font-weight-bold, 700);font-size:var(--weather-temp-size);letter-spacing:-.03em}.weather-card--1x2 .weather-card__inline-location{display:inline-flex;flex-shrink:0;align-items:center;--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);white-space:nowrap;gap:.125rem;margin-top:.2rem;font-size:.625rem;color:#64748b}.weather-card--1x2 .weather-card__inline-location i{font-size:.5rem;color:#94a3b8}.weather-card--1x2 .weather-card__condition--center{margin:0;display:flex;align-items:center;justify-content:center;text-align:center;--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500);font-size:var(--weather-label-size);color:#334155}.weather-card--1x2 .weather-card__footer-stats{display:flex;width:100%;align-items:center;justify-content:space-between;--tw-leading: 1;line-height:1;--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);gap:.5rem;font-size:var(--weather-meta-size)}.weather-card--1x2 .weather-card__stat--high,.weather-card--1x2 .weather-card__stat--low{color:var(--weather-low, #3b82f6)}.weather-card--2x1{--weather-icon-size: 2.75rem;--weather-temp-size: 2.5rem;padding:.875rem 1rem}.weather-card--2x1 .weather-card__body{flex-direction:row;flex-wrap:wrap;align-items:center;column-gap:1.25rem;row-gap:.5rem;margin-top:.25rem}.weather-card--2x1 .weather-card__primary{flex-shrink:0;flex-grow:0}.weather-card--2x1 .weather-card__stats{flex:auto;justify-content:flex-end;margin-left:auto}.weather-card--2x1 .weather-card__hourly{flex:1 1 100%;min-width:0;overflow:hidden}.weather-card--2x1 .weather-card__hourly-row{justify-content:space-between;overflow:hidden}.weather-card--2x1 .weather-card__hour{min-width:0px;flex:1}.weather-card--2x2{--weather-icon-size: 3.5rem;--weather-temp-size: 3.25rem;--weather-label-size: .9375rem;--weather-meta-size: .875rem}.weather-card--2x2 .weather-card__body{flex:1;justify-content:space-between;gap:.5rem}.weather-card--2x2 .weather-card__stats{margin-top:auto}.weather-card--4x2{--weather-icon-size: 2.75rem;--weather-temp-size: 2.75rem;--weather-label-size: .875rem;--weather-meta-size: .8125rem;--weather-caption-size: .625rem;padding:.75rem 1rem}.weather-card--4x2 .weather-card__body{display:grid;min-height:0px;flex:1;align-items:stretch;grid-template-columns:minmax(0,.95fr) minmax(0,1.8fr) minmax(0,1fr);grid-template-rows:auto 1fr;column-gap:.875rem;row-gap:.375rem;margin-top:.25rem}.weather-card--4x2 .weather-card__primary{grid-column:1;grid-row:1;min-width:0}.weather-card--4x2 .weather-card__condition{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.weather-card--4x2 .weather-card__stats{flex-direction:row;flex-wrap:wrap;align-content:flex-start;align-items:center;grid-column:1;grid-row:2;gap:.35rem .65rem;min-width:0;align-self:end}.weather-card--4x2 .weather-card__hourly{display:flex;min-height:0px;flex-direction:column;grid-column:2;grid-row:1 / span 2;overflow:hidden}.weather-card--4x2 .weather-card__section-title{margin:0 0 .35rem}.weather-card--4x2 .weather-card__hourly-row{display:flex;flex:1;align-items:stretch;justify-content:space-between;gap:.125rem;overflow:hidden}.weather-card--4x2 .weather-card__hour{display:flex;min-width:0px;flex:1;flex-direction:column;align-items:center;justify-content:center;gap:.2rem}.weather-card--4x2 .weather-card__hour-icon{width:1.5rem;height:1.5rem}.weather-card--4x2 .weather-card__hour-time,.weather-card--4x2 .weather-card__hour-temp{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.weather-card--4x2 .weather-card__daily{display:none}.weather-card--4x2 .weather-card__metrics{display:flex;height:100%;flex-direction:column;justify-content:space-evenly;grid-column:3;grid-row:1 / span 2;min-width:0;gap:.5rem}.weather-card--4x2 .weather-card__metric{grid-template-columns:auto minmax(0,1fr) auto;gap:.375rem}.weather-card--4x2 .weather-card__metric-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.weather-card--4x4{--weather-icon-size: 4rem;--weather-temp-size: 3.75rem;--weather-label-size: 1rem;--weather-meta-size: .875rem;--weather-caption-size: .75rem;padding:1.125rem 1.375rem}.weather-card--4x4 .weather-card__body{display:grid;align-items:flex-start;grid-template-columns:minmax(10rem,1fr) minmax(0,2.4fr) minmax(8rem,.9fr);grid-template-rows:auto auto;gap:1rem 1.5rem;margin-top:.625rem}.weather-card--4x4 .weather-card__primary{grid-column:1;grid-row:1}.weather-card--4x4 .weather-card__stats{flex-direction:column;align-items:flex-start;grid-column:1;grid-row:2;gap:.5rem}.weather-card--4x4 .weather-card__hourly{grid-column:2;grid-row:1;min-width:0;overflow:hidden}.weather-card--4x4 .weather-card__hourly-row{justify-content:space-between;overflow:hidden}.weather-card--4x4 .weather-card__hour{min-width:0px;flex:1}.weather-card--4x4 .weather-card__daily{grid-column:2;grid-row:2}.weather-card--4x4 .weather-card__metrics{justify-content:space-evenly;grid-column:3;grid-row:1 / span 2;gap:.875rem}.weather-card--4x4 .weather-card__hour-icon{width:calc(var(--weather-icon-size) * .5);height:calc(var(--weather-icon-size) * .5)}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@property --tw-leading{syntax: \"*\"; inherits: false;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-font-weight: initial;--tw-leading: initial}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "ngmodule", type: AXSkeletonModule }, { kind: "component", type: i1.AXSkeletonComponent, selector: "ax-skeleton", inputs: ["animated"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: AXPStateMessageComponent, selector: "axp-state-message", inputs: ["mode", "icon", "title", "description", "titleParams", "descriptionParams", "look"] }, { kind: "pipe", type: i2.AsyncPipe, name: "async" }, { kind: "pipe", type: i2.DecimalPipe, name: "number" }, { kind: "pipe", type: i3.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
493
+ }
494
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXPWeatherWidgetViewComponent, decorators: [{
495
+ type: Component,
496
+ args: [{ standalone: true, imports: [
497
+ CommonModule,
498
+ AXTranslationModule,
499
+ AXSkeletonModule,
500
+ AXButtonModule,
501
+ AXPStateMessageComponent,
502
+ ], providers: [
503
+ {
504
+ provide: AXPWeatherApiAbstract,
505
+ useClass: AXEWeatherGatewayService,
506
+ },
507
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n class=\"weather-card weather-card--{{ layoutVariant() }}\"\n [style.background]=\"visualTheme().background\"\n [style.--weather-high]=\"visualTheme().high\"\n [style.--weather-low]=\"visualTheme().low\"\n [style.--weather-accent]=\"visualTheme().accent\"\n [attr.aria-label]=\"'Current weather for ' + city()\"\n>\n @if (isLoading()) {\n <div class=\"weather-card__state\" [attr.aria-busy]=\"true\">\n <ax-skeleton class=\"weather-card__skeleton-icon\"></ax-skeleton>\n <div class=\"weather-card__skeleton-lines\">\n <ax-skeleton class=\"weather-card__skeleton-line weather-card__skeleton-line--lg\"></ax-skeleton>\n <ax-skeleton class=\"weather-card__skeleton-line\"></ax-skeleton>\n </div>\n </div>\n } @else if (hasError()) {\n <div class=\"weather-card__state\">\n <axp-state-message\n mode=\"error\"\n icon=\"fa-solid fa-exclamation-triangle\"\n [title]=\"'@dashboard:weather.error.title'\"\n [description]=\"errorMessage()\"\n >\n <button slot=\"actions\" type=\"button\" class=\"weather-card__retry\" (click)=\"refreshWeather()\">\n <i class=\"fa-solid fa-sync-alt\" aria-hidden=\"true\"></i>\n <span>{{ '@dashboard:weather.retry' | translate | async }}</span>\n </button>\n </axp-state-message>\n </div>\n } @else if (weatherData()) {\n <div class=\"weather-card__blob\" [style.background]=\"visualTheme().blob\" aria-hidden=\"true\"></div>\n\n @if (layoutVariant() === '1x2') {\n <div class=\"weather-card__tall\">\n <div class=\"weather-card__hero\">\n <div class=\"weather-card__hero-core\">\n <img\n [src]=\"getConditionIcon(weatherData()!.current.condition)\"\n class=\"weather-card__hero-icon\"\n [alt]=\"getConditionName(weatherData()!.current.condition) | translate | async\"\n loading=\"lazy\"\n />\n <span class=\"weather-card__temp\">{{ getCurrentTemperature() | number: '1.0-0' }}\u00B0</span>\n </div>\n <div class=\"weather-card__inline-location\">\n <i class=\"fa-solid fa-location-dot\" aria-hidden=\"true\"></i>\n <span>{{ locationShortLabel() }}</span>\n </div>\n </div>\n\n <p class=\"weather-card__condition weather-card__condition--center\">\n {{ getConditionName(weatherData()!.current.condition) | translate | async }}\n </p>\n\n @if (showStats()) {\n <div class=\"weather-card__footer-stats\">\n <span class=\"weather-card__stat weather-card__stat--high\">\n H {{ getMaxTemp() | number: '1.0-0' }}\u00B0\n </span>\n <span class=\"weather-card__stat weather-card__stat--low\">\n L {{ getMinTemp() | number: '1.0-0' }}\u00B0\n </span>\n </div>\n }\n </div>\n } @else {\n <header class=\"weather-card__header\">\n <img\n [src]=\"getConditionIcon(weatherData()!.current.condition)\"\n class=\"weather-card__icon\"\n [alt]=\"getConditionName(weatherData()!.current.condition) | translate | async\"\n loading=\"lazy\"\n />\n <div class=\"weather-card__location\">\n <span class=\"weather-card__city\">{{ city() }}</span>\n <i class=\"fa-solid fa-location-dot\" aria-hidden=\"true\"></i>\n </div>\n </header>\n\n <div class=\"weather-card__body\">\n <div class=\"weather-card__primary\">\n <div class=\"weather-card__temp\">{{ getCurrentTemperature() | number: '1.0-0' }}\u00B0</div>\n <p class=\"weather-card__condition\">\n {{ getConditionName(weatherData()!.current.condition) | translate | async }}\n </p>\n </div>\n\n @if (showStats()) {\n <div class=\"weather-card__stats\">\n <span class=\"weather-card__stat weather-card__stat--high\">\n H {{ getMaxTemp() | number: '1.0-0' }}\u00B0\n </span>\n <span class=\"weather-card__stat weather-card__stat--low\">\n L {{ getMinTemp() | number: '1.0-0' }}\u00B0\n </span>\n @if (showFeelsLike()) {\n <span class=\"weather-card__stat weather-card__stat--feels\">\n Feels {{ getFeelsLikeTemperature() | number: '1.0-0' }}\u00B0\n </span>\n }\n </div>\n }\n\n @if (showHourly()) {\n <section class=\"weather-card__hourly\">\n <h4 class=\"weather-card__section-title\">\n {{ '@dashboard:weather.hourly-forecast' | translate | async }}\n </h4>\n <div class=\"weather-card__hourly-row\">\n @for (hour of displayedHourlyForecast(); track hour.time) {\n <div class=\"weather-card__hour\">\n <span class=\"weather-card__hour-time\">{{ getHourLabel(hour.time) }}</span>\n <img\n [src]=\"getConditionIcon(hour.condition)\"\n class=\"weather-card__hour-icon\"\n [alt]=\"getConditionName(hour.condition) | translate | async\"\n loading=\"lazy\"\n />\n <span class=\"weather-card__hour-temp\">{{ getHourTemp(hour) | number: '1.0-0' }}\u00B0</span>\n </div>\n }\n </div>\n </section>\n }\n\n @if (showDaily()) {\n <section class=\"weather-card__daily\">\n <h4 class=\"weather-card__section-title\">\n {{ '@dashboard:weather.forecast' | translate | async }}\n </h4>\n <div class=\"weather-card__daily-list\">\n @for (day of displayedDailyForecast(); track day.date) {\n <div class=\"weather-card__daily-item\">\n <span class=\"weather-card__daily-name\">\n {{ getDayName(parseDate(day.date)) | translate | async }}\n </span>\n <img\n [src]=\"getConditionIcon(day.condition)\"\n class=\"weather-card__daily-icon\"\n [alt]=\"getConditionName(day.condition) | translate | async\"\n loading=\"lazy\"\n />\n <span class=\"weather-card__daily-high\">{{ getForecastDayTemp(day) | number: '1.0-0' }}\u00B0</span>\n <span class=\"weather-card__daily-low\">{{ getForecastDayMinTemp(day) | number: '1.0-0' }}\u00B0</span>\n </div>\n }\n </div>\n </section>\n }\n\n @if (showMetrics() && (showHumidity() || showWind() || getUvIndex() > 0)) {\n <section class=\"weather-card__metrics\">\n @if (showHumidity()) {\n <div class=\"weather-card__metric\">\n <i class=\"fa-solid fa-droplet\" aria-hidden=\"true\"></i>\n <span class=\"weather-card__metric-label\">{{ '@dashboard:weather.humidity' | translate | async }}</span>\n <span class=\"weather-card__metric-value\">{{ getHumidity() }}%</span>\n </div>\n }\n @if (showWind()) {\n <div class=\"weather-card__metric\">\n <i class=\"fa-solid fa-wind\" aria-hidden=\"true\"></i>\n <span class=\"weather-card__metric-label\">{{ '@dashboard:weather.wind' | translate | async }}</span>\n <span class=\"weather-card__metric-value\">\n {{ getWindSpeed() }} {{ getWindSpeedUnitKey() | translate | async }}\n </span>\n </div>\n }\n @if (getUvIndex() > 0) {\n <div class=\"weather-card__metric\">\n <i class=\"fa-solid fa-sun\" aria-hidden=\"true\"></i>\n <span class=\"weather-card__metric-label\">{{ '@dashboard:weather.uv-index' | translate | async }}</span>\n <span class=\"weather-card__metric-value\">{{ getUvIndex() }}</span>\n </div>\n }\n </section>\n }\n </div>\n }\n } @else {\n <div class=\"weather-card__state\">\n <axp-state-message\n icon=\"fa-solid fa-cloud-sun\"\n [title]=\"'@dashboard:weather.no-data.title'\"\n [description]=\"'@dashboard:weather.no-data.description'\"\n >\n <button slot=\"actions\" type=\"button\" class=\"weather-card__retry\" (click)=\"refreshWeather()\">\n <i class=\"fa-solid fa-sync-alt\" aria-hidden=\"true\"></i>\n <span>{{ '@dashboard:weather.load-data' | translate | async }}</span>\n </button>\n </axp-state-message>\n </div>\n }\n</div>\n", styles: ["@layer properties;:host{display:block;width:100%;height:100%}.weather-card{--weather-icon-size: 3rem;--weather-temp-size: 2.75rem;--weather-label-size: .875rem;--weather-meta-size: .8125rem;--weather-caption-size: .6875rem;position:relative;display:flex;width:100%;height:100%;min-height:0px;flex-direction:column;overflow:hidden;padding:1rem 1.125rem;border-radius:1rem;border:1px solid var(--weather-accent, #6ba4e8);color:#0f172a;box-shadow:0 1px 3px #0f172a0f}@supports (color: color-mix(in lab,red,red)){.weather-card{border:1px solid color-mix(in srgb,var(--weather-accent, #6ba4e8) 16%,white)}}.weather-card .weather-card__blob{pointer-events:none;position:absolute;inset:0}.weather-card .weather-card__state{position:relative;z-index:1;display:flex;width:100%;height:100%;flex-direction:column;align-items:center;justify-content:center;gap:.625rem}.weather-card .weather-card__retry{display:inline-flex;align-items:center;gap:.375rem}.weather-card .weather-card__header{position:relative;z-index:1;display:flex;flex-shrink:0;align-items:center;justify-content:space-between;gap:.75rem}.weather-card .weather-card__icon{width:var(--weather-icon-size);height:var(--weather-icon-size);flex-shrink:0;object-fit:contain}.weather-card .weather-card__location{display:inline-flex;min-width:0px;align-items:center;--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);gap:.3rem;font-size:var(--weather-meta-size);color:#334155}.weather-card .weather-card__location i{font-size:.6875rem;color:#94a3b8}.weather-card .weather-card__city{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-transform:capitalize}.weather-card .weather-card__body{position:relative;z-index:1;display:flex;min-height:0px;flex:1;flex-direction:column;gap:.625rem;margin-top:.375rem}.weather-card .weather-card__primary{display:flex;min-width:0px;flex-direction:column;gap:.25rem}.weather-card .weather-card__temp{font-size:var(--weather-temp-size);--tw-leading: 1;line-height:1;--tw-font-weight: var(--font-weight-bold, 700);font-weight:var(--font-weight-bold, 700);letter-spacing:-.04em;color:#0f172a}.weather-card .weather-card__condition{margin:0;--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500);font-size:var(--weather-label-size);line-height:1.35;color:#475569}.weather-card .weather-card__stats{display:flex;flex-wrap:wrap;align-items:center;--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);gap:.75rem 1rem;font-size:var(--weather-meta-size)}.weather-card .weather-card__stat--high{color:var(--weather-high, #f59e0b)}.weather-card .weather-card__stat--low{color:var(--weather-low, #3b82f6)}.weather-card .weather-card__stat--feels{--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500);color:#64748b}.weather-card .weather-card__section-title{margin:0 0 .5rem;font-size:var(--weather-caption-size);--tw-font-weight: var(--font-weight-bold, 700);font-weight:var(--font-weight-bold, 700);text-transform:uppercase;letter-spacing:.08em;color:#94a3b8}.weather-card .weather-card__hourly-row{display:flex;overflow-x:auto;gap:.375rem}.weather-card .weather-card__hour{display:flex;flex:1;flex-direction:column;align-items:center;gap:.25rem;min-width:2.75rem}.weather-card .weather-card__hour-time{font-size:var(--weather-caption-size);--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500);color:#94a3b8}.weather-card .weather-card__hour-icon{width:calc(var(--weather-icon-size) * .55);height:calc(var(--weather-icon-size) * .55);object-fit:contain}.weather-card .weather-card__hour-temp{font-size:var(--weather-meta-size);--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600)}.weather-card .weather-card__daily-list{display:flex;flex-direction:column;gap:.375rem}.weather-card .weather-card__daily-item{display:grid;align-items:center;grid-template-columns:minmax(4rem,1fr) auto auto auto;gap:.625rem;font-size:var(--weather-meta-size)}.weather-card .weather-card__daily-name{--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500);color:#334155}.weather-card .weather-card__daily-icon{width:calc(var(--weather-icon-size) * .5);height:calc(var(--weather-icon-size) * .5);object-fit:contain}.weather-card .weather-card__daily-high{--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600)}.weather-card .weather-card__daily-low{color:#64748b}.weather-card .weather-card__metrics{display:flex;flex-direction:column;gap:.625rem}.weather-card .weather-card__metric{display:grid;align-items:center;grid-template-columns:auto 1fr auto;gap:.5rem;font-size:var(--weather-meta-size);color:#64748b}.weather-card .weather-card__metric i{color:var(--weather-accent, #3b82f6);width:1rem;text-align:center;font-size:var(--text-sm, .875rem);line-height:var(--tw-leading, var(--text-sm--line-height, calc(1.25 / .875)))}.weather-card .weather-card__metric-value{--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);color:#0f172a}.weather-card .weather-card__skeleton-icon{width:var(--weather-icon-size);height:var(--weather-icon-size);border-radius:calc(infinity * 1px)}.weather-card .weather-card__skeleton-lines{display:flex;width:100%;flex-direction:column;gap:.375rem}.weather-card .weather-card__skeleton-line{width:55%;height:.75rem;border-radius:var(--radius-md, .375rem)}.weather-card .weather-card__skeleton-line--lg{width:35%;height:1.75rem}.weather-card--1x2{--weather-temp-size: 2.125rem;--weather-label-size: .875rem;--weather-meta-size: .8125rem;padding:.625rem .625rem .5rem}.weather-card--1x2 .weather-card__tall{position:relative;z-index:1;display:grid;width:100%;height:100%;min-height:0px;align-items:stretch;grid-template-rows:auto 1fr auto}.weather-card--1x2 .weather-card__hero{display:flex;width:100%;align-items:flex-start;justify-content:center;gap:.125rem;padding-top:.125rem}.weather-card--1x2 .weather-card__hero-core{position:relative;display:inline-flex;align-items:center;padding-left:1.375rem}.weather-card--1x2 .weather-card__hero-icon{pointer-events:none;position:absolute;top:50%;left:0;z-index:0;object-fit:contain;width:2.125rem;height:2.125rem;transform:translateY(-50%)}.weather-card--1x2 .weather-card__temp{position:relative;z-index:1;--tw-leading: 1;line-height:1;--tw-font-weight: var(--font-weight-bold, 700);font-weight:var(--font-weight-bold, 700);font-size:var(--weather-temp-size);letter-spacing:-.03em}.weather-card--1x2 .weather-card__inline-location{display:inline-flex;flex-shrink:0;align-items:center;--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);white-space:nowrap;gap:.125rem;margin-top:.2rem;font-size:.625rem;color:#64748b}.weather-card--1x2 .weather-card__inline-location i{font-size:.5rem;color:#94a3b8}.weather-card--1x2 .weather-card__condition--center{margin:0;display:flex;align-items:center;justify-content:center;text-align:center;--tw-font-weight: var(--font-weight-medium, 500);font-weight:var(--font-weight-medium, 500);font-size:var(--weather-label-size);color:#334155}.weather-card--1x2 .weather-card__footer-stats{display:flex;width:100%;align-items:center;justify-content:space-between;--tw-leading: 1;line-height:1;--tw-font-weight: var(--font-weight-semibold, 600);font-weight:var(--font-weight-semibold, 600);gap:.5rem;font-size:var(--weather-meta-size)}.weather-card--1x2 .weather-card__stat--high,.weather-card--1x2 .weather-card__stat--low{color:var(--weather-low, #3b82f6)}.weather-card--2x1{--weather-icon-size: 2.75rem;--weather-temp-size: 2.5rem;padding:.875rem 1rem}.weather-card--2x1 .weather-card__body{flex-direction:row;flex-wrap:wrap;align-items:center;column-gap:1.25rem;row-gap:.5rem;margin-top:.25rem}.weather-card--2x1 .weather-card__primary{flex-shrink:0;flex-grow:0}.weather-card--2x1 .weather-card__stats{flex:auto;justify-content:flex-end;margin-left:auto}.weather-card--2x1 .weather-card__hourly{flex:1 1 100%;min-width:0;overflow:hidden}.weather-card--2x1 .weather-card__hourly-row{justify-content:space-between;overflow:hidden}.weather-card--2x1 .weather-card__hour{min-width:0px;flex:1}.weather-card--2x2{--weather-icon-size: 3.5rem;--weather-temp-size: 3.25rem;--weather-label-size: .9375rem;--weather-meta-size: .875rem}.weather-card--2x2 .weather-card__body{flex:1;justify-content:space-between;gap:.5rem}.weather-card--2x2 .weather-card__stats{margin-top:auto}.weather-card--4x2{--weather-icon-size: 2.75rem;--weather-temp-size: 2.75rem;--weather-label-size: .875rem;--weather-meta-size: .8125rem;--weather-caption-size: .625rem;padding:.75rem 1rem}.weather-card--4x2 .weather-card__body{display:grid;min-height:0px;flex:1;align-items:stretch;grid-template-columns:minmax(0,.95fr) minmax(0,1.8fr) minmax(0,1fr);grid-template-rows:auto 1fr;column-gap:.875rem;row-gap:.375rem;margin-top:.25rem}.weather-card--4x2 .weather-card__primary{grid-column:1;grid-row:1;min-width:0}.weather-card--4x2 .weather-card__condition{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.weather-card--4x2 .weather-card__stats{flex-direction:row;flex-wrap:wrap;align-content:flex-start;align-items:center;grid-column:1;grid-row:2;gap:.35rem .65rem;min-width:0;align-self:end}.weather-card--4x2 .weather-card__hourly{display:flex;min-height:0px;flex-direction:column;grid-column:2;grid-row:1 / span 2;overflow:hidden}.weather-card--4x2 .weather-card__section-title{margin:0 0 .35rem}.weather-card--4x2 .weather-card__hourly-row{display:flex;flex:1;align-items:stretch;justify-content:space-between;gap:.125rem;overflow:hidden}.weather-card--4x2 .weather-card__hour{display:flex;min-width:0px;flex:1;flex-direction:column;align-items:center;justify-content:center;gap:.2rem}.weather-card--4x2 .weather-card__hour-icon{width:1.5rem;height:1.5rem}.weather-card--4x2 .weather-card__hour-time,.weather-card--4x2 .weather-card__hour-temp{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.weather-card--4x2 .weather-card__daily{display:none}.weather-card--4x2 .weather-card__metrics{display:flex;height:100%;flex-direction:column;justify-content:space-evenly;grid-column:3;grid-row:1 / span 2;min-width:0;gap:.5rem}.weather-card--4x2 .weather-card__metric{grid-template-columns:auto minmax(0,1fr) auto;gap:.375rem}.weather-card--4x2 .weather-card__metric-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.weather-card--4x4{--weather-icon-size: 4rem;--weather-temp-size: 3.75rem;--weather-label-size: 1rem;--weather-meta-size: .875rem;--weather-caption-size: .75rem;padding:1.125rem 1.375rem}.weather-card--4x4 .weather-card__body{display:grid;align-items:flex-start;grid-template-columns:minmax(10rem,1fr) minmax(0,2.4fr) minmax(8rem,.9fr);grid-template-rows:auto auto;gap:1rem 1.5rem;margin-top:.625rem}.weather-card--4x4 .weather-card__primary{grid-column:1;grid-row:1}.weather-card--4x4 .weather-card__stats{flex-direction:column;align-items:flex-start;grid-column:1;grid-row:2;gap:.5rem}.weather-card--4x4 .weather-card__hourly{grid-column:2;grid-row:1;min-width:0;overflow:hidden}.weather-card--4x4 .weather-card__hourly-row{justify-content:space-between;overflow:hidden}.weather-card--4x4 .weather-card__hour{min-width:0px;flex:1}.weather-card--4x4 .weather-card__daily{grid-column:2;grid-row:2}.weather-card--4x4 .weather-card__metrics{justify-content:space-evenly;grid-column:3;grid-row:1 / span 2;gap:.875rem}.weather-card--4x4 .weather-card__hour-icon{width:calc(var(--weather-icon-size) * .5);height:calc(var(--weather-icon-size) * .5)}@property --tw-font-weight{syntax: \"*\"; inherits: false;}@property --tw-leading{syntax: \"*\"; inherits: false;}@layer properties{@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-font-weight: initial;--tw-leading: initial}}}\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n"] }]
508
+ }], ctorParameters: () => [] });
509
+
510
+ var weatherWidget_component = /*#__PURE__*/Object.freeze({
511
+ __proto__: null,
512
+ AXPWeatherWidgetViewComponent: AXPWeatherWidgetViewComponent
513
+ });
514
+
515
+ /**
516
+ * Responsive weather widget — adapts layout from compact to extra-large based on tile size.
517
+ */
518
+ const AXPWeatherWidget = {
519
+ name: RootConfig.widgets.weather,
520
+ title: '@dashboard:widgets.weather.widget-title',
521
+ description: '@dashboard:widgets.weather.widget-description',
522
+ categories: [AXP_WIDGETS_UTILITY_CATEGORY],
523
+ subCategory: AXP_DASHBOARD_SHARED_SUB_UTILITY_WEATHER,
524
+ groups: [AXPWidgetGroupEnum.DashboardWidget],
525
+ type: 'dashboard',
526
+ icon: 'fa-light fa-cloud-sun',
527
+ properties: [
528
+ {
529
+ name: 'city',
530
+ title: '@dashboard:widgets.weather.city',
531
+ group: AXP_DATA_PROPERTY_GROUP,
532
+ schema: {
533
+ defaultValue: 'New York',
534
+ dataType: 'string',
535
+ interface: {
536
+ name: 'city',
537
+ path: 'options.city',
538
+ type: AXPWidgetsCatalog.text,
539
+ },
540
+ },
541
+ visible: true,
542
+ },
543
+ {
544
+ name: 'showHumidity',
545
+ title: '@dashboard:widgets.weather.show-humidity',
546
+ group: AXP_APPEARANCE_PROPERTY_GROUP,
547
+ schema: {
548
+ defaultValue: true,
549
+ dataType: 'boolean',
550
+ interface: {
551
+ name: 'showHumidity',
552
+ path: 'options.showHumidity',
553
+ type: AXPWidgetsCatalog.toggle,
554
+ },
555
+ },
556
+ visible: true,
557
+ },
558
+ {
559
+ name: 'showWind',
560
+ title: '@dashboard:widgets.weather.show-wind',
561
+ group: AXP_APPEARANCE_PROPERTY_GROUP,
562
+ schema: {
563
+ defaultValue: true,
564
+ dataType: 'boolean',
565
+ interface: {
566
+ name: 'showWind',
567
+ path: 'options.showWind',
568
+ type: AXPWidgetsCatalog.toggle,
569
+ },
570
+ },
571
+ visible: true,
572
+ },
573
+ {
574
+ name: 'showForecast',
575
+ title: '@dashboard:widgets.weather.show-forecast',
576
+ group: AXP_APPEARANCE_PROPERTY_GROUP,
577
+ schema: {
578
+ defaultValue: true,
579
+ dataType: 'boolean',
580
+ interface: {
581
+ name: 'showForecast',
582
+ path: 'options.showForecast',
583
+ type: AXPWidgetsCatalog.toggle,
584
+ },
585
+ },
586
+ visible: true,
587
+ },
588
+ {
589
+ name: 'forecastDays',
590
+ title: '@dashboard:widgets.weather.forecast-days',
591
+ group: AXP_APPEARANCE_PROPERTY_GROUP,
592
+ schema: {
593
+ defaultValue: 5,
594
+ dataType: 'number',
595
+ interface: {
596
+ name: 'forecastDays',
597
+ path: 'options.forecastDays',
598
+ type: AXPWidgetsCatalog.number,
599
+ options: {
600
+ minValue: 1,
601
+ maxValue: 7,
602
+ step: 1,
603
+ },
604
+ },
605
+ },
606
+ visible: true,
607
+ },
608
+ {
609
+ name: 'forecastHours',
610
+ title: '@dashboard:widgets.weather.forecast-hours',
611
+ group: AXP_APPEARANCE_PROPERTY_GROUP,
612
+ schema: {
613
+ defaultValue: 6,
614
+ dataType: 'number',
615
+ interface: {
616
+ name: 'forecastHours',
617
+ path: 'options.forecastHours',
618
+ type: AXPWidgetsCatalog.number,
619
+ options: {
620
+ minValue: 1,
621
+ maxValue: 8,
622
+ step: 1,
623
+ },
624
+ },
625
+ },
626
+ visible: true,
627
+ },
628
+ {
629
+ name: 'temperatureUnit',
630
+ title: '@dashboard:widgets.weather.temperature-unit',
631
+ group: AXP_DATA_PROPERTY_GROUP,
632
+ schema: {
633
+ defaultValue: '°C',
634
+ dataType: 'string',
635
+ interface: {
636
+ name: 'temperatureUnit',
637
+ path: 'options.temperatureUnit',
638
+ type: AXPWidgetsCatalog.select,
639
+ options: {
640
+ dataSource: ['°C', '°F'],
641
+ },
642
+ },
643
+ },
644
+ visible: true,
645
+ },
646
+ {
647
+ name: 'windSpeedUnit',
648
+ title: '@dashboard:widgets.weather.wind-speed-unit',
649
+ group: AXP_DATA_PROPERTY_GROUP,
650
+ schema: {
651
+ defaultValue: 'km/h',
652
+ dataType: 'string',
653
+ interface: {
654
+ name: 'windSpeedUnit',
655
+ path: 'options.windSpeedUnit',
656
+ type: AXPWidgetsCatalog.select,
657
+ options: {
658
+ dataSource: ['km/h', 'mph', 'm/s'],
659
+ },
660
+ },
661
+ },
662
+ visible: true,
663
+ },
664
+ {
665
+ name: 'autoRefresh',
666
+ title: '@dashboard:widgets.weather.auto-refresh',
667
+ group: AXP_BEHAVIOR_PROPERTY_GROUP,
668
+ schema: {
669
+ defaultValue: true,
670
+ dataType: 'boolean',
671
+ interface: {
672
+ name: 'autoRefresh',
673
+ path: 'options.autoRefresh',
674
+ type: AXPWidgetsCatalog.toggle,
675
+ },
676
+ },
677
+ visible: true,
678
+ },
679
+ {
680
+ name: 'refreshInterval',
681
+ title: '@dashboard:widgets.weather.refresh-interval',
682
+ group: AXP_BEHAVIOR_PROPERTY_GROUP,
683
+ schema: {
684
+ defaultValue: 15,
685
+ dataType: 'number',
686
+ interface: {
687
+ name: 'refreshInterval',
688
+ path: 'options.refreshInterval',
689
+ type: AXPWidgetsCatalog.select,
690
+ options: {
691
+ dataSource: [5, 10, 15, 30, 60],
692
+ },
693
+ },
694
+ },
695
+ visible: true,
696
+ },
697
+ ],
698
+ components: {
699
+ view: {
700
+ component: () => Promise.resolve().then(function () { return weatherWidget_component; }).then((c) => c.AXPWeatherWidgetViewComponent),
701
+ },
702
+ },
703
+ meta: {
704
+ title: { visible: false },
705
+ dimensions: AXP_UTILITY_WEATHER_DIMENSIONS,
706
+ },
707
+ };
708
+
709
+ //#region ---- Weather widget definition (AI sidecar) ----
710
+ /**
711
+ * Build-time metadata for the full weather widget; mirrors {@link AXPWeatherWidget}.
712
+ */
713
+ const axmWeatherWidgetDefinition = defineWidget({
714
+ name: RootConfig.widgets.weather,
715
+ title: '@dashboard:widgets.weather.widget-title',
716
+ description: '@dashboard:widgets.weather.widget-description',
717
+ icon: 'fa-light fa-cloud-sun',
718
+ categories: [
719
+ {
720
+ name: 'utility',
721
+ order: 7,
722
+ title: '@dashboard-management:shared-widgets.widget-picker-category.utility',
723
+ },
724
+ ],
725
+ subCategory: {
726
+ name: 'dashboard-utility-weather',
727
+ order: 3,
728
+ title: '@dashboard-management:shared-widgets.widget-picker-sub-categories.utility-weather',
729
+ },
730
+ groups: [AXPWidgetGroupEnum.DashboardWidget],
731
+ type: 'dashboard',
732
+ inputs: [
733
+ { name: 'city', title: 'City / location', interface: { name: 'text-editor' } },
734
+ { name: 'showHumidity', title: 'Show humidity', interface: { name: 'toggle-editor' } },
735
+ { name: 'showWind', title: 'Show wind', interface: { name: 'toggle-editor' } },
736
+ { name: 'showForecast', title: 'Show forecast', interface: { name: 'toggle-editor' } },
737
+ { name: 'forecastDays', title: 'Forecast days', interface: { name: 'number-editor' } },
738
+ { name: 'forecastHours', title: 'Forecast hours', interface: { name: 'number-editor' } },
739
+ { name: 'temperatureUnit', title: 'Temperature unit', interface: { name: 'select-editor' } },
740
+ { name: 'windSpeedUnit', title: 'Wind speed unit', interface: { name: 'select-editor' } },
741
+ { name: 'autoRefresh', title: 'Auto refresh', interface: { name: 'toggle-editor' } },
742
+ { name: 'refreshInterval', title: 'Refresh interval (ms)', interface: { name: 'number-editor' } },
743
+ ],
744
+ outputs: [
745
+ {
746
+ name: 'boundValue',
747
+ title: 'Weather snapshot',
748
+ description: 'Resolved weather DTO from the weather API service for the configured city.',
749
+ interface: { name: 'json' },
750
+ },
751
+ ],
752
+ ai: {
753
+ tags: ['dashboard', 'utility', 'weather', 'forecast'],
754
+ shortDescription: '**Responsive weather** tile that adapts from compact to full forecast layout.',
755
+ usage: {
756
+ whenToUse: ['Operational dashboards that need ambient weather context.'],
757
+ avoidWhen: ['Business KPIs without a location — KPI widgets.'],
758
+ },
759
+ renderSurfaces: [{ mode: 'view', summary: 'Dashboard tile: weather service–backed current + forecast UI.' }],
760
+ instruction: 'City string must be geocodable by the configured weather provider; see weather-widget.config.ts.',
761
+ },
762
+ });
763
+ //#endregion
764
+
765
+ class AXEWeatherWidgetsProvider {
766
+ getWidgets() {
767
+ return [AXPWeatherWidget];
768
+ }
769
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEWeatherWidgetsProvider, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
770
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEWeatherWidgetsProvider }); }
771
+ }
772
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEWeatherWidgetsProvider, decorators: [{
773
+ type: Injectable
774
+ }] });
775
+
776
+ class AXEWeatherExtensionModule {
777
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEWeatherExtensionModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
778
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "22.0.8", ngImport: i0, type: AXEWeatherExtensionModule }); }
779
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEWeatherExtensionModule, providers: [
780
+ { provide: AXP_MODULE_MANIFEST_PROVIDER, useValue: WeatherExtensionManifest, multi: true },
781
+ { provide: AXP_WIDGET_DEFINITION_PROVIDER, useClass: AXEWeatherWidgetsProvider, multi: true },
782
+ { provide: AXP_SETTING_DEFINITION_PROVIDER, useClass: AXEWeatherSettingsProvider, multi: true },
783
+ { provide: AXP_INTEGRATION_DOMAIN_PROVIDER, useClass: AXEWeatherIntegrationDomainProvider, multi: true },
784
+ ] }); }
785
+ }
786
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXEWeatherExtensionModule, decorators: [{
787
+ type: NgModule,
788
+ args: [{
789
+ providers: [
790
+ { provide: AXP_MODULE_MANIFEST_PROVIDER, useValue: WeatherExtensionManifest, multi: true },
791
+ { provide: AXP_WIDGET_DEFINITION_PROVIDER, useClass: AXEWeatherWidgetsProvider, multi: true },
792
+ { provide: AXP_SETTING_DEFINITION_PROVIDER, useClass: AXEWeatherSettingsProvider, multi: true },
793
+ { provide: AXP_INTEGRATION_DOMAIN_PROVIDER, useClass: AXEWeatherIntegrationDomainProvider, multi: true },
794
+ ],
795
+ }]
796
+ }] });
797
+
798
+ /**
799
+ * Generated bundle index. Do not edit.
800
+ */
801
+
802
+ export { AXEWeatherExtensionModule, AXEWeatherWidgetsProvider };
803
+ //# sourceMappingURL=acorex-platform-extension-weather-core-client.mjs.map