@acorex/platform 19.3.0 → 19.3.1

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.
Files changed (67) hide show
  1. package/common/lib/utils/index.d.ts +0 -1
  2. package/fesm2022/acorex-platform-common.mjs +1 -134
  3. package/fesm2022/acorex-platform-common.mjs.map +1 -1
  4. package/fesm2022/acorex-platform-layout-components.mjs +247 -0
  5. package/fesm2022/acorex-platform-layout-components.mjs.map +1 -0
  6. package/fesm2022/acorex-platform-layout-designer.mjs.map +1 -1
  7. package/fesm2022/{acorex-platform-widgets-tabular-data-edit-popup.component-CybYV1Kf.mjs → acorex-platform-widgets-tabular-data-edit-popup.component-1IseEVXQ.mjs} +2 -2
  8. package/fesm2022/acorex-platform-widgets-tabular-data-edit-popup.component-1IseEVXQ.mjs.map +1 -0
  9. package/fesm2022/acorex-platform-widgets.mjs +8 -4395
  10. package/fesm2022/acorex-platform-widgets.mjs.map +1 -1
  11. package/layout/components/README.md +3 -0
  12. package/layout/components/index.d.ts +1 -0
  13. package/layout/components/lib/user-avatar/index.d.ts +4 -0
  14. package/layout/components/lib/user-avatar/user-avatar.component.d.ts +27 -0
  15. package/layout/components/lib/user-avatar/user-avatar.provider.d.ts +3 -0
  16. package/layout/components/lib/user-avatar/user-avatar.service.d.ts +42 -0
  17. package/layout/components/lib/user-avatar/user-avatar.types.d.ts +12 -0
  18. package/package.json +5 -1
  19. package/widgets/lib/widgets/index.d.ts +0 -11
  20. package/common/lib/utils/data-generator.d.ts +0 -26
  21. package/fesm2022/acorex-platform-widgets-tabular-data-edit-popup.component-CybYV1Kf.mjs.map +0 -1
  22. package/widgets/lib/widgets/charts/bar-chart/bar-chart-widget.component.d.ts +0 -72
  23. package/widgets/lib/widgets/charts/bar-chart/bar-chart-widget.config.d.ts +0 -7
  24. package/widgets/lib/widgets/charts/bar-chart/bar-chart.type.d.ts +0 -34
  25. package/widgets/lib/widgets/charts/bar-chart/index.d.ts +0 -2
  26. package/widgets/lib/widgets/charts/chart.type.d.ts +0 -3
  27. package/widgets/lib/widgets/charts/clock-calendar/clock-calendar-widget.component.d.ts +0 -40
  28. package/widgets/lib/widgets/charts/clock-calendar/clock-calendar-widget.config.d.ts +0 -7
  29. package/widgets/lib/widgets/charts/clock-calendar/clock-calendar.types.d.ts +0 -50
  30. package/widgets/lib/widgets/charts/clock-calendar/index.d.ts +0 -3
  31. package/widgets/lib/widgets/charts/donut-chart/donut-chart-widget.component.d.ts +0 -58
  32. package/widgets/lib/widgets/charts/donut-chart/donut-chart-widget.config.d.ts +0 -7
  33. package/widgets/lib/widgets/charts/donut-chart/donut-chart.type.d.ts +0 -67
  34. package/widgets/lib/widgets/charts/donut-chart/index.d.ts +0 -2
  35. package/widgets/lib/widgets/charts/gauge-chart/gauge-chart-widget.component.d.ts +0 -75
  36. package/widgets/lib/widgets/charts/gauge-chart/gauge-chart-widget.config.d.ts +0 -7
  37. package/widgets/lib/widgets/charts/gauge-chart/gauge-chart.type.d.ts +0 -29
  38. package/widgets/lib/widgets/charts/gauge-chart/index.d.ts +0 -3
  39. package/widgets/lib/widgets/charts/index.d.ts +0 -11
  40. package/widgets/lib/widgets/charts/line-chart/index.d.ts +0 -3
  41. package/widgets/lib/widgets/charts/line-chart/line-chart-widget.component.d.ts +0 -76
  42. package/widgets/lib/widgets/charts/line-chart/line-chart-widget.config.d.ts +0 -7
  43. package/widgets/lib/widgets/charts/line-chart/line-chart.type.d.ts +0 -41
  44. package/widgets/lib/widgets/charts/notification/index.d.ts +0 -3
  45. package/widgets/lib/widgets/charts/notification/notification-widget.component.d.ts +0 -54
  46. package/widgets/lib/widgets/charts/notification/notification-widget.config.d.ts +0 -10
  47. package/widgets/lib/widgets/charts/notification/notification.type.d.ts +0 -47
  48. package/widgets/lib/widgets/charts/shared/chart-base.component.d.ts +0 -44
  49. package/widgets/lib/widgets/charts/shared/chart-base.type.d.ts +0 -37
  50. package/widgets/lib/widgets/charts/shared/components/chart-tooltip/chart-tooltip.component.d.ts +0 -28
  51. package/widgets/lib/widgets/charts/shared/components/chart-tooltip/index.d.ts +0 -1
  52. package/widgets/lib/widgets/charts/shared/index.d.ts +0 -3
  53. package/widgets/lib/widgets/charts/sticky-note/index.d.ts +0 -2
  54. package/widgets/lib/widgets/charts/sticky-note/sticky-note-widget.component.d.ts +0 -21
  55. package/widgets/lib/widgets/charts/sticky-note/sticky-note-widget.config.d.ts +0 -7
  56. package/widgets/lib/widgets/charts/tasklist/index.d.ts +0 -3
  57. package/widgets/lib/widgets/charts/tasklist/tasklist-widget.component.d.ts +0 -34
  58. package/widgets/lib/widgets/charts/tasklist/tasklist-widget.config.d.ts +0 -7
  59. package/widgets/lib/widgets/charts/tasklist/tasklist.type.d.ts +0 -36
  60. package/widgets/lib/widgets/charts/weather/index.d.ts +0 -3
  61. package/widgets/lib/widgets/charts/weather/weather-services/index.d.ts +0 -3
  62. package/widgets/lib/widgets/charts/weather/weather-services/weather-api.abstract.d.ts +0 -174
  63. package/widgets/lib/widgets/charts/weather/weather-services/weather-api.key.d.ts +0 -2
  64. package/widgets/lib/widgets/charts/weather/weather-services/weather-api.mock.service.d.ts +0 -47
  65. package/widgets/lib/widgets/charts/weather/weather-services/weather-api.service.d.ts +0 -48
  66. package/widgets/lib/widgets/charts/weather/weather-widget.component.d.ts +0 -109
  67. package/widgets/lib/widgets/charts/weather/weather-widget.config.d.ts +0 -14
@@ -10,9 +10,9 @@ import { AXDecoratorModule } from '@acorex/components/decorators';
10
10
  import * as i2 from '@acorex/components/loading';
11
11
  import { AXLoadingModule } from '@acorex/components/loading';
12
12
  import * as i1$1 from '@angular/common';
13
- import { CommonModule, DatePipe } from '@angular/common';
13
+ import { CommonModule } from '@angular/common';
14
14
  import * as i0 from '@angular/core';
15
- import { computed, EventEmitter, ChangeDetectionStrategy, Component, inject, afterNextRender, HostBinding, signal, ViewEncapsulation, InjectionToken, effect, ViewChild, untracked, CUSTOM_ELEMENTS_SCHEMA, ChangeDetectorRef, viewChild, ElementRef, afterRender, NgZone, model, input, linkedSignal, Injector, runInInjectionContext, Directive, output, HostListener, Injectable, importProvidersFrom, NgModule } from '@angular/core';
15
+ import { computed, EventEmitter, ChangeDetectionStrategy, Component, inject, afterNextRender, HostBinding, signal, ViewEncapsulation, InjectionToken, effect, ViewChild, untracked, CUSTOM_ELEMENTS_SCHEMA, ChangeDetectorRef, viewChild, ElementRef, afterRender, NgZone, model, input, linkedSignal, HostListener, importProvidersFrom, NgModule } from '@angular/core';
16
16
  import * as i1 from '@acorex/components/check-box';
17
17
  import { AXCheckBoxModule } from '@acorex/components/check-box';
18
18
  import * as i4 from '@acorex/components/form';
@@ -31,7 +31,7 @@ import { AXTextBoxModule } from '@acorex/components/text-box';
31
31
  import * as i5$1 from '@acorex/core/translation';
32
32
  import { AXTranslationModule, AXTranslationService } from '@acorex/core/translation';
33
33
  import { AXBasePageComponent } from '@acorex/components/page';
34
- import { AXDateTimeFormatter, AXDateTimeModule, AXCalendarService } from '@acorex/core/date-time';
34
+ import { AXDateTimeFormatter, AXCalendarService } from '@acorex/core/date-time';
35
35
  import * as i3$2 from '@acorex/components/datetime-box';
36
36
  import { AXDateTimeBoxModule } from '@acorex/components/datetime-box';
37
37
  import * as i5$2 from '@acorex/components/text-area';
@@ -39,8 +39,7 @@ import { AXTextAreaModule } from '@acorex/components/text-area';
39
39
  import { set, isNumber, castArray, cloneDeep, isEqual, sum, get } from 'lodash-es';
40
40
  import * as i4$1 from '@acorex/components/tabs';
41
41
  import { AXTabsModule } from '@acorex/components/tabs';
42
- import * as i2$9 from '@acorex/core/format';
43
- import { AXFormatService, AXFormatModule } from '@acorex/core/format';
42
+ import { AXFormatService } from '@acorex/core/format';
44
43
  import * as i1$4 from '@acorex/components/number-box';
45
44
  import { AXNumberBoxModule } from '@acorex/components/number-box';
46
45
  import * as i3$3 from '@acorex/components/password-box';
@@ -55,7 +54,7 @@ import * as i5$3 from '@acorex/components/search-box';
55
54
  import { AXSearchBoxModule } from '@acorex/components/search-box';
56
55
  import * as i2$4 from '@acorex/components/selection-list';
57
56
  import { AXSelectionListModule } from '@acorex/components/selection-list';
58
- import { first, Subscription, interval, map, Observable, catchError, throwError, switchMap } from 'rxjs';
57
+ import { first, Subscription } from 'rxjs';
59
58
  import { AXFileService } from '@acorex/core/file';
60
59
  import * as i6 from '@acorex/components/dropdown';
61
60
  import { AXDropdownModule } from '@acorex/components/dropdown';
@@ -71,7 +70,7 @@ import { AXImageModule } from '@acorex/components/image';
71
70
  import * as i1$6 from '@acorex/components/map';
72
71
  import { AXMapModule } from '@acorex/components/map';
73
72
  import * as i1$7 from '@acorex/components/grid-layout-builder';
74
- import { AXGridLayoutContainerComponent, AXGridLayoutBuilderModule, AXGridLayoutWidgetComponent } from '@acorex/components/grid-layout-builder';
73
+ import { AXGridLayoutContainerComponent, AXGridLayoutBuilderModule } from '@acorex/components/grid-layout-builder';
75
74
  import { AXPDesignerService, AXPWidgetDesignerRendererDirective, AXPDesignerGridDrawerComponent, AXPDesignerAddWidgetMiniButtonComponent } from '@acorex/platform/layout/designer';
76
75
  import * as i1$8 from '@acorex/components/button-group';
77
76
  import { AXButtonGroupModule } from '@acorex/components/button-group';
@@ -81,10 +80,6 @@ import * as i2$8 from '@acorex/components/color-box';
81
80
  import { AXColorBoxModule } from '@acorex/components/color-box';
82
81
  import * as i1$9 from '@acorex/components/popover';
83
82
  import { AXPopoverComponent, AXPopoverModule } from '@acorex/components/popover';
84
- import { AXTagModule } from '@acorex/components/tag';
85
- import * as i5$4 from '@acorex/components/avatar';
86
- import { AXAvatarModule } from '@acorex/components/avatar';
87
- import { HttpClient, HttpClientModule } from '@angular/common/http';
88
83
  import * as i1$a from '@acorex/components/cron-job';
89
84
  import { AXCronJobModule } from '@acorex/components/cron-job';
90
85
  import * as i1$b from '@acorex/components/qrcode';
@@ -12391,4368 +12386,6 @@ const AXPRequiredValidationWidget = {
12391
12386
  },
12392
12387
  };
12393
12388
 
12394
- /**
12395
- * Common base types and utilities for all chart components
12396
- */
12397
- /**
12398
- * Color utility functions for charts
12399
- */
12400
- const AXPChartColors = {
12401
- // Modern color palette suitable for data visualization
12402
- defaultColors: [
12403
- '#4361ee', // Blue
12404
- '#3a0ca3', // Purple
12405
- '#7209b7', // Violet
12406
- '#f72585', // Pink
12407
- '#4cc9f0', // Light Blue
12408
- '#4895ef', // Sky Blue
12409
- '#560bad', // Deep Purple
12410
- '#f15bb5', // Light Pink
12411
- '#00bbf9', // Cyan
12412
- '#00f5d4', // Teal
12413
- ],
12414
- // Get a color from the palette by index with wraparound
12415
- getColor: (index, customPalette) => {
12416
- const palette = customPalette || AXPChartColors.defaultColors;
12417
- return palette[index % palette.length];
12418
- },
12419
- // Generate a continuous color palette from a base color
12420
- generatePalette: (baseColor, count) => {
12421
- // Simple implementation - in real usage you might want a more sophisticated algorithm
12422
- const colors = [];
12423
- for (let i = 0; i < count; i++) {
12424
- // This is simplistic - a real implementation would use HSL or other color manipulation
12425
- const opacity = 0.4 + (0.6 * i) / count;
12426
- colors.push(`${baseColor}${Math.floor(opacity * 255)
12427
- .toString(16)
12428
- .padStart(2, '0')}`);
12429
- }
12430
- return colors;
12431
- },
12432
- };
12433
- /**
12434
- * Shared utility for loading D3.js dynamically
12435
- */
12436
- async function loadD3() {
12437
- try {
12438
- // Dynamic import of d3 without relying on d3 being imported at the top
12439
- return await import('d3');
12440
- }
12441
- catch (error) {
12442
- console.error('Failed to load D3.js:', error);
12443
- throw error;
12444
- }
12445
- }
12446
-
12447
- /**
12448
- * Base component class for all chart components with common chart functionality
12449
- */
12450
- class AXPChartBaseComponent extends AXPValueWidgetComponent {
12451
- // Constructor with protected change detector
12452
- constructor(cdr) {
12453
- super();
12454
- this.cdr = cdr;
12455
- // Get injector for running effects
12456
- this.injector = inject(Injector);
12457
- // Track component lifecycle
12458
- this.isInitialized = signal(false);
12459
- this.isRendered = signal(false);
12460
- // Internal chart data storage with fallback logic
12461
- this._internalData = signal(null);
12462
- // Accessor for chart data with fallback to getValue() or defaultValue
12463
- this.chartData = computed(() => {
12464
- if (this._internalData()) {
12465
- return this._internalData();
12466
- }
12467
- return this.getValue() || this.defaultValue;
12468
- });
12469
- // Options tracker for detecting changes
12470
- this._lastOptionsSnapshot = '';
12471
- // Store the effect cleanup function
12472
- this.effectRef = null;
12473
- }
12474
- ngOnInit() {
12475
- super.ngOnInit();
12476
- this.loadD3();
12477
- }
12478
- ngAfterViewInit() {
12479
- this.isRendered.set(true);
12480
- this.setupEffects();
12481
- }
12482
- ngOnDestroy() {
12483
- // Clean up effect if it exists
12484
- if (this.effectRef) {
12485
- this.effectRef.destroy();
12486
- }
12487
- this.cleanupChart();
12488
- }
12489
- /**
12490
- * Load D3.js library asynchronously
12491
- */
12492
- async loadD3() {
12493
- try {
12494
- this.d3 = await loadD3();
12495
- this.isInitialized.set(true);
12496
- // Initialize chart data once D3 is loaded
12497
- const initialData = this.getValue() || this.defaultValue;
12498
- if (initialData) {
12499
- this._internalData.set(initialData);
12500
- }
12501
- // Create chart once D3 is loaded and if we're already rendered
12502
- if (this.isRendered()) {
12503
- this.createChart();
12504
- }
12505
- }
12506
- catch (error) {
12507
- console.error('Error loading D3.js:', error);
12508
- }
12509
- }
12510
- /**
12511
- * Set up reactive effects to track data and option changes
12512
- */
12513
- setupEffects() {
12514
- // Run effect in injection context to avoid NG0203 error
12515
- this.effectRef = runInInjectionContext(this.injector, () => {
12516
- return effect(() => {
12517
- // Only update if D3 is loaded and component is rendered
12518
- if (!this.isInitialized() || !this.isRendered())
12519
- return;
12520
- // Track dependencies explicitly
12521
- const data = this.getValue();
12522
- const options = this.options();
12523
- // Store current options snapshot for comparison
12524
- const currentOptions = JSON.stringify(options);
12525
- const optionsChanged = currentOptions !== this._lastOptionsSnapshot;
12526
- this._lastOptionsSnapshot = currentOptions;
12527
- // Check if data changed
12528
- const dataChanged = data && JSON.stringify(data) !== JSON.stringify(this._internalData());
12529
- // Update internal data if it changed
12530
- if (dataChanged && data) {
12531
- this._internalData.set(data);
12532
- }
12533
- // Update chart if either data or options changed
12534
- if (dataChanged || optionsChanged) {
12535
- this.updateChart();
12536
- }
12537
- });
12538
- });
12539
- }
12540
- /**
12541
- * Get dimensions of the container element
12542
- */
12543
- getContainerDimensions(containerElement) {
12544
- if (!containerElement?.nativeElement) {
12545
- return { width: 300, height: 300 }; // Default fallback
12546
- }
12547
- const { clientWidth, clientHeight } = containerElement.nativeElement;
12548
- return {
12549
- width: clientWidth || 300,
12550
- height: clientHeight || 300,
12551
- };
12552
- }
12553
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: AXPChartBaseComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive }); }
12554
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.0.3", type: AXPChartBaseComponent, isStandalone: true, usesInheritance: true, ngImport: i0 }); }
12555
- }
12556
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: AXPChartBaseComponent, decorators: [{
12557
- type: Directive
12558
- }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }] });
12559
-
12560
- class AXPChartTooltipComponent {
12561
- constructor() {
12562
- this.data = input(null);
12563
- this.position = input({ x: 0, y: 0 });
12564
- this.visible = input(false);
12565
- /**
12566
- * Whether to show the tooltip's percentage badge
12567
- */
12568
- this.showPercentage = input(true);
12569
- /**
12570
- * Optional custom styling for the tooltip
12571
- */
12572
- this.style = input({});
12573
- }
12574
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: AXPChartTooltipComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
12575
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.3", type: AXPChartTooltipComponent, isStandalone: true, selector: "ax-chart-tooltip", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, visible: { classPropertyName: "visible", publicName: "visible", isSignal: true, isRequired: false, transformFunction: null }, showPercentage: { classPropertyName: "showPercentage", publicName: "showPercentage", isSignal: true, isRequired: false, transformFunction: null }, style: { classPropertyName: "style", publicName: "style", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (visible() && data()) {\n<div class=\"chart-tooltip\" [style.left.px]=\"position().x\" [style.top.px]=\"position().y\" [ngStyle]=\"style()\">\n <div class=\"chart-tooltip-title\">{{ data()!.title }}</div>\n <div class=\"chart-tooltip-content\">\n @if (data()!.color) {\n <div class=\"chart-tooltip-color\" [style.background-color]=\"data()!.color\"></div>\n }\n <div class=\"chart-tooltip-value\">{{ data()!.value }}</div>\n @if (showPercentage() && data()!.percentage) {\n <div class=\"chart-tooltip-percentage\">{{ data()!.percentage }}</div>\n }\n </div>\n</div>\n}\n", styles: [".chart-tooltip{position:absolute;pointer-events:none;background-color:rgba(33,33,33,.9);color:#fff;padding:.5rem .75rem;border-radius:.375rem;font-size:.8rem;z-index:10;box-shadow:0 4px 12px rgba(0,0,0,.15);-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);border:1px solid rgba(255,255,255,.1);transform:translate(10px,-50%);max-width:200px;font-family:var(--ax-font-family, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif);transition:opacity .15s ease,transform .15s ease}.chart-tooltip-title{font-weight:600;padding-bottom:.5rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.chart-tooltip-content{display:flex;justify-content:space-between;align-items:center;gap:.5rem}.chart-tooltip-color{width:10px;height:10px;border-radius:2px;flex-shrink:0;box-shadow:0 1px 2px rgba(0,0,0,.2)}.chart-tooltip-value{font-weight:500;flex-grow:1}.chart-tooltip-percentage{background-color:rgba(255,255,255,.2);padding:.125rem .375rem;border-radius:1rem;font-size:.7rem;font-weight:500;flex-shrink:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
12576
- }
12577
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: AXPChartTooltipComponent, decorators: [{
12578
- type: Component,
12579
- args: [{ selector: 'ax-chart-tooltip', standalone: true, imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (visible() && data()) {\n<div class=\"chart-tooltip\" [style.left.px]=\"position().x\" [style.top.px]=\"position().y\" [ngStyle]=\"style()\">\n <div class=\"chart-tooltip-title\">{{ data()!.title }}</div>\n <div class=\"chart-tooltip-content\">\n @if (data()!.color) {\n <div class=\"chart-tooltip-color\" [style.background-color]=\"data()!.color\"></div>\n }\n <div class=\"chart-tooltip-value\">{{ data()!.value }}</div>\n @if (showPercentage() && data()!.percentage) {\n <div class=\"chart-tooltip-percentage\">{{ data()!.percentage }}</div>\n }\n </div>\n</div>\n}\n", styles: [".chart-tooltip{position:absolute;pointer-events:none;background-color:rgba(33,33,33,.9);color:#fff;padding:.5rem .75rem;border-radius:.375rem;font-size:.8rem;z-index:10;box-shadow:0 4px 12px rgba(0,0,0,.15);-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);border:1px solid rgba(255,255,255,.1);transform:translate(10px,-50%);max-width:200px;font-family:var(--ax-font-family, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif);transition:opacity .15s ease,transform .15s ease}.chart-tooltip-title{font-weight:600;padding-bottom:.5rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.chart-tooltip-content{display:flex;justify-content:space-between;align-items:center;gap:.5rem}.chart-tooltip-color{width:10px;height:10px;border-radius:2px;flex-shrink:0;box-shadow:0 1px 2px rgba(0,0,0,.2)}.chart-tooltip-value{font-weight:500;flex-grow:1}.chart-tooltip-percentage{background-color:rgba(255,255,255,.2);padding:.125rem .375rem;border-radius:1rem;font-size:.7rem;font-weight:500;flex-shrink:0}\n"] }]
12580
- }] });
12581
-
12582
- /**
12583
- * Bar Chart Widget Component
12584
- * Renders data as vertical bars with interactive hover effects and animations
12585
- */
12586
- class AXPBarChartWidgetViewComponent extends AXPChartBaseComponent {
12587
- constructor() {
12588
- super(...arguments);
12589
- this.barClick = output();
12590
- // Chart container reference
12591
- this.chartContainerEl = viewChild.required('chartContainer');
12592
- this.margin = { top: 20, right: 20, bottom: 30, left: 40 };
12593
- // Tooltip state
12594
- this._tooltipVisible = signal(false);
12595
- this._tooltipPosition = signal({ x: 0, y: 0 });
12596
- this._tooltipData = signal({
12597
- title: '',
12598
- value: '0',
12599
- percentage: '0%',
12600
- color: '',
12601
- });
12602
- // Tooltip accessors
12603
- this.tooltipVisible = computed(() => this._tooltipVisible());
12604
- this.tooltipPosition = computed(() => this._tooltipPosition());
12605
- this.tooltipData = computed(() => this._tooltipData());
12606
- // Bar appearance options
12607
- this.barWidth = computed(() => this.options()['barWidth'] ?? 80);
12608
- this.cornerRadius = computed(() => this.options()['cornerRadius'] ?? 4);
12609
- }
12610
- // Chart lifecycle methods
12611
- /**
12612
- * Creates the bar chart SVG and renders all elements
12613
- */
12614
- createChart() {
12615
- if (!this.d3 || !this.chartContainerEl()?.nativeElement)
12616
- return;
12617
- const containerElement = this.chartContainerEl().nativeElement;
12618
- const data = this.chartData() || [];
12619
- // Clear existing chart
12620
- this.d3.select(containerElement).selectAll('svg').remove();
12621
- // Early return if no data
12622
- if (!data.length) {
12623
- this.showNoDataMessage(containerElement);
12624
- return;
12625
- }
12626
- // Get options and setup dimensions
12627
- const options = this.options();
12628
- this.setupDimensions(containerElement, options);
12629
- // No need to create SVG here as it's now done in setupDimensions
12630
- // Create scales and axes
12631
- this.setupScales(data);
12632
- this.createAxes(options);
12633
- // Render the bars
12634
- this.renderBars(data);
12635
- }
12636
- updateChart() {
12637
- this.createChart();
12638
- }
12639
- cleanupChart() {
12640
- if (this.svg) {
12641
- this.d3.select(this.chartContainerEl()?.nativeElement).selectAll('svg').remove();
12642
- this.svg = null;
12643
- this.chart = null;
12644
- }
12645
- this._tooltipVisible.set(false);
12646
- }
12647
- // Private chart creation methods
12648
- /**
12649
- * Sets up chart dimensions and creates SVG with responsive attributes
12650
- */
12651
- setupDimensions(containerElement, options) {
12652
- // Get container dimensions
12653
- const containerWidth = containerElement.clientWidth;
12654
- const containerHeight = containerElement.clientHeight;
12655
- // If options specify width and height, use those, otherwise default to container size
12656
- const minDim = Math.min(200, containerWidth, containerHeight); // Ensure reasonable minimum
12657
- if (options.width && options.height) {
12658
- // Explicit dimensions provided
12659
- this.width = options.width - this.margin.left - this.margin.right;
12660
- this.height = options.height - this.margin.top - this.margin.bottom;
12661
- }
12662
- else {
12663
- // Responsive dimensions
12664
- this.width = Math.max(containerWidth, minDim) - this.margin.left - this.margin.right;
12665
- this.height = Math.max(containerHeight, minDim) - this.margin.top - this.margin.bottom;
12666
- }
12667
- // Create responsive SVG that scales with its container
12668
- const svg = this.d3
12669
- .select(containerElement)
12670
- .append('svg')
12671
- .attr('width', '100%')
12672
- .attr('height', '100%')
12673
- .attr('viewBox', `0 0 ${this.width + this.margin.left + this.margin.right} ${this.height + this.margin.top + this.margin.bottom}`)
12674
- .attr('preserveAspectRatio', 'xMidYMid meet');
12675
- this.svg = svg;
12676
- // Create chart group with margins
12677
- this.chart = this.svg.append('g').attr('transform', `translate(${this.margin.left},${this.margin.top})`);
12678
- }
12679
- /**
12680
- * Creates x and y scales for the chart
12681
- */
12682
- setupScales(data) {
12683
- // Get the bar width percentage (default 80%)
12684
- const barWidthPercent = this.barWidth() / 100;
12685
- // Calculate padding based on barWidth (inverse relationship)
12686
- const padding = Math.max(0.1, 1 - barWidthPercent);
12687
- // Create x scale (band scale for categorical data)
12688
- this.xScale = this.d3
12689
- .scaleBand()
12690
- .domain(data.map((d) => d.label))
12691
- .range([0, this.width])
12692
- .padding(padding);
12693
- // Create y scale (linear scale for values)
12694
- this.yScale = this.d3
12695
- .scaleLinear()
12696
- .domain([0, this.d3.max(data, (d) => d.value) || 0])
12697
- .nice()
12698
- .range([this.height, 0]);
12699
- }
12700
- /**
12701
- * Creates x and y axes with grid lines
12702
- */
12703
- createAxes(options) {
12704
- // Only create axes if they are enabled in options
12705
- const showXAxis = options.showXAxis !== false;
12706
- const showYAxis = options.showYAxis !== false;
12707
- const showGrid = options.showGrid !== false;
12708
- if (showXAxis) {
12709
- // Create X axis
12710
- this.xAxis = this.chart
12711
- .append('g')
12712
- .attr('class', 'axp-bar-chart-axis-x')
12713
- .attr('transform', `translate(0,${this.height})`)
12714
- .call(this.d3.axisBottom(this.xScale));
12715
- }
12716
- if (showYAxis) {
12717
- // Create Y axis
12718
- this.yAxis = this.chart.append('g').attr('class', 'axp-bar-chart-axis-y').call(this.d3.axisLeft(this.yScale));
12719
- }
12720
- if (showGrid) {
12721
- // Add horizontal grid lines
12722
- this.chart
12723
- .append('g')
12724
- .attr('class', 'axp-bar-chart-grid')
12725
- .call(this.d3
12726
- .axisLeft(this.yScale)
12727
- .tickSize(-this.width)
12728
- .tickFormat(() => ''))
12729
- .selectAll('.tick')
12730
- .style('color', 'rgb(153 153 153 / 30%)'); // Add gray color to ticks
12731
- }
12732
- }
12733
- /**
12734
- * Renders the bars with animations
12735
- */
12736
- renderBars(data) {
12737
- // Get corner radius from options
12738
- const radius = this.cornerRadius();
12739
- // Add bars with modern colors
12740
- const bars = this.chart
12741
- .selectAll('.axp-bar-chart-bar')
12742
- .data(data)
12743
- .enter()
12744
- .append('rect')
12745
- .attr('class', 'axp-bar-chart-bar')
12746
- .attr('x', (d) => this.xScale(d.label))
12747
- .attr('width', this.xScale.bandwidth())
12748
- .attr('y', this.height) // Start from bottom for animation
12749
- .attr('height', 0) // Start with height 0 for animation
12750
- .attr('rx', radius) // Rounded corners
12751
- .attr('ry', radius) // Rounded corners
12752
- .attr('fill', (d, i) => d.color || AXPChartColors.getColor(i))
12753
- .on('mouseenter', (event, d) => this.handleBarHover(event, d))
12754
- .on('mousemove', (event) => this.updateTooltipPosition(event))
12755
- .on('mouseleave', () => this._tooltipVisible.set(false))
12756
- .on('click', (event, d) => this.handleBarClick(event, d));
12757
- // Add animation
12758
- bars
12759
- .transition()
12760
- .duration(800)
12761
- .delay((d, i) => i * 50) // Stagger each bar animation
12762
- .attr('y', (d) => this.yScale(d.value))
12763
- .attr('height', (d) => this.height - this.yScale(d.value))
12764
- .ease(this.d3.easeQuadOut); // Simple quadratic ease-out animation
12765
- }
12766
- // Event handlers
12767
- /**
12768
- * Handles bar hover event and shows tooltip
12769
- */
12770
- handleBarHover(event, datum) {
12771
- if (this.options().showTooltip !== false) {
12772
- const index = this.chartData().findIndex((item) => item.id === datum.id);
12773
- const color = datum.color || AXPChartColors.getColor(index);
12774
- // Calculate percentage of total
12775
- const total = this.chartData().reduce((sum, item) => sum + item.value, 0);
12776
- const percentage = total > 0 ? ((datum.value / total) * 100).toFixed(1) : '0';
12777
- this._tooltipData.set({
12778
- title: datum.label,
12779
- value: datum.value.toString(),
12780
- percentage: `${percentage}%`,
12781
- color: color,
12782
- });
12783
- this.updateTooltipPosition(event);
12784
- this._tooltipVisible.set(true);
12785
- }
12786
- }
12787
- /**
12788
- * Updates tooltip position based on mouse coordinates
12789
- */
12790
- updateTooltipPosition(event) {
12791
- const container = this.chartContainerEl().nativeElement.getBoundingClientRect();
12792
- const x = event.clientX - container.left;
12793
- const y = event.clientY - container.top;
12794
- this._tooltipPosition.set({
12795
- x: x,
12796
- y: y,
12797
- });
12798
- }
12799
- /**
12800
- * Handles bar click event
12801
- */
12802
- handleBarClick(event, datum) {
12803
- this.barClick.emit(datum);
12804
- }
12805
- /**
12806
- * Shows a message when no data is available
12807
- */
12808
- showNoDataMessage(containerElement) {
12809
- const messageContainer = this.d3
12810
- .select(containerElement)
12811
- .append('div')
12812
- .attr('class', 'axp-bar-chart-no-data-message')
12813
- .style('width', '100%')
12814
- .style('height', '100%')
12815
- .style('display', 'flex')
12816
- .style('flex-direction', 'column')
12817
- .style('align-items', 'center')
12818
- .style('justify-content', 'center')
12819
- .style('text-align', 'center');
12820
- // Add an icon
12821
- messageContainer
12822
- .append('div')
12823
- .attr('class', 'axp-bar-chart-no-data-icon')
12824
- .style('margin-bottom', '10px')
12825
- .style('color', 'var(--ax-text-muted, #999)')
12826
- .html('<i class="fa-light fa-chart-bar fa-2x"></i>');
12827
- // Add text message
12828
- messageContainer
12829
- .append('div')
12830
- .attr('class', 'axp-bar-chart-no-data-text')
12831
- .style('font-size', '16px')
12832
- .style('font-weight', '600')
12833
- .style('color', 'var(--ax-text-color, #333)')
12834
- .text('No data available');
12835
- }
12836
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: AXPBarChartWidgetViewComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
12837
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "19.0.3", type: AXPBarChartWidgetViewComponent, isStandalone: true, selector: "ng-component", outputs: { barClick: "barClick" }, viewQueries: [{ propertyName: "chartContainerEl", first: true, predicate: ["chartContainer"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"axp-bar-chart\" #chartContainer>\n <!-- Shared tooltip component -->\n <ax-chart-tooltip\n [visible]=\"tooltipVisible()\"\n [position]=\"tooltipPosition()\"\n [data]=\"tooltipData()\"\n [showPercentage]=\"true\"\n ></ax-chart-tooltip>\n</div>\n", styles: [":host{display:block;width:100%;height:100%;min-height:200px}.axp-bar-chart{width:100%;height:100%;position:relative;display:flex;align-items:center;justify-content:center;border-radius:.5rem;overflow:hidden}.axp-bar-chart svg{width:100%;height:100%;max-width:100%;max-height:100%;overflow:visible}.axp-bar-chart-bar{transition:all .3s cubic-bezier(.4,0,.2,1);cursor:pointer}.axp-bar-chart-bar:hover{filter:brightness(.9);transform:translateY(-3px)}.axp-bar-chart-axis-x path,.axp-bar-chart-axis-y path{stroke:var(--ax-border-color, #e0e0e0)}.axp-bar-chart-axis-x line,.axp-bar-chart-axis-y line,.axp-bar-chart-grid line{stroke:var(--ax-border-color, #e0e0e0);stroke-dasharray:2,2;stroke-opacity:.5}.axp-bar-chart-grid path{stroke-width:0}.axp-bar-chart-axis-x text,.axp-bar-chart-axis-y text{fill:var(--ax-text-muted, #666);font-size:clamp(8px,2vmin,12px)}.axp-bar-chart-no-data-message{font-family:var(--ax-font-family, system-ui, sans-serif);display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:1rem;width:100%;height:100%}.axp-bar-chart-no-data-icon{margin-bottom:.75rem;color:var(--ax-text-muted, #999)}.axp-bar-chart-no-data-text{font-weight:600;color:var(--ax-text-color, #333)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: AXPChartTooltipComponent, selector: "ax-chart-tooltip", inputs: ["data", "position", "visible", "showPercentage", "style"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
12838
- }
12839
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: AXPBarChartWidgetViewComponent, decorators: [{
12840
- type: Component,
12841
- args: [{ standalone: true, imports: [CommonModule, AXPChartTooltipComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"axp-bar-chart\" #chartContainer>\n <!-- Shared tooltip component -->\n <ax-chart-tooltip\n [visible]=\"tooltipVisible()\"\n [position]=\"tooltipPosition()\"\n [data]=\"tooltipData()\"\n [showPercentage]=\"true\"\n ></ax-chart-tooltip>\n</div>\n", styles: [":host{display:block;width:100%;height:100%;min-height:200px}.axp-bar-chart{width:100%;height:100%;position:relative;display:flex;align-items:center;justify-content:center;border-radius:.5rem;overflow:hidden}.axp-bar-chart svg{width:100%;height:100%;max-width:100%;max-height:100%;overflow:visible}.axp-bar-chart-bar{transition:all .3s cubic-bezier(.4,0,.2,1);cursor:pointer}.axp-bar-chart-bar:hover{filter:brightness(.9);transform:translateY(-3px)}.axp-bar-chart-axis-x path,.axp-bar-chart-axis-y path{stroke:var(--ax-border-color, #e0e0e0)}.axp-bar-chart-axis-x line,.axp-bar-chart-axis-y line,.axp-bar-chart-grid line{stroke:var(--ax-border-color, #e0e0e0);stroke-dasharray:2,2;stroke-opacity:.5}.axp-bar-chart-grid path{stroke-width:0}.axp-bar-chart-axis-x text,.axp-bar-chart-axis-y text{fill:var(--ax-text-muted, #666);font-size:clamp(8px,2vmin,12px)}.axp-bar-chart-no-data-message{font-family:var(--ax-font-family, system-ui, sans-serif);display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:1rem;width:100%;height:100%}.axp-bar-chart-no-data-icon{margin-bottom:.75rem;color:var(--ax-text-muted, #999)}.axp-bar-chart-no-data-text{font-weight:600;color:var(--ax-text-color, #333)}\n"] }]
12842
- }] });
12843
-
12844
- var barChartWidget_component = /*#__PURE__*/Object.freeze({
12845
- __proto__: null,
12846
- AXPBarChartWidgetViewComponent: AXPBarChartWidgetViewComponent
12847
- });
12848
-
12849
- const AXP_WIDGETS_CHART_CATEGORY = {
12850
- name: 'chart',
12851
- order: 6,
12852
- title: 'Charts',
12853
- };
12854
- const AXP_WIDGETS_UTILITY_CATEGORY = {
12855
- name: 'utility',
12856
- order: 7,
12857
- title: 'Utilities',
12858
- };
12859
-
12860
- const AXPBarChartWidget = {
12861
- name: 'bar-chart',
12862
- title: 'Bar Chart Widget',
12863
- categories: [AXP_WIDGETS_CHART_CATEGORY],
12864
- groups: [AXPWidgetGroupEnum.DashboardWidget],
12865
- type: 'dashboard',
12866
- icon: 'fa-light fa-chart-bar',
12867
- properties: [
12868
- // ====== Layout & Dimensions ======
12869
- {
12870
- name: 'height',
12871
- title: 'Height',
12872
- group: AXP_STYLING_PROPERTY_GROUP,
12873
- schema: {
12874
- defaultValue: 300,
12875
- dataType: 'number',
12876
- interface: {
12877
- name: 'height',
12878
- path: 'options.height',
12879
- type: AXPWidgetsCatalog.number,
12880
- options: {
12881
- minValue: 0,
12882
- maxValue: 800,
12883
- },
12884
- },
12885
- },
12886
- visible: true,
12887
- },
12888
- {
12889
- name: 'width',
12890
- title: 'Width',
12891
- group: AXP_STYLING_PROPERTY_GROUP,
12892
- schema: {
12893
- defaultValue: null,
12894
- dataType: 'number',
12895
- interface: {
12896
- name: 'width',
12897
- path: 'options.width',
12898
- type: AXPWidgetsCatalog.number,
12899
- options: {
12900
- minValue: 0,
12901
- maxValue: 1200,
12902
- },
12903
- },
12904
- },
12905
- visible: true,
12906
- },
12907
- // ====== Axis Settings ======
12908
- {
12909
- name: 'showXAxis',
12910
- title: 'Show X Axis',
12911
- group: AXP_APPEARANCE_PROPERTY_GROUP,
12912
- schema: {
12913
- defaultValue: true,
12914
- dataType: 'boolean',
12915
- interface: {
12916
- name: 'showXAxis',
12917
- path: 'options.showXAxis',
12918
- type: AXPWidgetsCatalog.toggle,
12919
- },
12920
- },
12921
- visible: true,
12922
- },
12923
- {
12924
- name: 'showYAxis',
12925
- title: 'Show Y Axis',
12926
- group: AXP_APPEARANCE_PROPERTY_GROUP,
12927
- schema: {
12928
- defaultValue: true,
12929
- dataType: 'boolean',
12930
- interface: {
12931
- name: 'showYAxis',
12932
- path: 'options.showYAxis',
12933
- type: AXPWidgetsCatalog.toggle,
12934
- },
12935
- },
12936
- visible: true,
12937
- },
12938
- {
12939
- name: 'showGrid',
12940
- title: 'Show Grid Lines',
12941
- group: AXP_APPEARANCE_PROPERTY_GROUP,
12942
- schema: {
12943
- defaultValue: true,
12944
- dataType: 'boolean',
12945
- interface: {
12946
- name: 'showGrid',
12947
- path: 'options.showGrid',
12948
- type: AXPWidgetsCatalog.toggle,
12949
- },
12950
- },
12951
- visible: true,
12952
- },
12953
- // ====== Tooltip Settings ======
12954
- {
12955
- name: 'showTooltip',
12956
- title: 'Show Tooltip',
12957
- group: AXP_APPEARANCE_PROPERTY_GROUP,
12958
- schema: {
12959
- defaultValue: true,
12960
- dataType: 'boolean',
12961
- interface: {
12962
- name: 'showTooltip',
12963
- path: 'options.showTooltip',
12964
- type: AXPWidgetsCatalog.toggle,
12965
- },
12966
- },
12967
- visible: true,
12968
- },
12969
- // ====== Bar Appearance ======
12970
- {
12971
- name: 'barWidth',
12972
- title: 'Bar Width',
12973
- group: AXP_APPEARANCE_PROPERTY_GROUP,
12974
- schema: {
12975
- defaultValue: 80,
12976
- dataType: 'number',
12977
- interface: {
12978
- name: 'barWidth',
12979
- path: 'options.barWidth',
12980
- type: AXPWidgetsCatalog.number,
12981
- options: {
12982
- placeholder: '1-100',
12983
- minValue: 1,
12984
- maxValue: 100,
12985
- },
12986
- },
12987
- },
12988
- visible: true,
12989
- },
12990
- {
12991
- name: 'cornerRadius',
12992
- title: 'Corner Radius',
12993
- group: AXP_APPEARANCE_PROPERTY_GROUP,
12994
- schema: {
12995
- defaultValue: 4,
12996
- dataType: 'number',
12997
- interface: {
12998
- name: 'cornerRadius',
12999
- path: 'options.cornerRadius',
13000
- type: AXPWidgetsCatalog.number,
13001
- options: {
13002
- placeholder: '0-20',
13003
- minValue: 0,
13004
- maxValue: 20,
13005
- },
13006
- },
13007
- },
13008
- visible: true,
13009
- },
13010
- ],
13011
- components: {
13012
- view: {
13013
- component: () => Promise.resolve().then(function () { return barChartWidget_component; }).then((c) => c.AXPBarChartWidgetViewComponent),
13014
- },
13015
- },
13016
- meta: {
13017
- dimensions: {
13018
- width: 5,
13019
- height: 6,
13020
- minWidth: 2,
13021
- minHeight: 2,
13022
- maxWidth: 6,
13023
- maxHeight: 7,
13024
- },
13025
- },
13026
- };
13027
-
13028
- class AXPClockCalendarWidgetViewComponent extends AXPValueWidgetComponent {
13029
- constructor() {
13030
- super(...arguments);
13031
- // Dependencies
13032
- this.cdr = inject(ChangeDetectorRef);
13033
- // Time state
13034
- this.currentTime = new Date();
13035
- this.currentDate = new Date();
13036
- this.clockSubscription = null;
13037
- // Static clock elements
13038
- this.clockHours = Array.from({ length: 12 }, (_, i) => i + 1);
13039
- this.clockHourNumbers = Array.from({ length: 12 }, (_, i) => ({
13040
- number: i === 0 ? 12 : i,
13041
- angle: i * 30,
13042
- }));
13043
- // Clock hands rotation angles
13044
- this.hourRotation = 0;
13045
- this.minuteRotation = 0;
13046
- this.secondRotation = 0;
13047
- // Options with computed properties and defaults
13048
- this.displayLayout = computed(() => this.options()?.displayLayout?.id ?? 'both');
13049
- this.showDigitalClock = computed(() => {
13050
- const layout = this.displayLayout();
13051
- return layout === 'both' || layout === 'digital';
13052
- });
13053
- this.showAnalogClock = computed(() => {
13054
- const layout = this.displayLayout();
13055
- return layout === 'both' || layout === 'analog';
13056
- });
13057
- this.showDate = computed(() => this.options()?.showDate !== false);
13058
- this.showDayOfWeek = computed(() => this.options()?.showDayOfWeek !== false);
13059
- this.use24Hour = computed(() => this.options()?.use24Hour === true);
13060
- this.showSeconds = computed(() => this.options()?.showSeconds !== false);
13061
- this.dateFormat = computed(() => this.options()?.dateFormat?.id ?? 'dd MMM yyyy');
13062
- this.timezone = computed(() => this.options()?.timezone?.id ?? 'local');
13063
- this.showTimezoneIndicator = computed(() => this.timezone() !== 'local');
13064
- this.displayTimezone = computed(() => {
13065
- const tz = this.timezone();
13066
- if (tz === 'local')
13067
- return '';
13068
- if (tz.startsWith('UTC')) {
13069
- const [match, sign, hours, minutes] = tz.match(/UTC([+-])(\d+):?(\d+)?/) ?? [];
13070
- if (match) {
13071
- if (!minutes || minutes === '00') {
13072
- return `UTC${sign}${parseInt(hours)}`;
13073
- }
13074
- return `UTC${sign}${parseInt(hours)}:${minutes}`;
13075
- }
13076
- }
13077
- return tz;
13078
- });
13079
- this.timeFormat = computed(() => this.showSeconds() ? (this.use24Hour() ? 'HH:mm:ss' : 'hh:mm:ss a') : this.use24Hour() ? 'HH:mm' : 'hh:mm a');
13080
- }
13081
- ngOnInit() {
13082
- super.ngOnInit();
13083
- this.startClockTimer();
13084
- this.updateTime();
13085
- }
13086
- ngOnDestroy() {
13087
- this.stopClockTimer();
13088
- }
13089
- startClockTimer() {
13090
- this.stopClockTimer();
13091
- this.clockSubscription = interval(1000).subscribe(() => this.updateTime());
13092
- }
13093
- stopClockTimer() {
13094
- this.clockSubscription?.unsubscribe();
13095
- this.clockSubscription = null;
13096
- }
13097
- updateTime() {
13098
- const now = this.getAdjustedTime();
13099
- this.currentTime = now;
13100
- this.currentDate = now;
13101
- this.updateClockHandsRotation(now);
13102
- this.cdr.markForCheck();
13103
- }
13104
- getAdjustedTime() {
13105
- const now = new Date();
13106
- const tz = this.timezone();
13107
- if (tz === 'local' || !tz.startsWith('UTC')) {
13108
- return now;
13109
- }
13110
- const [match, sign, hours, minutes] = tz.match(/UTC([+-])(\d+):?(\d+)?/) ?? [];
13111
- if (!match) {
13112
- return now;
13113
- }
13114
- const offsetHours = parseInt(hours) * (sign === '+' ? 1 : -1);
13115
- const offsetMinutes = minutes ? parseInt(minutes) * (sign === '+' ? 1 : -1) : 0;
13116
- const utcTime = now.getTime() + now.getTimezoneOffset() * 60000;
13117
- return new Date(utcTime + (offsetHours * 3600000 + offsetMinutes * 60000));
13118
- }
13119
- updateClockHandsRotation(now) {
13120
- const hours = now.getHours() % 12;
13121
- const minutes = now.getMinutes();
13122
- const seconds = now.getSeconds();
13123
- // For analog clocks:
13124
- // - 12 o'clock is 0°
13125
- // - 3 o'clock is 90°
13126
- // - 6 o'clock is 180°
13127
- // - 9 o'clock is 270°
13128
- // Hour hand: 30° per hour (360°/12) plus gradual movement from minutes
13129
- this.hourRotation = hours * 30 + minutes / 2 + 180;
13130
- // Minute hand: 6° per minute (360°/60)
13131
- this.minuteRotation = minutes * 6 + 180;
13132
- // Second hand: 6° per second (360°/60)
13133
- this.secondRotation = seconds * 6 + 180;
13134
- }
13135
- initializeWidget() {
13136
- this.updateTime();
13137
- }
13138
- updateWidget() {
13139
- this.stopClockTimer();
13140
- this.startClockTimer();
13141
- this.updateTime();
13142
- }
13143
- destroyWidget() {
13144
- this.stopClockTimer();
13145
- }
13146
- getDayOfWeek() {
13147
- const days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
13148
- return days[this.currentDate.getDay()];
13149
- }
13150
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: AXPClockCalendarWidgetViewComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
13151
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.3", type: AXPClockCalendarWidgetViewComponent, isStandalone: true, selector: "ng-component", usesInheritance: true, ngImport: i0, template: "<div class=\"axp-clock-calendar-container\">\n <!-- Timezone indicator (only shown when not local) -->\n @if (showTimezoneIndicator()) {\n <div class=\"axp-clock-calendar-timezone-badge\"><i class=\"fa-solid fa-globe\"></i> {{ displayTimezone() }}</div>\n }\n\n <!-- Day of week display -->\n @if (showDayOfWeek()) {\n <div class=\"axp-clock-calendar-day-label\">{{ getDayOfWeek() }}</div>\n }\n\n <div class=\"axp-clock-calendar-content\">\n <!-- Digital Clock Display -->\n @if (showDigitalClock()) {\n <div class=\"axp-clock-calendar-digital-clock\">\n {{ currentTime | format : 'datetime' : timeFormat() | async }}\n </div>\n }\n\n <!-- Analog Clock Display -->\n @if (showAnalogClock()) {\n <div class=\"axp-clock-calendar-analog-clock\">\n <!-- Hour markers -->\n @for (hour of clockHours; track hour) {\n <div\n class=\"axp-clock-calendar-hour-marker\"\n [style.transform]=\"'rotate(' + hour * 30 + 'deg) translateY(-80px)'\"\n ></div>\n }\n\n <!-- Clock Numbers -->\n <div class=\"axp-clock-calendar-numbers-container\">\n @for (hour of clockHourNumbers; track hour) {\n <div\n class=\"axp-clock-calendar-hour-number\"\n [style.transform]=\"'rotate(' + hour.angle + 'deg) translateY(-82px)'\"\n >\n <span [style.transform]=\"'rotate(' + -hour.angle + 'deg)'\">{{ hour.number }}</span>\n </div>\n }\n </div>\n\n <!-- Clock Hands -->\n <div class=\"axp-clock-calendar-hands-container\">\n <div class=\"axp-clock-calendar-hour-hand\" [style.transform]=\"'rotate(' + hourRotation + 'deg)'\"></div>\n <div class=\"axp-clock-calendar-minute-hand\" [style.transform]=\"'rotate(' + minuteRotation + 'deg)'\"></div>\n @if (showSeconds()) {\n <div class=\"axp-clock-calendar-second-hand\" [style.transform]=\"'rotate(' + secondRotation + 'deg)'\"></div>\n }\n <div class=\"axp-clock-calendar-center-dot\"></div>\n </div>\n </div>\n }\n\n <!-- Date Display -->\n @if (showDate()) {\n <div class=\"axp-clock-calendar-date-display\">\n <i class=\"fa-regular fa-calendar\"></i>\n {{ currentDate | format : 'datetime' : dateFormat() | async }}\n </div>\n }\n </div>\n</div>\n", styles: [":host{display:block;width:100%;height:100%}.axp-clock-calendar-container{display:flex;flex-direction:column;align-items:center;justify-content:center;width:100%;height:100%;padding:1rem;position:relative;overflow:hidden;box-shadow:var(--ax-shadow-sm);background-color:var(--ax-surface-color);color:var(--ax-text-color)}.axp-clock-calendar-content{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.75rem;width:100%;height:100%;position:relative}.axp-clock-calendar-timezone-badge{position:absolute;top:.5rem;right:.5rem;font-size:.75rem;padding:.25rem;border-radius:1rem;border-width:1px;border-color:rgba(23,23,23,.5)}.axp-clock-calendar-timezone-badge:is(.ax-dark *){border-color:rgba(245,245,245,.5)}.axp-clock-calendar-timezone-badge{display:flex;align-items:center;gap:.25rem}.axp-clock-calendar-timezone-badge i{font-size:.75rem}.axp-clock-calendar-day-label{font-size:.9rem;font-weight:600;text-transform:uppercase;letter-spacing:.05rem;margin-bottom:.25rem}.axp-clock-calendar-digital-clock{font-size:1.5rem;font-weight:500;letter-spacing:.05rem;font-family:monospace;padding:.5rem .75rem;border-radius:.25rem}.axp-clock-calendar-analog-clock{position:relative;border-radius:50%;border-width:1px;border-color:rgba(23,23,23,.5)}.axp-clock-calendar-analog-clock:is(.ax-dark *){border-color:rgba(245,245,245,.5)}.axp-clock-calendar-analog-clock{display:flex;align-items:center;justify-content:center;background:var(--ax-surface-color);width:min(180px,100%);height:0;padding-bottom:min(180px,100%);margin:.5rem auto;min-width:120px;min-height:120px;box-shadow:var(--ax-shadow-sm)}.axp-clock-calendar-hands-container,.axp-clock-calendar-numbers-container{position:absolute;top:0;left:0;width:100%;height:100%;border-radius:50%}.axp-clock-calendar-hour-marker{position:absolute;left:50%;top:50%;width:2px;height:4%;margin-left:-1px;border-radius:1px;background-color:var(--ax-text-muted);transform-origin:50% 0}.axp-clock-calendar-hour-number{position:absolute;left:50%;top:50%;transform-origin:50% 0;font-size:clamp(.7rem,2.5vw,.9rem);font-weight:600;color:var(--ax-text-color)}.axp-clock-calendar-hour-number span{display:block}.axp-clock-calendar-hour-hand{position:absolute;top:50%;left:50%;width:4px;height:25%;margin-left:-2px;background-color:rgba(23,23,23,.75)}.axp-clock-calendar-hour-hand:is(.ax-dark *){background-color:rgba(245,245,245,.75)}.axp-clock-calendar-hour-hand{transform-origin:50% 0;border-radius:3px;box-shadow:0 0 4px rgba(0,0,0,.3)}.axp-clock-calendar-minute-hand{position:absolute;top:50%;left:50%;width:3px;height:38%;margin-left:-1.5px;background-color:rgba(23,23,23,.5)}.axp-clock-calendar-minute-hand:is(.ax-dark *){background-color:rgba(245,245,245,.5)}.axp-clock-calendar-minute-hand{transform-origin:50% 0;border-radius:2px;box-shadow:0 0 4px rgba(0,0,0,.3)}.axp-clock-calendar-second-hand{position:absolute;top:50%;left:50%;width:2px;height:42%;margin-left:-1px;--tw-bg-opacity: 1;background-color:rgba(var(--ax-sys-color-danger-500),var(--tw-bg-opacity, 1))}.axp-clock-calendar-second-hand:is(.ax-dark *){--tw-bg-opacity: 1;background-color:rgba(var(--ax-sys-color-danger-400),var(--tw-bg-opacity, 1))}.axp-clock-calendar-second-hand{transform-origin:50% 0;border-radius:1px;box-shadow:0 0 3px rgba(0,0,0,.2)}.axp-clock-calendar-center-dot{position:absolute;top:50%;left:50%;width:10px;height:10px;border-radius:50%;margin-top:-5px;margin-left:-5px;background-color:#d81159;border:2px solid var(--ax-surface-color);box-shadow:0 0 4px rgba(0,0,0,.3)}.axp-clock-calendar-date-display{display:flex;align-items:center;gap:.5rem;font-size:.85rem;padding:.25rem .6rem;border-radius:.25rem;background-color:var(--ax-surface-hover);color:var(--ax-text-color)}.axp-clock-calendar-date-display i{font-size:.85rem;opacity:.7}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "ngmodule", type: AXDateTimeModule }, { kind: "ngmodule", type: AXFormatModule }, { kind: "pipe", type: i2$9.AXFormatPipe, name: "format" }, { kind: "ngmodule", type: AXTagModule }, { kind: "ngmodule", type: AXDecoratorModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
13152
- }
13153
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: AXPClockCalendarWidgetViewComponent, decorators: [{
13154
- type: Component,
13155
- args: [{ standalone: true, imports: [CommonModule, AXDateTimeModule, AXFormatModule, AXTagModule, AXDecoratorModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"axp-clock-calendar-container\">\n <!-- Timezone indicator (only shown when not local) -->\n @if (showTimezoneIndicator()) {\n <div class=\"axp-clock-calendar-timezone-badge\"><i class=\"fa-solid fa-globe\"></i> {{ displayTimezone() }}</div>\n }\n\n <!-- Day of week display -->\n @if (showDayOfWeek()) {\n <div class=\"axp-clock-calendar-day-label\">{{ getDayOfWeek() }}</div>\n }\n\n <div class=\"axp-clock-calendar-content\">\n <!-- Digital Clock Display -->\n @if (showDigitalClock()) {\n <div class=\"axp-clock-calendar-digital-clock\">\n {{ currentTime | format : 'datetime' : timeFormat() | async }}\n </div>\n }\n\n <!-- Analog Clock Display -->\n @if (showAnalogClock()) {\n <div class=\"axp-clock-calendar-analog-clock\">\n <!-- Hour markers -->\n @for (hour of clockHours; track hour) {\n <div\n class=\"axp-clock-calendar-hour-marker\"\n [style.transform]=\"'rotate(' + hour * 30 + 'deg) translateY(-80px)'\"\n ></div>\n }\n\n <!-- Clock Numbers -->\n <div class=\"axp-clock-calendar-numbers-container\">\n @for (hour of clockHourNumbers; track hour) {\n <div\n class=\"axp-clock-calendar-hour-number\"\n [style.transform]=\"'rotate(' + hour.angle + 'deg) translateY(-82px)'\"\n >\n <span [style.transform]=\"'rotate(' + -hour.angle + 'deg)'\">{{ hour.number }}</span>\n </div>\n }\n </div>\n\n <!-- Clock Hands -->\n <div class=\"axp-clock-calendar-hands-container\">\n <div class=\"axp-clock-calendar-hour-hand\" [style.transform]=\"'rotate(' + hourRotation + 'deg)'\"></div>\n <div class=\"axp-clock-calendar-minute-hand\" [style.transform]=\"'rotate(' + minuteRotation + 'deg)'\"></div>\n @if (showSeconds()) {\n <div class=\"axp-clock-calendar-second-hand\" [style.transform]=\"'rotate(' + secondRotation + 'deg)'\"></div>\n }\n <div class=\"axp-clock-calendar-center-dot\"></div>\n </div>\n </div>\n }\n\n <!-- Date Display -->\n @if (showDate()) {\n <div class=\"axp-clock-calendar-date-display\">\n <i class=\"fa-regular fa-calendar\"></i>\n {{ currentDate | format : 'datetime' : dateFormat() | async }}\n </div>\n }\n </div>\n</div>\n", styles: [":host{display:block;width:100%;height:100%}.axp-clock-calendar-container{display:flex;flex-direction:column;align-items:center;justify-content:center;width:100%;height:100%;padding:1rem;position:relative;overflow:hidden;box-shadow:var(--ax-shadow-sm);background-color:var(--ax-surface-color);color:var(--ax-text-color)}.axp-clock-calendar-content{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.75rem;width:100%;height:100%;position:relative}.axp-clock-calendar-timezone-badge{position:absolute;top:.5rem;right:.5rem;font-size:.75rem;padding:.25rem;border-radius:1rem;border-width:1px;border-color:rgba(23,23,23,.5)}.axp-clock-calendar-timezone-badge:is(.ax-dark *){border-color:rgba(245,245,245,.5)}.axp-clock-calendar-timezone-badge{display:flex;align-items:center;gap:.25rem}.axp-clock-calendar-timezone-badge i{font-size:.75rem}.axp-clock-calendar-day-label{font-size:.9rem;font-weight:600;text-transform:uppercase;letter-spacing:.05rem;margin-bottom:.25rem}.axp-clock-calendar-digital-clock{font-size:1.5rem;font-weight:500;letter-spacing:.05rem;font-family:monospace;padding:.5rem .75rem;border-radius:.25rem}.axp-clock-calendar-analog-clock{position:relative;border-radius:50%;border-width:1px;border-color:rgba(23,23,23,.5)}.axp-clock-calendar-analog-clock:is(.ax-dark *){border-color:rgba(245,245,245,.5)}.axp-clock-calendar-analog-clock{display:flex;align-items:center;justify-content:center;background:var(--ax-surface-color);width:min(180px,100%);height:0;padding-bottom:min(180px,100%);margin:.5rem auto;min-width:120px;min-height:120px;box-shadow:var(--ax-shadow-sm)}.axp-clock-calendar-hands-container,.axp-clock-calendar-numbers-container{position:absolute;top:0;left:0;width:100%;height:100%;border-radius:50%}.axp-clock-calendar-hour-marker{position:absolute;left:50%;top:50%;width:2px;height:4%;margin-left:-1px;border-radius:1px;background-color:var(--ax-text-muted);transform-origin:50% 0}.axp-clock-calendar-hour-number{position:absolute;left:50%;top:50%;transform-origin:50% 0;font-size:clamp(.7rem,2.5vw,.9rem);font-weight:600;color:var(--ax-text-color)}.axp-clock-calendar-hour-number span{display:block}.axp-clock-calendar-hour-hand{position:absolute;top:50%;left:50%;width:4px;height:25%;margin-left:-2px;background-color:rgba(23,23,23,.75)}.axp-clock-calendar-hour-hand:is(.ax-dark *){background-color:rgba(245,245,245,.75)}.axp-clock-calendar-hour-hand{transform-origin:50% 0;border-radius:3px;box-shadow:0 0 4px rgba(0,0,0,.3)}.axp-clock-calendar-minute-hand{position:absolute;top:50%;left:50%;width:3px;height:38%;margin-left:-1.5px;background-color:rgba(23,23,23,.5)}.axp-clock-calendar-minute-hand:is(.ax-dark *){background-color:rgba(245,245,245,.5)}.axp-clock-calendar-minute-hand{transform-origin:50% 0;border-radius:2px;box-shadow:0 0 4px rgba(0,0,0,.3)}.axp-clock-calendar-second-hand{position:absolute;top:50%;left:50%;width:2px;height:42%;margin-left:-1px;--tw-bg-opacity: 1;background-color:rgba(var(--ax-sys-color-danger-500),var(--tw-bg-opacity, 1))}.axp-clock-calendar-second-hand:is(.ax-dark *){--tw-bg-opacity: 1;background-color:rgba(var(--ax-sys-color-danger-400),var(--tw-bg-opacity, 1))}.axp-clock-calendar-second-hand{transform-origin:50% 0;border-radius:1px;box-shadow:0 0 3px rgba(0,0,0,.2)}.axp-clock-calendar-center-dot{position:absolute;top:50%;left:50%;width:10px;height:10px;border-radius:50%;margin-top:-5px;margin-left:-5px;background-color:#d81159;border:2px solid var(--ax-surface-color);box-shadow:0 0 4px rgba(0,0,0,.3)}.axp-clock-calendar-date-display{display:flex;align-items:center;gap:.5rem;font-size:.85rem;padding:.25rem .6rem;border-radius:.25rem;background-color:var(--ax-surface-hover);color:var(--ax-text-color)}.axp-clock-calendar-date-display i{font-size:.85rem;opacity:.7}\n"] }]
13156
- }] });
13157
-
13158
- var clockCalendarWidget_component = /*#__PURE__*/Object.freeze({
13159
- __proto__: null,
13160
- AXPClockCalendarWidgetViewComponent: AXPClockCalendarWidgetViewComponent
13161
- });
13162
-
13163
- /**
13164
- * ACoreX Clock Calendar Widget Types
13165
- * Contains all types and interfaces for the Clock Calendar widget
13166
- */
13167
- /**
13168
- * Common timezone options for the widget configuration
13169
- */
13170
- const AXP_TIMEZONE_OPTIONS = [
13171
- { id: 'local', title: 'Local Time' },
13172
- { id: 'UTC+0', title: 'UTC (GMT)' },
13173
- { id: 'UTC+1', title: 'Central European Time (UTC+1)' },
13174
- { id: 'UTC+2', title: 'Eastern European Time (UTC+2)' },
13175
- { id: 'UTC+3', title: 'Moscow Time (UTC+3)' },
13176
- { id: 'UTC+4', title: 'Gulf Time (UTC+4)' },
13177
- { id: 'UTC+5:30', title: 'Indian Time (UTC+5:30)' },
13178
- { id: 'UTC+8', title: 'China Time (UTC+8)' },
13179
- { id: 'UTC+9', title: 'Japan Time (UTC+9)' },
13180
- { id: 'UTC+10', title: 'Australian Eastern Time (UTC+10)' },
13181
- { id: 'UTC-5', title: 'Eastern Time (UTC-5)' },
13182
- { id: 'UTC-6', title: 'Central Time (UTC-6)' },
13183
- { id: 'UTC-7', title: 'Mountain Time (UTC-7)' },
13184
- { id: 'UTC-8', title: 'Pacific Time (UTC-8)' },
13185
- ];
13186
- /**
13187
- * Common date format options for the widget configuration
13188
- */
13189
- const AXP_DATE_FORMAT_OPTIONS = [
13190
- { id: 'dd MMM yyyy', title: '31 Dec 2023' },
13191
- { id: 'MMM dd, yyyy', title: 'Dec 31, 2023' },
13192
- { id: 'dd/MM/yyyy', title: '31/12/2023' },
13193
- { id: 'MM/dd/yyyy', title: '12/31/2023' },
13194
- ];
13195
-
13196
- const AXPClockCalendarWidget = {
13197
- name: 'clock-calendar',
13198
- title: 'Clock & Calendar',
13199
- categories: [AXP_WIDGETS_UTILITY_CATEGORY],
13200
- groups: [AXPWidgetGroupEnum.DashboardWidget],
13201
- type: 'dashboard',
13202
- icon: 'fa-light fa-clock',
13203
- properties: [
13204
- // ====== Display Settings ======
13205
- {
13206
- name: 'displayLayout',
13207
- title: 'Display Layout',
13208
- group: AXP_APPEARANCE_PROPERTY_GROUP,
13209
- schema: {
13210
- dataType: 'string',
13211
- defaultValue: 'both',
13212
- interface: {
13213
- name: 'displayLayout',
13214
- path: 'options.displayLayout',
13215
- type: AXPWidgetsCatalog.select,
13216
- options: {
13217
- dataSource: [
13218
- { id: 'both', title: 'Digital & Analog' },
13219
- { id: 'digital', title: 'Digital Only' },
13220
- { id: 'analog', title: 'Analog Only' },
13221
- ],
13222
- },
13223
- },
13224
- },
13225
- visible: true,
13226
- },
13227
- {
13228
- name: 'showDate',
13229
- title: 'Show Date',
13230
- group: AXP_APPEARANCE_PROPERTY_GROUP,
13231
- schema: {
13232
- defaultValue: true,
13233
- dataType: 'boolean',
13234
- interface: {
13235
- name: 'showDate',
13236
- path: 'options.showDate',
13237
- type: AXPWidgetsCatalog.toggle,
13238
- },
13239
- },
13240
- visible: true,
13241
- },
13242
- {
13243
- name: 'showDayOfWeek',
13244
- title: 'Show Day of Week',
13245
- group: AXP_APPEARANCE_PROPERTY_GROUP,
13246
- schema: {
13247
- defaultValue: true,
13248
- dataType: 'boolean',
13249
- interface: {
13250
- name: 'showDayOfWeek',
13251
- path: 'options.showDayOfWeek',
13252
- type: AXPWidgetsCatalog.toggle,
13253
- },
13254
- },
13255
- visible: true,
13256
- },
13257
- // ====== Time Format Settings ======
13258
- {
13259
- name: 'use24Hour',
13260
- title: 'Use 24 Hour Format',
13261
- group: AXP_BEHAVIOR_PROPERTY_GROUP,
13262
- schema: {
13263
- defaultValue: false,
13264
- dataType: 'boolean',
13265
- interface: {
13266
- name: 'use24Hour',
13267
- path: 'options.use24Hour',
13268
- type: AXPWidgetsCatalog.toggle,
13269
- },
13270
- },
13271
- visible: true,
13272
- },
13273
- {
13274
- name: 'showSeconds',
13275
- title: 'Show Seconds',
13276
- group: AXP_BEHAVIOR_PROPERTY_GROUP,
13277
- schema: {
13278
- defaultValue: true,
13279
- dataType: 'boolean',
13280
- interface: {
13281
- name: 'showSeconds',
13282
- path: 'options.showSeconds',
13283
- type: AXPWidgetsCatalog.toggle,
13284
- },
13285
- },
13286
- visible: true,
13287
- },
13288
- // {
13289
- // name: 'showHourMarkers',
13290
- // title: 'Show Hour Markers',
13291
- // group: AXP_APPEARANCE_PROPERTY_GROUP,
13292
- // schema: {
13293
- // defaultValue: true,
13294
- // dataType: 'boolean',
13295
- // interface: {
13296
- // name: 'showHourMarkers',
13297
- // path: 'options.showHourMarkers',
13298
- // type: AXPWidgetsCatalog.toggle,
13299
- // },
13300
- // },
13301
- // visible: true,
13302
- // },
13303
- {
13304
- name: 'dateFormat',
13305
- title: 'Date Format',
13306
- group: AXP_BEHAVIOR_PROPERTY_GROUP,
13307
- schema: {
13308
- defaultValue: 'dd MMM yyyy',
13309
- dataType: 'string',
13310
- interface: {
13311
- name: 'dateFormat',
13312
- path: 'options.dateFormat',
13313
- type: AXPWidgetsCatalog.select,
13314
- options: {
13315
- dataSource: AXP_DATE_FORMAT_OPTIONS,
13316
- },
13317
- },
13318
- },
13319
- visible: true,
13320
- },
13321
- {
13322
- name: 'timezone',
13323
- title: 'Timezone',
13324
- group: AXP_BEHAVIOR_PROPERTY_GROUP,
13325
- schema: {
13326
- defaultValue: 'local',
13327
- dataType: 'string',
13328
- interface: {
13329
- name: 'timezone',
13330
- path: 'options.timezone',
13331
- type: AXPWidgetsCatalog.select,
13332
- options: {
13333
- dataSource: AXP_TIMEZONE_OPTIONS,
13334
- },
13335
- },
13336
- },
13337
- visible: true,
13338
- },
13339
- ],
13340
- components: {
13341
- view: {
13342
- component: () => Promise.resolve().then(function () { return clockCalendarWidget_component; }).then((m) => m.AXPClockCalendarWidgetViewComponent),
13343
- },
13344
- },
13345
- meta: {
13346
- dimensions: {
13347
- width: 2,
13348
- height: 5,
13349
- minWidth: 2,
13350
- minHeight: 2,
13351
- maxWidth: 3,
13352
- maxHeight: 6,
13353
- },
13354
- },
13355
- };
13356
-
13357
- /**
13358
- * Donut Chart Widget Component
13359
- * Displays data in a circular donut chart with interactive segments
13360
- */
13361
- class AXPDonutChartWidgetViewComponent extends AXPChartBaseComponent {
13362
- constructor() {
13363
- super(...arguments);
13364
- this.segmentClick = output();
13365
- // Chart reference and D3 related properties
13366
- this.cdr = inject(ChangeDetectorRef);
13367
- this.chartContainerEl = viewChild.required('chartContainer');
13368
- this.pieData = [];
13369
- // State management
13370
- this.hiddenSegments = new Set();
13371
- // Tooltip state
13372
- this._tooltipVisible = signal(false);
13373
- this._tooltipPosition = signal({ x: 0, y: 0 });
13374
- this._tooltipData = signal({
13375
- title: '',
13376
- value: 0,
13377
- percentage: '0%',
13378
- color: '',
13379
- });
13380
- // Public computed properties for the template
13381
- this.tooltipVisible = computed(() => this._tooltipVisible());
13382
- this.tooltipPosition = computed(() => this._tooltipPosition());
13383
- this.tooltipData = computed(() => this._tooltipData());
13384
- // Computed configuration options
13385
- this.showLegend = computed(() => this.options()['showLegend'] !== false);
13386
- this.showTooltip = computed(() => this.options()['showTooltip'] !== false);
13387
- this.legendPosition = computed(() => this.options()['legendPosition']?.id || 'right');
13388
- this.donutWidth = computed(() => this.options()['donutWidth'] ?? 35);
13389
- this.cornerRadius = computed(() => this.options()['cornerRadius'] ?? 4);
13390
- // Data accessor for handling different incoming data formats
13391
- this.chartDataArray = computed(() => {
13392
- const data = this.chartData();
13393
- if (data === null || data === undefined) {
13394
- return [];
13395
- }
13396
- // Handle both array format and object format with data property
13397
- return Array.isArray(data) ? data : data.data || [];
13398
- });
13399
- }
13400
- getColor(index) {
13401
- return AXPChartColors.getColor(index);
13402
- }
13403
- isSegmentHidden(id) {
13404
- return this.hiddenSegments.has(id);
13405
- }
13406
- // Event handlers
13407
- onSegmentClick(item) {
13408
- this.toggleSegment(item);
13409
- this.segmentClick.emit(item);
13410
- }
13411
- onLegendMouseEnter(item) {
13412
- if (!this.svg)
13413
- return;
13414
- // Highlight the target segment
13415
- this.svg
13416
- .selectAll('path')
13417
- .filter((d) => d?.data?.id === item.id)
13418
- .classed('axp-donut-chart-highlighted', true)
13419
- .attr('transform', 'scale(1.02)');
13420
- // Dim other segments
13421
- this.svg
13422
- .selectAll('path')
13423
- .filter((d) => d?.data?.id !== item.id)
13424
- .classed('axp-donut-chart-dimmed', true);
13425
- }
13426
- onLegendMouseLeave() {
13427
- if (!this.svg)
13428
- return;
13429
- // Reset all segments
13430
- this.svg
13431
- .selectAll('path')
13432
- .classed('axp-donut-chart-highlighted', false)
13433
- .classed('axp-donut-chart-dimmed', false)
13434
- .attr('transform', 'scale(1)');
13435
- }
13436
- // Chart lifecycle methods (implementation of base class abstract methods)
13437
- createChart() {
13438
- if (!this.d3 || !this.chartContainerEl()?.nativeElement)
13439
- return;
13440
- try {
13441
- const containerElement = this.chartContainerEl().nativeElement;
13442
- const chartElement = containerElement.querySelector('div');
13443
- this.clearChart(chartElement);
13444
- const data = this.chartDataArray();
13445
- if (!data || data.length === 0) {
13446
- this.showNoDataMessage(chartElement);
13447
- return;
13448
- }
13449
- const options = this.options();
13450
- const { width, height } = this.setupDimensions(containerElement, options);
13451
- this.renderDonutChart(chartElement, width, height);
13452
- }
13453
- catch (error) {
13454
- console.error('Error creating donut chart:', error);
13455
- this.handleChartError();
13456
- }
13457
- }
13458
- updateChart() {
13459
- this.createChart();
13460
- }
13461
- cleanupChart() {
13462
- if (this.svg) {
13463
- this.d3.select(this.chartContainerEl()?.nativeElement).selectAll('svg').remove();
13464
- this.svg = null;
13465
- this.pieData = [];
13466
- }
13467
- this.hiddenSegments.clear();
13468
- this._tooltipVisible.set(false);
13469
- }
13470
- // Private helper methods
13471
- clearChart(element) {
13472
- this.d3.select(element).selectAll('*').remove();
13473
- }
13474
- handleChartError() {
13475
- try {
13476
- const containerElement = this.chartContainerEl()?.nativeElement;
13477
- if (containerElement) {
13478
- const chartElement = containerElement.querySelector('div');
13479
- if (chartElement) {
13480
- this.showNoDataMessage(chartElement);
13481
- }
13482
- }
13483
- }
13484
- catch (e) {
13485
- console.error('Failed to show no data message:', e);
13486
- }
13487
- }
13488
- setupDimensions(containerElement, options) {
13489
- const containerWidth = containerElement.clientWidth;
13490
- const containerHeight = containerElement.clientHeight;
13491
- // Ensure minimum dimensions for the chart
13492
- const minDim = 200;
13493
- const width = Math.max(options['width'] || containerWidth, minDim);
13494
- const height = Math.max(options['height'] || containerHeight, minDim);
13495
- return { width, height };
13496
- }
13497
- renderDonutChart(chartElement, width, height) {
13498
- const data = this.chartDataArray();
13499
- // Filter out hidden segments
13500
- const visibleData = data.filter((item) => !this.hiddenSegments.has(item.id));
13501
- // If all segments are hidden, show no data message and return
13502
- if (visibleData.length === 0) {
13503
- this.showAllSegmentsHiddenMessage(chartElement);
13504
- return;
13505
- }
13506
- const total = this.calculateTotal();
13507
- // Calculate chart dimensions based on legend position
13508
- const { chartWidth, chartHeight, translateX, translateY } = this.calculateChartLayout(width, height);
13509
- // Create SVG container with filters
13510
- const svg = this.createSvgWithFilters(chartElement, width, height);
13511
- // Create main chart group
13512
- this.svg = svg.append('g').attr('transform', `translate(${translateX}, ${translateY})`);
13513
- // Create donut segments
13514
- this.createDonutSegments(chartWidth, chartHeight, visibleData, total);
13515
- // Add total in center
13516
- this.addTotalDisplay(total);
13517
- }
13518
- calculateChartLayout(width, height) {
13519
- const legendPosition = this.legendPosition();
13520
- let chartWidth = width;
13521
- let chartHeight = height;
13522
- let translateX = width / 2;
13523
- let translateY = height / 2;
13524
- if (this.showLegend()) {
13525
- if (legendPosition === 'right') {
13526
- // Reserve space for right legend
13527
- const legendWidth = Math.min(width * 0.3, 150);
13528
- chartWidth = width - legendWidth - 20;
13529
- translateX = (width - chartWidth) / 3 + chartWidth / 2;
13530
- }
13531
- else if (legendPosition === 'bottom') {
13532
- // Reserve space for bottom legend
13533
- const legendHeight = Math.min(height * 0.2, 80);
13534
- chartHeight = height - legendHeight - 20;
13535
- translateY = (height - chartHeight) / 3 + chartHeight / 2;
13536
- }
13537
- }
13538
- return { chartWidth, chartHeight, translateX, translateY };
13539
- }
13540
- createSvgWithFilters(chartElement, width, height) {
13541
- const svg = this.d3
13542
- .select(chartElement)
13543
- .append('svg')
13544
- .attr('width', width)
13545
- .attr('height', height)
13546
- .attr('viewBox', `0 0 ${width} ${height}`)
13547
- .attr('preserveAspectRatio', 'xMidYMid meet');
13548
- // Add drop shadow filter
13549
- const defs = svg.append('defs');
13550
- const filter = defs.append('filter').attr('id', 'axp-donut-chart-segment-shadow').attr('height', '130%');
13551
- filter.append('feGaussianBlur').attr('in', 'SourceAlpha').attr('stdDeviation', 2).attr('result', 'blur');
13552
- filter.append('feOffset').attr('in', 'blur').attr('dx', 1).attr('dy', 1).attr('result', 'offsetBlur');
13553
- const feComponentTransfer = filter
13554
- .append('feComponentTransfer')
13555
- .attr('in', 'offsetBlur')
13556
- .attr('result', 'offsetBlur');
13557
- feComponentTransfer.append('feFuncA').attr('type', 'linear').attr('slope', 0.3);
13558
- const feMerge = filter.append('feMerge');
13559
- feMerge.append('feMergeNode').attr('in', 'offsetBlur');
13560
- feMerge.append('feMergeNode').attr('in', 'SourceGraphic');
13561
- return svg;
13562
- }
13563
- createDonutSegments(chartWidth, chartHeight, data, total) {
13564
- // Create pie layout
13565
- const pie = this.d3
13566
- .pie()
13567
- .value((d) => d.value)
13568
- .sort(null)
13569
- .padAngle(0.02);
13570
- // Calculate the radius of the donut chart
13571
- const radius = (Math.min(chartWidth, chartHeight) / 2) * 0.85;
13572
- // Calculate inner radius based on donutWidth percentage
13573
- const donutWidthPercent = this.donutWidth() / 100;
13574
- const innerRadius = radius * (1 - donutWidthPercent);
13575
- // Create arc generator with the configured radius and corner radius
13576
- const arc = this.d3
13577
- .arc()
13578
- .innerRadius(innerRadius)
13579
- .outerRadius(radius * 0.95)
13580
- .cornerRadius(this.cornerRadius());
13581
- // Generate pie data
13582
- this.pieData = pie(data);
13583
- // Create color scale
13584
- const colorScale = this.setupColorScale(this.chartDataArray());
13585
- // Add segments with animation
13586
- this.addSegmentsWithAnimation(arc, colorScale, total);
13587
- }
13588
- setupColorScale(data) {
13589
- return this.d3
13590
- .scaleOrdinal()
13591
- .domain(data.map((_, i) => i.toString()))
13592
- .range(data.map((_, i) => this.getColor(i)));
13593
- }
13594
- addSegmentsWithAnimation(arc, colorScale, total) {
13595
- // Add segments
13596
- const segments = this.svg
13597
- .selectAll('path')
13598
- .data(this.pieData)
13599
- .enter()
13600
- .append('path')
13601
- .attr('class', 'axp-donut-chart-segment')
13602
- .attr('fill', (d, i) => colorScale(i.toString()))
13603
- .style('opacity', 0)
13604
- .on('click', (event, d) => this.onSegmentClick(d.data))
13605
- .on('mousemove', (event, d) => this.handleSegmentHover(event, d, total))
13606
- .on('mouseleave', () => this._tooltipVisible.set(false));
13607
- // Animate segments
13608
- segments
13609
- .transition()
13610
- .duration(800)
13611
- .ease(this.d3.easeCubicOut)
13612
- .delay((d, i) => i * 50)
13613
- .style('opacity', 1)
13614
- .attrTween('d', (d) => {
13615
- const interpolate = this.d3.interpolate({ startAngle: d.startAngle, endAngle: d.startAngle }, d);
13616
- return (t) => arc(interpolate(t));
13617
- });
13618
- }
13619
- handleSegmentHover(event, datum, total) {
13620
- if (!this.showTooltip())
13621
- return;
13622
- const percentage = ((datum.data.value / total) * 100).toFixed(1);
13623
- const segmentColor = this.getColor(this.chartDataArray().findIndex((item) => item.id === datum.data.id));
13624
- this._tooltipData.set({
13625
- title: datum.data.name,
13626
- value: datum.data.value,
13627
- percentage: `${percentage}%`,
13628
- color: segmentColor,
13629
- });
13630
- this.updateTooltipPosition(event);
13631
- this._tooltipVisible.set(true);
13632
- }
13633
- updateTooltipPosition(event) {
13634
- const container = this.chartContainerEl().nativeElement.getBoundingClientRect();
13635
- const x = event.clientX - container.left;
13636
- const y = event.clientY - container.top;
13637
- this._tooltipPosition.set({
13638
- x: x,
13639
- y: y,
13640
- });
13641
- }
13642
- toggleSegment(item) {
13643
- if (this.hiddenSegments.has(item.id)) {
13644
- this.hiddenSegments.delete(item.id);
13645
- }
13646
- else {
13647
- this.hiddenSegments.add(item.id);
13648
- }
13649
- // Hide tooltip when toggling segments
13650
- this._tooltipVisible.set(false);
13651
- this.updateChart();
13652
- }
13653
- calculateTotal() {
13654
- return this.chartDataArray()
13655
- .filter((item) => !this.hiddenSegments.has(item.id))
13656
- .reduce((sum, item) => sum + item.value, 0);
13657
- }
13658
- showNoDataMessage(containerElement) {
13659
- const messageContainer = this.d3
13660
- .select(containerElement)
13661
- .append('div')
13662
- .attr('class', 'axp-donut-chart-no-data-message')
13663
- .style('width', 'auto')
13664
- .style('text-align', 'center');
13665
- // Add an icon
13666
- messageContainer
13667
- .append('div')
13668
- .attr('class', 'axp-donut-chart-no-data-icon')
13669
- .html('<i class="fa-light fa-chart-pie-simple fa-2x"></i>');
13670
- // Add text message and help text
13671
- messageContainer.append('div').attr('class', 'axp-donut-chart-no-data-text').text('No data available');
13672
- messageContainer
13673
- .append('div')
13674
- .attr('class', 'axp-donut-chart-no-data-help')
13675
- .text('Please provide data in the correct format');
13676
- // Position in center
13677
- const container = containerElement.getBoundingClientRect();
13678
- messageContainer.style('left', `${container.width / 2}px`).style('top', `${container.height / 2}px`);
13679
- }
13680
- // Add method to show message when all segments are hidden
13681
- showAllSegmentsHiddenMessage(containerElement) {
13682
- this.clearChart(containerElement);
13683
- // Add a simple div to ensure proper positioning
13684
- const wrapper = this.d3
13685
- .select(containerElement)
13686
- .append('div')
13687
- .style('position', 'relative')
13688
- .style('width', '100%')
13689
- .style('height', '100%');
13690
- const messageContainer = wrapper
13691
- .append('div')
13692
- .attr('class', 'axp-donut-chart-no-data-message')
13693
- .style('position', 'absolute')
13694
- .style('left', '50%')
13695
- .style('top', '50%')
13696
- .style('transform', 'translate(-50%, -50%)')
13697
- .style('text-align', 'center')
13698
- .style('z-index', '10')
13699
- .style('background-color', 'rgba(255, 255, 255, 0.95)')
13700
- .style('padding', '1.5rem')
13701
- .style('border-radius', '0.5rem')
13702
- .style('box-shadow', '0 2px 12px rgba(0, 0, 0, 0.08)')
13703
- .style('width', '80%')
13704
- .style('max-width', '300px');
13705
- // Add an icon
13706
- messageContainer
13707
- .append('div')
13708
- .attr('class', 'axp-donut-chart-no-data-icon')
13709
- .style('color', 'var(--ax-text-muted, #999)')
13710
- .style('margin-bottom', '0.75rem')
13711
- .html('<i class="fa-light fa-eye-slash fa-2x"></i>');
13712
- // Add text message and help text
13713
- messageContainer
13714
- .append('div')
13715
- .attr('class', 'axp-donut-chart-no-data-text')
13716
- .style('font-size', '1rem')
13717
- .style('font-weight', '600')
13718
- .style('color', 'var(--ax-text-color, #333)')
13719
- .style('margin-bottom', '0.5rem')
13720
- .text('All segments are hidden');
13721
- messageContainer
13722
- .append('div')
13723
- .attr('class', 'axp-donut-chart-no-data-help')
13724
- .style('font-size', '0.8rem')
13725
- .style('color', 'var(--ax-text-muted, #999)')
13726
- .text('Click on a legend item to show data');
13727
- }
13728
- addTotalDisplay(total) {
13729
- if (!this.svg)
13730
- return;
13731
- // Remove any existing total display
13732
- this.svg.selectAll('.axp-donut-chart-total-display').remove();
13733
- // Create group for the total display
13734
- const totalDisplay = this.svg
13735
- .append('g')
13736
- .attr('class', 'axp-donut-chart-total-display')
13737
- .attr('text-anchor', 'middle');
13738
- // Add total value
13739
- totalDisplay
13740
- .append('text')
13741
- .attr('class', 'axp-donut-chart-total-value')
13742
- .style('font-size', '1.8rem')
13743
- .style('font-weight', '600')
13744
- .style('fill', 'currentColor')
13745
- .text(total.toLocaleString());
13746
- // Add "Total" label
13747
- totalDisplay
13748
- .append('text')
13749
- .attr('class', 'axp-donut-chart-total-label')
13750
- .attr('dy', '1.4em')
13751
- .style('font-size', '0.9rem')
13752
- .style('fill', 'currentColor')
13753
- .style('fill-opacity', '0.8')
13754
- .text('Total');
13755
- }
13756
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: AXPDonutChartWidgetViewComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
13757
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.3", type: AXPDonutChartWidgetViewComponent, isStandalone: true, selector: "ng-component", outputs: { segmentClick: "segmentClick" }, viewQueries: [{ propertyName: "chartContainerEl", first: true, predicate: ["chartContainer"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"axp-donut-chart-container\" #chartContainer>\n <!-- Chart visualization area -->\n <div class=\"axp-donut-chart-content\"></div>\n\n <!-- Legend with conditional positioning -->\n @if (showLegend()) {\n <div\n class=\"axp-donut-chart-legend-container\"\n [class.axp-donut-chart-legend-right]=\"legendPosition() === 'right'\"\n [class.axp-donut-chart-legend-bottom]=\"legendPosition() === 'bottom'\"\n >\n @for (item of chartDataArray(); track item.id) {\n <div\n class=\"axp-donut-chart-legend-item\"\n (mouseenter)=\"onLegendMouseEnter(item)\"\n (mouseleave)=\"onLegendMouseLeave()\"\n (click)=\"onSegmentClick(item)\"\n [class.axp-donut-chart-hidden]=\"isSegmentHidden(item.id)\"\n >\n <div class=\"axp-donut-chart-legend-color\" [style.background-color]=\"getColor($index)\"></div>\n <span class=\"axp-donut-chart-legend-name\">{{ item.name }}</span>\n <span class=\"axp-donut-chart-legend-value\">{{ item.value }}</span>\n </div>\n }\n </div>\n }\n\n <!-- Shared tooltip component -->\n <ax-chart-tooltip\n [visible]=\"tooltipVisible()\"\n [position]=\"tooltipPosition()\"\n [data]=\"tooltipData()\"\n [showPercentage]=\"true\"\n ></ax-chart-tooltip>\n</div>\n", styles: [":host{display:block;width:100%;height:100%}.axp-donut-chart-container{position:relative;height:100%;width:100%;padding:1rem;border-radius:.5rem;box-shadow:var(--ax-card-shadow, 0 2px 8px rgba(0, 0, 0, .05));box-sizing:border-box;display:flex;flex-direction:column;overflow:hidden}.axp-donut-chart-content{flex:1;width:100%;display:flex;align-items:center;justify-content:center;position:relative;min-height:0}.axp-donut-chart-legend-container{display:flex;gap:.75rem;padding:.75rem;flex-wrap:wrap;justify-content:center;font-family:var(--ax-font-family, system-ui, sans-serif);z-index:5}.axp-donut-chart-legend-right{flex-direction:column;position:absolute;right:.75rem;top:0;bottom:0;margin:auto;height:fit-content;max-height:80%;overflow-y:auto;overflow-x:hidden;padding:.75rem;background-color:rgba(255,255,255,.9);-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);border-radius:.375rem;box-shadow:0 2px 8px rgba(0,0,0,.05);max-width:30%}.axp-donut-chart-legend-bottom{position:absolute;bottom:.5rem;left:0;right:0;margin:0 auto;width:auto;max-width:98%;flex-direction:row;flex-wrap:wrap;justify-content:center;padding:.75rem;background-color:rgba(255,255,255,.9);-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);border-radius:.375rem;box-shadow:0 2px 8px rgba(0,0,0,.05)}.axp-donut-chart-legend-item{display:flex;align-items:center;gap:.375rem;padding:.25rem .5rem;border-radius:.375rem;cursor:pointer;transition:all .2s ease;white-space:nowrap;font-size:.875rem}.axp-donut-chart-legend-item:hover{background-color:var(--ax-hover-bg, rgba(0, 0, 0, .05));transform:translateY(-1px)}.axp-donut-chart-legend-item.axp-donut-chart-hidden{opacity:.5}.axp-donut-chart-legend-color{min-width:10px;min-height:10px;width:10px;height:10px;border-radius:2px;box-shadow:0 1px 3px rgba(0,0,0,.1)}.axp-donut-chart-legend-name{font-size:.875rem;font-weight:500;color:var(--ax-text-color, #333)}.axp-donut-chart-legend-value{font-size:.875rem;opacity:.7;margin-left:4px;font-weight:400;color:var(--ax-text-muted, #666)}.axp-donut-chart-no-data-message{position:absolute;text-align:center;transform:translate(-50%,-50%);font-family:var(--ax-font-family, system-ui, sans-serif);background-color:rgba(255,255,255,.9);padding:1.5rem;border-radius:.5rem;box-shadow:0 2px 12px rgba(0,0,0,.08);width:80%;max-width:300px}.axp-donut-chart-no-data-message .axp-donut-chart-no-data-icon{color:var(--ax-text-muted, #999);margin-bottom:.75rem}.axp-donut-chart-no-data-message .axp-donut-chart-no-data-text{font-size:1rem;font-weight:600;color:var(--ax-text-color, #333);margin-bottom:.5rem}.axp-donut-chart-no-data-message .axp-donut-chart-no-data-help{font-size:.8rem;color:var(--ax-text-muted, #999)}.axp-donut-chart-segment{cursor:pointer;transition:all .3s cubic-bezier(.25,.8,.25,1);stroke:#fff;stroke-width:1.5px;filter:drop-shadow(0px 1px 2px rgba(0,0,0,.1))}.axp-donut-chart-segment:hover{opacity:.92;filter:drop-shadow(0px 3px 5px rgba(0,0,0,.15));transform:scale(1.01)}.axp-donut-chart-highlighted{opacity:1;filter:brightness(1.05) drop-shadow(0px 3px 5px rgba(0,0,0,.15));transform:scale(1.02)}.axp-donut-chart-dimmed{opacity:.4}.axp-donut-chart-total-display{pointer-events:none}.axp-donut-chart-total-value{fill:currentColor}.axp-donut-chart-total-label{fill:currentColor;opacity:.8}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: AXPChartTooltipComponent, selector: "ax-chart-tooltip", inputs: ["data", "position", "visible", "showPercentage", "style"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
13758
- }
13759
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: AXPDonutChartWidgetViewComponent, decorators: [{
13760
- type: Component,
13761
- args: [{ standalone: true, imports: [CommonModule, AXPChartTooltipComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"axp-donut-chart-container\" #chartContainer>\n <!-- Chart visualization area -->\n <div class=\"axp-donut-chart-content\"></div>\n\n <!-- Legend with conditional positioning -->\n @if (showLegend()) {\n <div\n class=\"axp-donut-chart-legend-container\"\n [class.axp-donut-chart-legend-right]=\"legendPosition() === 'right'\"\n [class.axp-donut-chart-legend-bottom]=\"legendPosition() === 'bottom'\"\n >\n @for (item of chartDataArray(); track item.id) {\n <div\n class=\"axp-donut-chart-legend-item\"\n (mouseenter)=\"onLegendMouseEnter(item)\"\n (mouseleave)=\"onLegendMouseLeave()\"\n (click)=\"onSegmentClick(item)\"\n [class.axp-donut-chart-hidden]=\"isSegmentHidden(item.id)\"\n >\n <div class=\"axp-donut-chart-legend-color\" [style.background-color]=\"getColor($index)\"></div>\n <span class=\"axp-donut-chart-legend-name\">{{ item.name }}</span>\n <span class=\"axp-donut-chart-legend-value\">{{ item.value }}</span>\n </div>\n }\n </div>\n }\n\n <!-- Shared tooltip component -->\n <ax-chart-tooltip\n [visible]=\"tooltipVisible()\"\n [position]=\"tooltipPosition()\"\n [data]=\"tooltipData()\"\n [showPercentage]=\"true\"\n ></ax-chart-tooltip>\n</div>\n", styles: [":host{display:block;width:100%;height:100%}.axp-donut-chart-container{position:relative;height:100%;width:100%;padding:1rem;border-radius:.5rem;box-shadow:var(--ax-card-shadow, 0 2px 8px rgba(0, 0, 0, .05));box-sizing:border-box;display:flex;flex-direction:column;overflow:hidden}.axp-donut-chart-content{flex:1;width:100%;display:flex;align-items:center;justify-content:center;position:relative;min-height:0}.axp-donut-chart-legend-container{display:flex;gap:.75rem;padding:.75rem;flex-wrap:wrap;justify-content:center;font-family:var(--ax-font-family, system-ui, sans-serif);z-index:5}.axp-donut-chart-legend-right{flex-direction:column;position:absolute;right:.75rem;top:0;bottom:0;margin:auto;height:fit-content;max-height:80%;overflow-y:auto;overflow-x:hidden;padding:.75rem;background-color:rgba(255,255,255,.9);-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);border-radius:.375rem;box-shadow:0 2px 8px rgba(0,0,0,.05);max-width:30%}.axp-donut-chart-legend-bottom{position:absolute;bottom:.5rem;left:0;right:0;margin:0 auto;width:auto;max-width:98%;flex-direction:row;flex-wrap:wrap;justify-content:center;padding:.75rem;background-color:rgba(255,255,255,.9);-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);border-radius:.375rem;box-shadow:0 2px 8px rgba(0,0,0,.05)}.axp-donut-chart-legend-item{display:flex;align-items:center;gap:.375rem;padding:.25rem .5rem;border-radius:.375rem;cursor:pointer;transition:all .2s ease;white-space:nowrap;font-size:.875rem}.axp-donut-chart-legend-item:hover{background-color:var(--ax-hover-bg, rgba(0, 0, 0, .05));transform:translateY(-1px)}.axp-donut-chart-legend-item.axp-donut-chart-hidden{opacity:.5}.axp-donut-chart-legend-color{min-width:10px;min-height:10px;width:10px;height:10px;border-radius:2px;box-shadow:0 1px 3px rgba(0,0,0,.1)}.axp-donut-chart-legend-name{font-size:.875rem;font-weight:500;color:var(--ax-text-color, #333)}.axp-donut-chart-legend-value{font-size:.875rem;opacity:.7;margin-left:4px;font-weight:400;color:var(--ax-text-muted, #666)}.axp-donut-chart-no-data-message{position:absolute;text-align:center;transform:translate(-50%,-50%);font-family:var(--ax-font-family, system-ui, sans-serif);background-color:rgba(255,255,255,.9);padding:1.5rem;border-radius:.5rem;box-shadow:0 2px 12px rgba(0,0,0,.08);width:80%;max-width:300px}.axp-donut-chart-no-data-message .axp-donut-chart-no-data-icon{color:var(--ax-text-muted, #999);margin-bottom:.75rem}.axp-donut-chart-no-data-message .axp-donut-chart-no-data-text{font-size:1rem;font-weight:600;color:var(--ax-text-color, #333);margin-bottom:.5rem}.axp-donut-chart-no-data-message .axp-donut-chart-no-data-help{font-size:.8rem;color:var(--ax-text-muted, #999)}.axp-donut-chart-segment{cursor:pointer;transition:all .3s cubic-bezier(.25,.8,.25,1);stroke:#fff;stroke-width:1.5px;filter:drop-shadow(0px 1px 2px rgba(0,0,0,.1))}.axp-donut-chart-segment:hover{opacity:.92;filter:drop-shadow(0px 3px 5px rgba(0,0,0,.15));transform:scale(1.01)}.axp-donut-chart-highlighted{opacity:1;filter:brightness(1.05) drop-shadow(0px 3px 5px rgba(0,0,0,.15));transform:scale(1.02)}.axp-donut-chart-dimmed{opacity:.4}.axp-donut-chart-total-display{pointer-events:none}.axp-donut-chart-total-value{fill:currentColor}.axp-donut-chart-total-label{fill:currentColor;opacity:.8}\n"] }]
13762
- }] });
13763
-
13764
- var donutChartWidget_component = /*#__PURE__*/Object.freeze({
13765
- __proto__: null,
13766
- AXPDonutChartWidgetViewComponent: AXPDonutChartWidgetViewComponent
13767
- });
13768
-
13769
- const AXPDonutChartWidget = {
13770
- name: 'donut-chart',
13771
- title: 'Donut Chart Widget',
13772
- categories: AXP_WIDGETS_CHART_CATEGORY,
13773
- groups: [AXPWidgetGroupEnum.DashboardWidget],
13774
- type: 'dashboard',
13775
- icon: 'fa-light fa-donut',
13776
- properties: [
13777
- // ====== Size & Layout ======
13778
- {
13779
- name: 'height',
13780
- title: 'Height',
13781
- group: AXP_STYLING_PROPERTY_GROUP,
13782
- schema: {
13783
- defaultValue: 300,
13784
- dataType: 'number',
13785
- interface: {
13786
- name: 'height',
13787
- path: 'options.height',
13788
- type: AXPWidgetsCatalog.number,
13789
- options: {
13790
- minValue: 200,
13791
- maxValue: 800,
13792
- },
13793
- },
13794
- },
13795
- visible: true,
13796
- },
13797
- {
13798
- name: 'width',
13799
- title: 'Width',
13800
- group: AXP_STYLING_PROPERTY_GROUP,
13801
- schema: {
13802
- defaultValue: 300,
13803
- dataType: 'number',
13804
- interface: {
13805
- name: 'width',
13806
- path: 'options.width',
13807
- type: AXPWidgetsCatalog.number,
13808
- options: {
13809
- minValue: 200,
13810
- maxValue: 1200,
13811
- },
13812
- },
13813
- },
13814
- visible: true,
13815
- },
13816
- // ====== Legend ======
13817
- {
13818
- name: 'showLegend',
13819
- title: 'Show Legend',
13820
- group: AXP_APPEARANCE_PROPERTY_GROUP,
13821
- schema: {
13822
- defaultValue: true,
13823
- dataType: 'boolean',
13824
- interface: {
13825
- name: 'showLegend',
13826
- path: 'options.showLegend',
13827
- type: AXPWidgetsCatalog.toggle,
13828
- },
13829
- },
13830
- visible: true,
13831
- },
13832
- {
13833
- name: 'legendPosition',
13834
- title: 'Legend Position',
13835
- group: AXP_APPEARANCE_PROPERTY_GROUP,
13836
- schema: {
13837
- defaultValue: 'right',
13838
- dataType: 'string',
13839
- interface: {
13840
- name: 'legendPosition',
13841
- path: 'options.legendPosition',
13842
- type: AXPWidgetsCatalog.select,
13843
- options: {
13844
- dataSource: ['right', 'bottom'],
13845
- },
13846
- },
13847
- },
13848
- visible: true,
13849
- },
13850
- // ====== Tooltip ======
13851
- {
13852
- name: 'showTooltip',
13853
- title: 'Show Tooltip',
13854
- group: AXP_APPEARANCE_PROPERTY_GROUP,
13855
- schema: {
13856
- defaultValue: true,
13857
- dataType: 'boolean',
13858
- interface: {
13859
- name: 'showTooltip',
13860
- path: 'options.showTooltip',
13861
- type: AXPWidgetsCatalog.toggle,
13862
- },
13863
- },
13864
- visible: true,
13865
- },
13866
- // ====== Donut Appearance ======
13867
- {
13868
- name: 'donutWidth',
13869
- title: 'Donut Width',
13870
- group: AXP_APPEARANCE_PROPERTY_GROUP,
13871
- schema: {
13872
- defaultValue: 35,
13873
- dataType: 'number',
13874
- interface: {
13875
- name: 'donutWidth',
13876
- path: 'options.donutWidth',
13877
- type: AXPWidgetsCatalog.number,
13878
- options: {
13879
- placeholder: '10-80',
13880
- minValue: 10,
13881
- maxValue: 80,
13882
- },
13883
- },
13884
- },
13885
- visible: true,
13886
- },
13887
- {
13888
- name: 'cornerRadius',
13889
- title: 'Corner Radius',
13890
- group: AXP_APPEARANCE_PROPERTY_GROUP,
13891
- schema: {
13892
- defaultValue: 4,
13893
- dataType: 'number',
13894
- interface: {
13895
- name: 'cornerRadius',
13896
- path: 'options.cornerRadius',
13897
- type: AXPWidgetsCatalog.number,
13898
- options: {
13899
- placeholder: '0-20',
13900
- minValue: 0,
13901
- maxValue: 20,
13902
- },
13903
- },
13904
- },
13905
- visible: true,
13906
- },
13907
- ],
13908
- components: {
13909
- view: {
13910
- component: () => Promise.resolve().then(function () { return donutChartWidget_component; }).then((c) => c.AXPDonutChartWidgetViewComponent),
13911
- },
13912
- },
13913
- meta: {
13914
- dimensions: {
13915
- width: 4,
13916
- height: 4,
13917
- minWidth: 2,
13918
- minHeight: 2,
13919
- maxWidth: 5,
13920
- maxHeight: 6,
13921
- },
13922
- },
13923
- };
13924
-
13925
- /**
13926
- * Gauge Chart Widget Component
13927
- * Renders a semi-circular gauge chart with animated needle and thresholds
13928
- */
13929
- class AXPGaugeChartWidgetViewComponent extends AXPChartBaseComponent {
13930
- constructor() {
13931
- super(...arguments);
13932
- this.cdr = inject(ChangeDetectorRef);
13933
- this.chartContainerEl = viewChild.required('chartContainer');
13934
- this.chartEl = viewChild.required('chart');
13935
- // Default values
13936
- this.backgroundColor = 'transparent';
13937
- this.baseColor = '#e2e8f0';
13938
- this.labelFontSize = 16;
13939
- this.showValue = true;
13940
- this.valueColor = '#6366f1';
13941
- this.valueFontSize = 24;
13942
- // Default values as computed properties
13943
- this.minValue = computed(() => this.options()['minValue'] ?? 0);
13944
- this.maxValue = computed(() => this.options()['maxValue'] ?? 100);
13945
- this.thresholds = computed(() => this.options()['thresholds'] ?? []);
13946
- this.label = computed(() => this.options()['label'] ?? '');
13947
- this.width = computed(() => this.options()['width'] ?? 300);
13948
- this.height = computed(() => this.options()['height'] ?? 300);
13949
- this.gaugeWidth = computed(() => this.options()['gaugeWidth'] ?? 22);
13950
- this.cornerRadius = computed(() => this.options()['cornerRadius'] ?? 5);
13951
- }
13952
- /**
13953
- * Creates the gauge chart with all elements
13954
- */
13955
- createChart() {
13956
- // Clear any existing chart
13957
- this.d3.select(this.chartEl().nativeElement).selectAll('*').remove();
13958
- // Calculate responsive dimensions
13959
- const containerElement = this.chartContainerEl().nativeElement;
13960
- const containerWidth = containerElement.clientWidth;
13961
- const containerHeight = containerElement.clientHeight;
13962
- // Determine size based on explicit dimensions or container size
13963
- const options = this.options();
13964
- let size;
13965
- if (options.width && options.height) {
13966
- // Use explicit dimensions if provided
13967
- size = Math.min(options.width, options.height * 2);
13968
- }
13969
- else {
13970
- // Use container dimensions with minimum constraints
13971
- const minDim = Math.max(200, Math.min(containerWidth, containerHeight));
13972
- size = Math.min(containerWidth, containerHeight * 2, minDim * 2);
13973
- }
13974
- const margin = size * 0.1;
13975
- // Set up SVG with responsive viewBox
13976
- const svg = this.d3
13977
- .select(this.chartEl().nativeElement)
13978
- .attr('width', '100%')
13979
- .attr('height', '100%')
13980
- .attr('viewBox', `0 0 ${size} ${size / 2}`)
13981
- .attr('preserveAspectRatio', 'xMidYMid meet');
13982
- // Create a group for the chart with margin and move it to show only the top half
13983
- const chartGroup = svg.append('g').attr('transform', `translate(${size / 2}, ${size / 2 - margin})`);
13984
- // Define gauge parameters
13985
- const radius = size / 2 - margin;
13986
- const innerRadius = radius - this.gaugeWidth();
13987
- const outerRadius = radius;
13988
- // Create gradient definitions
13989
- this.createGradients(svg, this.thresholds());
13990
- // Draw the background arc
13991
- this.drawBackgroundArc(chartGroup, innerRadius, outerRadius, this.cornerRadius());
13992
- // Draw the threshold arcs if thresholds exist
13993
- if (this.thresholds().length > 0) {
13994
- this.drawThresholds(chartGroup, innerRadius, outerRadius, this.minValue(), this.maxValue(), this.thresholds(), this.cornerRadius());
13995
- }
13996
- // Draw tick marks
13997
- this.drawTicks(chartGroup, outerRadius, this.minValue(), this.maxValue());
13998
- // Draw the dial/needle with animation
13999
- this.drawDial(chartGroup, radius, this.chartData() ?? 0, this.minValue(), this.maxValue());
14000
- // Draw the value display (after the dial so it's on top)
14001
- if (this.showValue) {
14002
- this.drawValueDisplay(chartGroup, this.chartData() ?? 0, this.label(), radius);
14003
- }
14004
- }
14005
- updateChart() {
14006
- this.createChart();
14007
- }
14008
- cleanupChart() {
14009
- if (this.chartEl()?.nativeElement) {
14010
- this.d3.select(this.chartEl().nativeElement).selectAll('*').remove();
14011
- }
14012
- }
14013
- /**
14014
- * Creates gradient definitions for thresholds
14015
- */
14016
- createGradients(svg, thresholds) {
14017
- const defs = svg.append('defs');
14018
- // Create a radial gradient for the background
14019
- const bgGradient = defs
14020
- .append('radialGradient')
14021
- .attr('id', 'gauge-bg-gradient')
14022
- .attr('cx', '50%')
14023
- .attr('cy', '50%')
14024
- .attr('r', '50%')
14025
- .attr('gradientUnits', 'userSpaceOnUse');
14026
- if (thresholds.length === 0) {
14027
- // Default gradient when no thresholds are provided
14028
- bgGradient
14029
- .append('stop')
14030
- .attr('offset', '0%')
14031
- .attr('stop-color', this.d3.color(this.valueColor)?.brighter(0.5).toString() || this.valueColor);
14032
- bgGradient.append('stop').attr('offset', '100%').attr('stop-color', this.valueColor);
14033
- }
14034
- else {
14035
- bgGradient
14036
- .append('stop')
14037
- .attr('offset', '0%')
14038
- .attr('stop-color', this.d3.color(this.baseColor)?.brighter(0.5).toString() || this.baseColor);
14039
- bgGradient.append('stop').attr('offset', '100%').attr('stop-color', this.baseColor);
14040
- // Create gradients for each threshold
14041
- thresholds.forEach((threshold, i) => {
14042
- const gradient = defs
14043
- .append('linearGradient')
14044
- .attr('id', `threshold-gradient-${i}`)
14045
- .attr('gradientUnits', 'userSpaceOnUse')
14046
- .attr('x1', '-1')
14047
- .attr('y1', '0')
14048
- .attr('x2', '1')
14049
- .attr('y2', '0');
14050
- gradient
14051
- .append('stop')
14052
- .attr('offset', '0%')
14053
- .attr('stop-color', this.d3.color(threshold.color)?.brighter(0.5).toString() || threshold.color);
14054
- gradient.append('stop').attr('offset', '100%').attr('stop-color', threshold.color);
14055
- });
14056
- }
14057
- }
14058
- /**
14059
- * Draws the background arc
14060
- */
14061
- drawBackgroundArc(chartGroup, innerRadius, outerRadius, cornerRadius) {
14062
- const backgroundArc = this.d3
14063
- .arc()
14064
- .innerRadius(innerRadius)
14065
- .outerRadius(outerRadius)
14066
- .startAngle(-Math.PI / 2) // Start from bottom (-90 degrees)
14067
- .endAngle(Math.PI / 2) // End at top (90 degrees)
14068
- .cornerRadius(cornerRadius);
14069
- chartGroup
14070
- .append('path')
14071
- .attr('d', backgroundArc({
14072
- innerRadius,
14073
- outerRadius,
14074
- startAngle: -Math.PI / 2,
14075
- endAngle: Math.PI / 2,
14076
- }))
14077
- .attr('fill', this.backgroundColor === 'transparent' ? 'url(#gauge-bg-gradient)' : this.backgroundColor)
14078
- .attr('filter', 'drop-shadow(0px 2px 3px rgba(0,0,0,0.1))');
14079
- }
14080
- /**
14081
- * Draws the threshold arcs with colors
14082
- */
14083
- drawThresholds(chartGroup, innerRadius, outerRadius, minValue, maxValue, thresholds, cornerRadius) {
14084
- const arc = this.d3
14085
- .arc()
14086
- .innerRadius(innerRadius)
14087
- .outerRadius(outerRadius * 0.98)
14088
- .cornerRadius(cornerRadius);
14089
- // Sort thresholds by value in ascending order
14090
- const sortedThresholds = [...thresholds].sort((a, b) => a.value - b.value);
14091
- // Calculate all angles first using the color angle calculation
14092
- const angles = sortedThresholds.map((t) => this.scaleValueToColorAngle(t.value, minValue, maxValue));
14093
- // Start from the minimum value angle
14094
- let previousEndAngle = this.scaleValueToColorAngle(minValue, minValue, maxValue);
14095
- sortedThresholds.forEach((threshold, i) => {
14096
- const endAngle = angles[i];
14097
- chartGroup
14098
- .append('path')
14099
- .attr('d', arc({
14100
- innerRadius,
14101
- outerRadius,
14102
- startAngle: previousEndAngle,
14103
- endAngle,
14104
- }))
14105
- .attr('fill', `url(#threshold-gradient-${i})`)
14106
- .attr('stroke', 'rgba(255,255,255,0.3)')
14107
- .attr('stroke-width', 1);
14108
- previousEndAngle = endAngle;
14109
- });
14110
- // Fill the remaining space to maxValue if needed
14111
- const lastEndAngle = this.scaleValueToColorAngle(maxValue, minValue, maxValue);
14112
- if (previousEndAngle < lastEndAngle) {
14113
- chartGroup
14114
- .append('path')
14115
- .attr('d', arc({
14116
- innerRadius,
14117
- outerRadius,
14118
- startAngle: previousEndAngle,
14119
- endAngle: lastEndAngle,
14120
- }))
14121
- .attr('fill', `url(#threshold-gradient-${sortedThresholds.length - 1})`)
14122
- .attr('stroke', 'rgba(255,255,255,0.3)')
14123
- .attr('stroke-width', 1);
14124
- }
14125
- }
14126
- /**
14127
- * Draws tick marks around the gauge
14128
- */
14129
- drawTicks(chartGroup, radius, minValue, maxValue) {
14130
- const tickCount = 10;
14131
- const minorTickCount = 40;
14132
- // Draw minor ticks
14133
- for (let i = 0; i <= minorTickCount; i++) {
14134
- const value = minValue + (i / minorTickCount) * (maxValue - minValue);
14135
- const angle = this.scaleValueToAngle(value, minValue, maxValue);
14136
- const outerPoint = {
14137
- x: radius * 0.9 * Math.cos(angle),
14138
- y: radius * 0.9 * Math.sin(angle),
14139
- };
14140
- const innerPoint = {
14141
- x: radius * 0.87 * Math.cos(angle),
14142
- y: radius * 0.87 * Math.sin(angle),
14143
- };
14144
- chartGroup
14145
- .append('line')
14146
- .attr('x1', innerPoint.x)
14147
- .attr('y1', innerPoint.y)
14148
- .attr('x2', outerPoint.x)
14149
- .attr('y2', outerPoint.y)
14150
- .attr('stroke', '#adb5bd')
14151
- .attr('stroke-width', 0.5);
14152
- }
14153
- // Draw major ticks and labels
14154
- for (let i = 0; i <= tickCount; i++) {
14155
- const value = minValue + (i / tickCount) * (maxValue - minValue);
14156
- const angle = this.scaleValueToAngle(value, minValue, maxValue);
14157
- const outerPoint = {
14158
- x: radius * 0.92 * Math.cos(angle),
14159
- y: radius * 0.92 * Math.sin(angle),
14160
- };
14161
- const innerPoint = {
14162
- x: radius * 0.85 * Math.cos(angle),
14163
- y: radius * 0.85 * Math.sin(angle),
14164
- };
14165
- // Major tick line
14166
- chartGroup
14167
- .append('line')
14168
- .attr('x1', innerPoint.x)
14169
- .attr('y1', innerPoint.y)
14170
- .attr('x2', outerPoint.x)
14171
- .attr('y2', outerPoint.y)
14172
- .attr('stroke', '#adb5bd')
14173
- .attr('stroke-width', 2);
14174
- // Label position with offset
14175
- const labelRadius = radius * 1.15;
14176
- const labelX = labelRadius * Math.cos(angle);
14177
- const labelY = labelRadius * Math.sin(angle);
14178
- // Add tick value label
14179
- chartGroup
14180
- .append('text')
14181
- .attr('x', labelX)
14182
- .attr('y', labelY)
14183
- .attr('text-anchor', 'middle')
14184
- .style('font-size', `${radius * 0.1}px`)
14185
- .style('font-weight', '500')
14186
- .style('fill', 'currentColor')
14187
- .text(value.toFixed(0));
14188
- }
14189
- }
14190
- /**
14191
- * Draws the value display in the center
14192
- */
14193
- drawValueDisplay(chartGroup, value, label, radius) {
14194
- // Value text - positioned below the needle pivot
14195
- chartGroup
14196
- .append('text')
14197
- .attr('class', 'gauge-value')
14198
- .attr('x', 0)
14199
- .attr('y', radius * 0.25) // Moved up from 0.3
14200
- .attr('text-anchor', 'middle')
14201
- .attr('dominant-baseline', 'central')
14202
- .style('font-size', `${this.valueFontSize}px`)
14203
- .style('font-weight', 'bold')
14204
- .style('fill', 'currentColor')
14205
- .text(value.toFixed(1));
14206
- // Label text
14207
- chartGroup
14208
- .append('text')
14209
- .attr('class', 'gauge-label')
14210
- .attr('x', 0)
14211
- .attr('y', radius * 0.45) // Keeping this position to create more space
14212
- .attr('text-anchor', 'middle')
14213
- .attr('dominant-baseline', 'central')
14214
- .style('font-size', `${this.labelFontSize}px`)
14215
- .style('fill', '#6c757d')
14216
- .text(label);
14217
- }
14218
- /**
14219
- * Draws the dial/needle with animation
14220
- */
14221
- drawDial(chartGroup, radius, value, minValue, maxValue) {
14222
- const needleGroup = chartGroup.append('g');
14223
- const valueAngle = this.scaleValueToAngle(value, minValue, maxValue); // Use regular angle for needle
14224
- // Draw needle base (circle)
14225
- needleGroup
14226
- .append('circle')
14227
- .attr('cx', 0)
14228
- .attr('cy', 0)
14229
- .attr('r', radius * 0.08)
14230
- .attr('fill', 'url(#gauge-bg-gradient)')
14231
- .attr('stroke', '#6c757d')
14232
- .attr('stroke-width', 1);
14233
- // Inner circle
14234
- needleGroup
14235
- .append('circle')
14236
- .attr('cx', 0)
14237
- .attr('cy', 0)
14238
- .attr('r', radius * 0.04)
14239
- .attr('fill', '#495057');
14240
- // Create needle
14241
- const needlePath = needleGroup
14242
- .append('path')
14243
- .attr('d', this.createNeedlePath(radius))
14244
- .attr('fill', '#dc3545')
14245
- .attr('transform', `rotate(${this.radiansToDegrees(-Math.PI)})`) // Start at left (-180 degrees)
14246
- .attr('filter', 'drop-shadow(0px 1px 2px rgba(0,0,0,0.3))');
14247
- // Animate the needle
14248
- needlePath
14249
- .transition()
14250
- .duration(800)
14251
- .ease(this.d3.easeCubicOut)
14252
- .attrTween('transform', () => {
14253
- const interpolate = this.d3.interpolate(-Math.PI, valueAngle);
14254
- return (t) => `rotate(${this.radiansToDegrees(interpolate(t))})`;
14255
- });
14256
- }
14257
- /**
14258
- * Creates a path for the needle
14259
- */
14260
- createNeedlePath(radius) {
14261
- const needleLength = radius * 0.75;
14262
- const needleBaseWidth = radius * 0.04;
14263
- return `M 0 -${needleBaseWidth} L ${needleLength} 0 L 0 ${needleBaseWidth} Z`;
14264
- }
14265
- /**
14266
- * Scales a value to an angle for needle positioning
14267
- */
14268
- scaleValueToAngle(value, min, max) {
14269
- const scaledValue = (value - min) / (max - min);
14270
- // Map from -180 to 0 degrees in radians, starting from the left
14271
- return -Math.PI + scaledValue * Math.PI;
14272
- }
14273
- /**
14274
- * Scales a value to an angle for threshold colors
14275
- */
14276
- scaleValueToColorAngle(value, min, max) {
14277
- const scaledValue = (value - min) / (max - min);
14278
- // Map from -90 to 90 degrees in radians (-π/2 to π/2), starting from the bottom
14279
- return -Math.PI / 2 + scaledValue * Math.PI;
14280
- }
14281
- /**
14282
- * Converts radians to degrees
14283
- */
14284
- radiansToDegrees(radians) {
14285
- return radians * (180 / Math.PI);
14286
- }
14287
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: AXPGaugeChartWidgetViewComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
14288
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "19.0.3", type: AXPGaugeChartWidgetViewComponent, isStandalone: true, selector: "ng-component", viewQueries: [{ propertyName: "chartContainerEl", first: true, predicate: ["chartContainer"], descendants: true, isSignal: true }, { propertyName: "chartEl", first: true, predicate: ["chart"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"ax-flex ax-justify-center ax-items-center ax-w-full ax-h-full\" #chartContainer>\n <svg #chart class=\"ax-w-full ax-h-full\"></svg>\n</div>\n", styles: [":host{display:block;width:100%;height:100%;min-height:200px}div.ax-flex{position:relative;width:100%;height:100%;overflow:visible}svg{display:block;width:100%;height:100%;max-width:100%;max-height:100%;overflow:visible}.gauge-arc{transition:stroke-dashoffset 1s ease}.gauge-label{font-family:var(--ax-font-family, system-ui, sans-serif);font-weight:600}.gauge-value{font-family:var(--ax-font-family, system-ui, sans-serif);font-weight:700}.gauge-min-max{font-family:var(--ax-font-family, system-ui, sans-serif);opacity:.7}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
14289
- }
14290
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: AXPGaugeChartWidgetViewComponent, decorators: [{
14291
- type: Component,
14292
- args: [{ standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"ax-flex ax-justify-center ax-items-center ax-w-full ax-h-full\" #chartContainer>\n <svg #chart class=\"ax-w-full ax-h-full\"></svg>\n</div>\n", styles: [":host{display:block;width:100%;height:100%;min-height:200px}div.ax-flex{position:relative;width:100%;height:100%;overflow:visible}svg{display:block;width:100%;height:100%;max-width:100%;max-height:100%;overflow:visible}.gauge-arc{transition:stroke-dashoffset 1s ease}.gauge-label{font-family:var(--ax-font-family, system-ui, sans-serif);font-weight:600}.gauge-value{font-family:var(--ax-font-family, system-ui, sans-serif);font-weight:700}.gauge-min-max{font-family:var(--ax-font-family, system-ui, sans-serif);opacity:.7}\n"] }]
14293
- }] });
14294
-
14295
- var gaugeChartWidget_component = /*#__PURE__*/Object.freeze({
14296
- __proto__: null,
14297
- AXPGaugeChartWidgetViewComponent: AXPGaugeChartWidgetViewComponent
14298
- });
14299
-
14300
- const AXPGaugeChartWidget = {
14301
- name: 'gauge-chart',
14302
- title: 'Gauge Chart Widget',
14303
- categories: [AXP_WIDGETS_CHART_CATEGORY],
14304
- type: 'dashboard',
14305
- icon: 'fa-light fa-gauge',
14306
- properties: [
14307
- // ====== Layout & Dimensions ======
14308
- {
14309
- name: 'height',
14310
- title: 'Height',
14311
- group: AXP_STYLING_PROPERTY_GROUP,
14312
- schema: {
14313
- defaultValue: 300,
14314
- dataType: 'number',
14315
- interface: {
14316
- name: 'height',
14317
- path: 'options.height',
14318
- type: AXPWidgetsCatalog.number,
14319
- options: {
14320
- placeholder: '1-800',
14321
- minValue: 1,
14322
- maxValue: 800,
14323
- },
14324
- },
14325
- },
14326
- visible: true,
14327
- },
14328
- {
14329
- name: 'width',
14330
- title: 'Width',
14331
- group: AXP_STYLING_PROPERTY_GROUP,
14332
- schema: {
14333
- defaultValue: null,
14334
- dataType: 'number',
14335
- interface: {
14336
- name: 'width',
14337
- path: 'options.width',
14338
- type: AXPWidgetsCatalog.number,
14339
- options: {
14340
- placeholder: '1-1200',
14341
- minValue: 1,
14342
- maxValue: 1200,
14343
- },
14344
- },
14345
- },
14346
- visible: true,
14347
- },
14348
- // ====== Gauge Configuration ======
14349
- {
14350
- name: 'minValue',
14351
- title: 'Minimum Value',
14352
- group: AXP_APPEARANCE_PROPERTY_GROUP,
14353
- schema: {
14354
- defaultValue: 0,
14355
- dataType: 'number',
14356
- interface: {
14357
- name: 'minValue',
14358
- path: 'options.minValue',
14359
- type: AXPWidgetsCatalog.number,
14360
- },
14361
- },
14362
- visible: true,
14363
- },
14364
- {
14365
- name: 'maxValue',
14366
- title: 'Maximum Value',
14367
- group: AXP_APPEARANCE_PROPERTY_GROUP,
14368
- schema: {
14369
- defaultValue: 100,
14370
- dataType: 'number',
14371
- interface: {
14372
- name: 'maxValue',
14373
- path: 'options.maxValue',
14374
- type: AXPWidgetsCatalog.number,
14375
- },
14376
- },
14377
- visible: true,
14378
- },
14379
- // ====== Gauge Appearance ======
14380
- {
14381
- name: 'gaugeWidth',
14382
- title: 'Gauge Width',
14383
- group: AXP_APPEARANCE_PROPERTY_GROUP,
14384
- schema: {
14385
- defaultValue: 30,
14386
- dataType: 'number',
14387
- interface: {
14388
- name: 'gaugeWidth',
14389
- path: 'options.gaugeWidth',
14390
- type: AXPWidgetsCatalog.number,
14391
- options: {
14392
- placeholder: '1-100',
14393
- minValue: 1,
14394
- maxValue: 100,
14395
- },
14396
- },
14397
- },
14398
- visible: true,
14399
- },
14400
- {
14401
- name: 'cornerRadius',
14402
- title: 'Corner Radius',
14403
- group: AXP_APPEARANCE_PROPERTY_GROUP,
14404
- schema: {
14405
- defaultValue: 4,
14406
- dataType: 'number',
14407
- interface: {
14408
- name: 'cornerRadius',
14409
- path: 'options.cornerRadius',
14410
- type: AXPWidgetsCatalog.number,
14411
- options: {
14412
- placeholder: '1-20',
14413
- minValue: 0,
14414
- maxValue: 20,
14415
- },
14416
- },
14417
- },
14418
- visible: true,
14419
- },
14420
- // ====== Label Display ======
14421
- {
14422
- name: 'label',
14423
- title: 'Label Text',
14424
- group: AXP_APPEARANCE_PROPERTY_GROUP,
14425
- schema: {
14426
- defaultValue: '',
14427
- dataType: 'string',
14428
- interface: {
14429
- name: 'label',
14430
- path: 'options.label',
14431
- type: AXPWidgetsCatalog.text,
14432
- },
14433
- },
14434
- visible: true,
14435
- },
14436
- ],
14437
- components: {
14438
- view: {
14439
- component: () => Promise.resolve().then(function () { return gaugeChartWidget_component; }).then((c) => c.AXPGaugeChartWidgetViewComponent),
14440
- },
14441
- },
14442
- meta: {
14443
- dimensions: {
14444
- width: 3,
14445
- height: 4,
14446
- minWidth: 2,
14447
- minHeight: 2,
14448
- maxWidth: 4,
14449
- maxHeight: 5,
14450
- },
14451
- },
14452
- };
14453
-
14454
- /**
14455
- * Line Chart Widget Component
14456
- * Renders data as lines with interactive hover effects and animations
14457
- */
14458
- class AXPLineChartWidgetViewComponent extends AXPChartBaseComponent {
14459
- constructor() {
14460
- super(...arguments);
14461
- this.pointClick = output();
14462
- // Chart container reference
14463
- this.chartContainerEl = viewChild.required('chartContainer');
14464
- this.margin = { top: 20, right: 20, bottom: 30, left: 40 };
14465
- // Tooltip state
14466
- this._tooltipVisible = signal(false);
14467
- this._tooltipPosition = signal({ x: 0, y: 0 });
14468
- this._tooltipData = signal({
14469
- title: '',
14470
- value: '0',
14471
- percentage: '0%',
14472
- color: '',
14473
- });
14474
- // Tooltip accessors
14475
- this.tooltipVisible = computed(() => this._tooltipVisible());
14476
- this.tooltipPosition = computed(() => this._tooltipPosition());
14477
- this.tooltipData = computed(() => this._tooltipData());
14478
- // Line appearance options
14479
- this.lineWidth = computed(() => this.options()['lineWidth'] ?? 2);
14480
- this.showPoints = computed(() => this.options()['showPoints'] !== false);
14481
- this.pointRadius = computed(() => this.options()['pointRadius'] ?? 4);
14482
- this.smoothLine = computed(() => this.options()['smoothLine'] !== false);
14483
- this.fillArea = computed(() => this.options()['fillArea'] === true);
14484
- this.fillOpacity = computed(() => (this.options()['fillOpacity'] ?? 20) / 100);
14485
- }
14486
- // Chart lifecycle methods
14487
- /**
14488
- * Creates the line chart SVG and renders all elements
14489
- */
14490
- createChart() {
14491
- if (!this.d3 || !this.chartContainerEl()?.nativeElement)
14492
- return;
14493
- const containerElement = this.chartContainerEl().nativeElement;
14494
- let data = this.chartData() || [];
14495
- // Clear existing chart
14496
- this.d3.select(containerElement).selectAll('svg').remove();
14497
- // Normalize data structure to array of arrays (multi-series format)
14498
- let normalizedData = [];
14499
- // Handle both flat array and array of arrays formats
14500
- if (data.length > 0) {
14501
- if (Array.isArray(data[0])) {
14502
- // Already in multi-series format
14503
- normalizedData = data;
14504
- }
14505
- else {
14506
- // Single series or flat array with seriesName
14507
- const dataPoints = data;
14508
- // Check if this is multi-series data (has seriesName property)
14509
- const hasSeriesNames = dataPoints.some((d) => d.seriesName !== undefined);
14510
- if (hasSeriesNames) {
14511
- // Organize by series name
14512
- const seriesMap = new Map();
14513
- dataPoints.forEach((d) => {
14514
- const seriesName = d.seriesName || 'default';
14515
- if (!seriesMap.has(seriesName)) {
14516
- seriesMap.set(seriesName, []);
14517
- }
14518
- seriesMap.get(seriesName).push(d);
14519
- });
14520
- // Convert to array of arrays
14521
- normalizedData = Array.from(seriesMap.values());
14522
- }
14523
- else {
14524
- // Single series, wrap it in an array
14525
- normalizedData = [dataPoints];
14526
- }
14527
- }
14528
- }
14529
- // Early return if no data
14530
- if (normalizedData.length === 0 || normalizedData.flat().length === 0) {
14531
- this.showNoDataMessage(containerElement);
14532
- return;
14533
- }
14534
- // Get options and setup dimensions
14535
- const options = this.options();
14536
- this.setupDimensions(containerElement, options);
14537
- // Flatten data for scale calculation
14538
- const flatData = normalizedData.flat();
14539
- // Create scales with the flattened data
14540
- this.setupScales(flatData);
14541
- this.createAxes(options);
14542
- // Render the lines using the structured data (array of arrays)
14543
- this.renderLines(normalizedData);
14544
- }
14545
- updateChart() {
14546
- this.createChart();
14547
- }
14548
- cleanupChart() {
14549
- if (this.svg) {
14550
- this.d3.select(this.chartContainerEl()?.nativeElement).selectAll('svg').remove();
14551
- this.svg = null;
14552
- this.chart = null;
14553
- }
14554
- this._tooltipVisible.set(false);
14555
- }
14556
- // Private chart creation methods
14557
- /**
14558
- * Sets up chart dimensions and creates SVG with responsive attributes
14559
- */
14560
- setupDimensions(containerElement, options) {
14561
- // Get container dimensions
14562
- const containerWidth = containerElement.clientWidth;
14563
- const containerHeight = containerElement.clientHeight;
14564
- // If options specify width and height, use those, otherwise default to container size
14565
- const minDim = Math.min(200, containerWidth, containerHeight); // Ensure reasonable minimum
14566
- if (options.width && options.height) {
14567
- // Explicit dimensions provided
14568
- this.width = options.width - this.margin.left - this.margin.right;
14569
- this.height = options.height - this.margin.top - this.margin.bottom;
14570
- }
14571
- else {
14572
- // Responsive dimensions
14573
- this.width = Math.max(containerWidth, minDim) - this.margin.left - this.margin.right;
14574
- this.height = Math.max(containerHeight, minDim) - this.margin.top - this.margin.bottom;
14575
- }
14576
- // Create responsive SVG that scales with its container
14577
- const svg = this.d3
14578
- .select(containerElement)
14579
- .append('svg')
14580
- .attr('width', '100%')
14581
- .attr('height', '100%')
14582
- .attr('viewBox', `0 0 ${this.width + this.margin.left + this.margin.right} ${this.height + this.margin.top + this.margin.bottom}`)
14583
- .attr('preserveAspectRatio', 'xMidYMid meet');
14584
- this.svg = svg;
14585
- // Create chart group with margins
14586
- this.chart = this.svg.append('g').attr('transform', `translate(${this.margin.left},${this.margin.top})`);
14587
- }
14588
- /**
14589
- * Creates x and y scales for the chart
14590
- */
14591
- setupScales(data) {
14592
- // Determine x-axis type based on data
14593
- const allNumericX = data.every((d) => typeof d.xValue === 'number');
14594
- const allDates = data.every((d) => !isNaN(new Date(d.xValue).getTime()));
14595
- // Create appropriate x scale based on data type
14596
- if (allNumericX) {
14597
- // Numeric x-axis
14598
- this.xScale = this.d3
14599
- .scaleLinear()
14600
- .domain([
14601
- this.d3.min(data, (d) => d.xValue) || 0,
14602
- this.d3.max(data, (d) => d.xValue) || 0,
14603
- ])
14604
- .range([0, this.width]);
14605
- }
14606
- else if (allDates) {
14607
- // Date x-axis
14608
- this.xScale = this.d3
14609
- .scaleTime()
14610
- .domain([
14611
- this.d3.min(data, (d) => new Date(d.xValue)) || new Date(),
14612
- this.d3.max(data, (d) => new Date(d.xValue)) || new Date(),
14613
- ])
14614
- .range([0, this.width]);
14615
- }
14616
- else {
14617
- // Categorical x-axis
14618
- this.xScale = this.d3
14619
- .scaleBand()
14620
- .domain(data.map((d) => d.xValue.toString()))
14621
- .range([0, this.width])
14622
- .padding(0.1);
14623
- }
14624
- // Create y scale (linear scale for values)
14625
- this.yScale = this.d3
14626
- .scaleLinear()
14627
- .domain([0, this.d3.max(data, (d) => d.value) || 0])
14628
- .nice()
14629
- .range([this.height, 0]);
14630
- }
14631
- /**
14632
- * Creates x and y axes with grid lines
14633
- */
14634
- createAxes(options) {
14635
- // Only create axes if they are enabled in options
14636
- const showXAxis = options.showXAxis !== false;
14637
- const showYAxis = options.showYAxis !== false;
14638
- const showGrid = options.showGrid !== false;
14639
- if (showXAxis) {
14640
- // Create X axis
14641
- this.xAxis = this.chart
14642
- .append('g')
14643
- .attr('class', 'axp-line-chart-axis-x')
14644
- .attr('transform', `translate(0,${this.height})`)
14645
- .call(this.d3.axisBottom(this.xScale));
14646
- }
14647
- if (showYAxis) {
14648
- // Create Y axis
14649
- this.yAxis = this.chart.append('g').attr('class', 'axp-line-chart-axis-y').call(this.d3.axisLeft(this.yScale));
14650
- }
14651
- if (showGrid) {
14652
- // Add horizontal grid lines
14653
- this.chart
14654
- .append('g')
14655
- .attr('class', 'axp-line-chart-grid')
14656
- .call(this.d3
14657
- .axisLeft(this.yScale)
14658
- .tickSize(-this.width)
14659
- .tickFormat(() => ''))
14660
- .selectAll('.tick')
14661
- .style('color', 'rgb(153 153 153 / 30%)'); // Add gray color to ticks
14662
- }
14663
- }
14664
- /**
14665
- * Renders the lines with animations
14666
- */
14667
- renderLines(data) {
14668
- // Get line options
14669
- const lineWidth = this.lineWidth();
14670
- const useSmooth = this.smoothLine();
14671
- const shouldFill = this.fillArea();
14672
- const fillOpacity = this.fillOpacity();
14673
- // Create line generator
14674
- const getX = (d) => {
14675
- // Handle different x scale types
14676
- if (this.xScale.bandwidth) {
14677
- // band scale for categorical
14678
- return this.xScale(d.xValue.toString()) + this.xScale.bandwidth() / 2;
14679
- }
14680
- else {
14681
- // linear or time scale
14682
- return this.xScale(d.xValue);
14683
- }
14684
- };
14685
- // Define line generator function
14686
- const lineGenerator = useSmooth
14687
- ? this.d3
14688
- .line()
14689
- .x(getX)
14690
- .y((d) => this.yScale(d.value))
14691
- .curve(this.d3.curveMonotoneX) // Smooth curve
14692
- : this.d3
14693
- .line()
14694
- .x(getX)
14695
- .y((d) => this.yScale(d.value))
14696
- .curve(this.d3.curveLinear); // Straight lines
14697
- // Define area generator if we should fill
14698
- const areaGenerator = useSmooth
14699
- ? this.d3
14700
- .area()
14701
- .x(getX)
14702
- .y0(this.height)
14703
- .y1((d) => this.yScale(d.value))
14704
- .curve(this.d3.curveMonotoneX)
14705
- : this.d3
14706
- .area()
14707
- .x(getX)
14708
- .y0(this.height)
14709
- .y1((d) => this.yScale(d.value))
14710
- .curve(this.d3.curveLinear);
14711
- // Render each series
14712
- data.forEach((seriesData, seriesIndex) => {
14713
- // Skip empty series
14714
- if (!seriesData.length)
14715
- return;
14716
- // Sort data within series by x value
14717
- seriesData.sort((a, b) => {
14718
- if (typeof a.xValue === 'number' && typeof b.xValue === 'number') {
14719
- return a.xValue - b.xValue;
14720
- }
14721
- else {
14722
- return a.xValue.toString().localeCompare(b.xValue.toString());
14723
- }
14724
- });
14725
- // Get series name and color
14726
- const seriesName = seriesData[0].seriesName || `Series ${seriesIndex + 1}`;
14727
- // Get color for this series - prefer the color from data point, otherwise use palette
14728
- const seriesColor = seriesData[0]?.color || AXPChartColors.getColor(seriesIndex);
14729
- // Draw the area if fill is enabled
14730
- if (shouldFill) {
14731
- this.chart
14732
- .append('path')
14733
- .datum(seriesData)
14734
- .attr('class', 'axp-line-chart-area')
14735
- .attr('fill', seriesColor)
14736
- .attr('fill-opacity', fillOpacity)
14737
- .attr('d', areaGenerator);
14738
- }
14739
- // Draw the line with animation
14740
- const path = this.chart
14741
- .append('path')
14742
- .datum(seriesData)
14743
- .attr('class', 'axp-line-chart-line')
14744
- .attr('stroke', seriesColor)
14745
- .attr('stroke-width', lineWidth)
14746
- .attr('fill', 'none') // Ensure no fill on the line
14747
- .attr('d', lineGenerator);
14748
- // Animate the line drawing
14749
- const pathLength = path.node().getTotalLength();
14750
- path
14751
- .attr('stroke-dasharray', pathLength)
14752
- .attr('stroke-dashoffset', pathLength)
14753
- .transition()
14754
- .duration(1000)
14755
- .attr('stroke-dashoffset', 0)
14756
- .ease(this.d3.easeQuadOut);
14757
- // Add data points if enabled
14758
- if (this.showPoints()) {
14759
- this.chart
14760
- .selectAll(`.axp-line-chart-point-${seriesIndex}`)
14761
- .data(seriesData)
14762
- .enter()
14763
- .append('circle')
14764
- .attr('class', `axp-line-chart-point axp-line-chart-point-${seriesIndex}`)
14765
- .attr('cx', (d) => getX(d))
14766
- .attr('cy', (d) => this.yScale(d.value))
14767
- .attr('r', 0) // Start with radius 0 for animation
14768
- .attr('fill', seriesColor)
14769
- .attr('stroke', 'white')
14770
- .attr('stroke-width', 1)
14771
- .on('mouseenter', (event, d) => this.handlePointHover(event, d, seriesName))
14772
- .on('mousemove', (event) => this.updateTooltipPosition(event))
14773
- .on('mouseleave', () => this._tooltipVisible.set(false))
14774
- .on('click', (event, d) => this.handlePointClick(event, d))
14775
- .transition()
14776
- .delay((d, i) => i * 50 + 300) // Stagger with delay after line animation
14777
- .duration(300)
14778
- .attr('r', this.pointRadius())
14779
- .ease(this.d3.easeBackOut); // Bouncy animation
14780
- }
14781
- });
14782
- }
14783
- // Event handlers
14784
- /**
14785
- * Handles point hover event and shows tooltip
14786
- */
14787
- handlePointHover(event, datum, seriesName) {
14788
- if (this.options().showTooltip !== false) {
14789
- const data = this.chartData();
14790
- let index = 0;
14791
- // Find index based on data structure
14792
- if (Array.isArray(data)) {
14793
- if (Array.isArray(data[0])) {
14794
- // Multi-series data (array of arrays)
14795
- const flatData = data.flat();
14796
- index = flatData.findIndex((item) => item.id === datum.id);
14797
- }
14798
- else {
14799
- // Single series (array of data points)
14800
- index = data.findIndex((item) => item.id === datum.id);
14801
- }
14802
- }
14803
- const color = datum.color || AXPChartColors.getColor(index);
14804
- // Update tooltip data
14805
- this._tooltipData.set({
14806
- title: datum.label || seriesName,
14807
- value: datum.value.toString(),
14808
- percentage: '', // Not showing percentage for line charts
14809
- color,
14810
- });
14811
- // Position tooltip near the point
14812
- this.updateTooltipPosition(event);
14813
- this._tooltipVisible.set(true);
14814
- }
14815
- }
14816
- /**
14817
- * Updates tooltip position based on mouse coordinates
14818
- */
14819
- updateTooltipPosition(event) {
14820
- const rect = event.target.getBoundingClientRect();
14821
- const containerRect = this.chartContainerEl().nativeElement.getBoundingClientRect();
14822
- // Position relative to the container
14823
- const x = event.clientX - containerRect.left;
14824
- const y = event.clientY - containerRect.top;
14825
- this._tooltipPosition.set({ x, y });
14826
- }
14827
- /**
14828
- * Handles point click event
14829
- */
14830
- handlePointClick(event, datum) {
14831
- // Emit click event with the data point
14832
- this.pointClick.emit(datum);
14833
- }
14834
- /**
14835
- * Shows a message when no data is available
14836
- */
14837
- showNoDataMessage(containerElement) {
14838
- const noDataDiv = this.d3.select(containerElement).append('div').attr('class', 'axp-line-chart-no-data-message');
14839
- noDataDiv
14840
- .append('div')
14841
- .attr('class', 'axp-line-chart-no-data-icon')
14842
- .html('<i class="fa-light fa-chart-line fa-3x"></i>');
14843
- noDataDiv.append('div').attr('class', 'axp-line-chart-no-data-text').text('No data available');
14844
- }
14845
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: AXPLineChartWidgetViewComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
14846
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "19.0.3", type: AXPLineChartWidgetViewComponent, isStandalone: true, selector: "ng-component", outputs: { pointClick: "pointClick" }, viewQueries: [{ propertyName: "chartContainerEl", first: true, predicate: ["chartContainer"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"axp-line-chart\" #chartContainer>\n <!-- Shared tooltip component -->\n <ax-chart-tooltip\n [visible]=\"tooltipVisible()\"\n [position]=\"tooltipPosition()\"\n [data]=\"tooltipData()\"\n [showPercentage]=\"false\"\n ></ax-chart-tooltip>\n</div>\n", styles: [":host{display:block;width:100%;height:100%;min-height:200px}.axp-line-chart{width:100%;height:100%;position:relative;display:flex;align-items:center;justify-content:center;border-radius:.5rem;overflow:hidden}.axp-line-chart svg{width:100%;height:100%;max-width:100%;max-height:100%;overflow:visible}.axp-line-chart-line{fill:none;stroke-width:2px;stroke-linejoin:round;stroke-linecap:round;transition:stroke-width .3s ease}.axp-line-chart-line:hover{stroke-width:3px}.axp-line-chart-area{opacity:.2;transition:opacity .3s ease}.axp-line-chart-area:hover{opacity:.3}.axp-line-chart-point{cursor:pointer;transition:all .3s cubic-bezier(.4,0,.2,1)}.axp-line-chart-point:hover{r:6;stroke-width:2;stroke:var(--ax-background-color, #fff)}.axp-line-chart-axis-x path,.axp-line-chart-axis-y path{stroke:var(--ax-border-color, #e0e0e0)}.axp-line-chart-axis-x line,.axp-line-chart-axis-y line,.axp-line-chart-grid line{stroke:var(--ax-border-color, #e0e0e0);stroke-dasharray:2,2;stroke-opacity:.5}.axp-line-chart-grid path{stroke-width:0}.axp-line-chart-axis-x text,.axp-line-chart-axis-y text{fill:var(--ax-text-muted, #666);font-size:clamp(8px,2vmin,12px)}.axp-line-chart-no-data-message{font-family:var(--ax-font-family, system-ui, sans-serif);display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:1rem;width:100%;height:100%}.axp-line-chart-no-data-icon{margin-bottom:.75rem;color:var(--ax-text-muted, #999)}.axp-line-chart-no-data-text{font-weight:600;color:var(--ax-text-color, #333)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: AXPChartTooltipComponent, selector: "ax-chart-tooltip", inputs: ["data", "position", "visible", "showPercentage", "style"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
14847
- }
14848
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: AXPLineChartWidgetViewComponent, decorators: [{
14849
- type: Component,
14850
- args: [{ standalone: true, imports: [CommonModule, AXPChartTooltipComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"axp-line-chart\" #chartContainer>\n <!-- Shared tooltip component -->\n <ax-chart-tooltip\n [visible]=\"tooltipVisible()\"\n [position]=\"tooltipPosition()\"\n [data]=\"tooltipData()\"\n [showPercentage]=\"false\"\n ></ax-chart-tooltip>\n</div>\n", styles: [":host{display:block;width:100%;height:100%;min-height:200px}.axp-line-chart{width:100%;height:100%;position:relative;display:flex;align-items:center;justify-content:center;border-radius:.5rem;overflow:hidden}.axp-line-chart svg{width:100%;height:100%;max-width:100%;max-height:100%;overflow:visible}.axp-line-chart-line{fill:none;stroke-width:2px;stroke-linejoin:round;stroke-linecap:round;transition:stroke-width .3s ease}.axp-line-chart-line:hover{stroke-width:3px}.axp-line-chart-area{opacity:.2;transition:opacity .3s ease}.axp-line-chart-area:hover{opacity:.3}.axp-line-chart-point{cursor:pointer;transition:all .3s cubic-bezier(.4,0,.2,1)}.axp-line-chart-point:hover{r:6;stroke-width:2;stroke:var(--ax-background-color, #fff)}.axp-line-chart-axis-x path,.axp-line-chart-axis-y path{stroke:var(--ax-border-color, #e0e0e0)}.axp-line-chart-axis-x line,.axp-line-chart-axis-y line,.axp-line-chart-grid line{stroke:var(--ax-border-color, #e0e0e0);stroke-dasharray:2,2;stroke-opacity:.5}.axp-line-chart-grid path{stroke-width:0}.axp-line-chart-axis-x text,.axp-line-chart-axis-y text{fill:var(--ax-text-muted, #666);font-size:clamp(8px,2vmin,12px)}.axp-line-chart-no-data-message{font-family:var(--ax-font-family, system-ui, sans-serif);display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:1rem;width:100%;height:100%}.axp-line-chart-no-data-icon{margin-bottom:.75rem;color:var(--ax-text-muted, #999)}.axp-line-chart-no-data-text{font-weight:600;color:var(--ax-text-color, #333)}\n"] }]
14851
- }] });
14852
-
14853
- var lineChartWidget_component = /*#__PURE__*/Object.freeze({
14854
- __proto__: null,
14855
- AXPLineChartWidgetViewComponent: AXPLineChartWidgetViewComponent
14856
- });
14857
-
14858
- const AXPLineChartWidget = {
14859
- name: 'line-chart',
14860
- title: 'Line Chart Widget',
14861
- categories: [AXP_WIDGETS_CHART_CATEGORY],
14862
- groups: [AXPWidgetGroupEnum.DashboardWidget],
14863
- type: 'dashboard',
14864
- icon: 'fa-light fa-chart-line',
14865
- properties: [
14866
- // ====== Layout & Dimensions ======
14867
- {
14868
- name: 'height',
14869
- title: 'Height',
14870
- group: AXP_STYLING_PROPERTY_GROUP,
14871
- schema: {
14872
- defaultValue: 300,
14873
- dataType: 'number',
14874
- interface: {
14875
- name: 'height',
14876
- path: 'options.height',
14877
- type: AXPWidgetsCatalog.number,
14878
- options: {
14879
- minValue: 0,
14880
- maxValue: 800,
14881
- },
14882
- },
14883
- },
14884
- visible: true,
14885
- },
14886
- {
14887
- name: 'width',
14888
- title: 'Width',
14889
- group: AXP_STYLING_PROPERTY_GROUP,
14890
- schema: {
14891
- defaultValue: null,
14892
- dataType: 'number',
14893
- interface: {
14894
- name: 'width',
14895
- path: 'options.width',
14896
- type: AXPWidgetsCatalog.number,
14897
- options: {
14898
- minValue: 0,
14899
- maxValue: 1200,
14900
- },
14901
- },
14902
- },
14903
- visible: true,
14904
- },
14905
- // ====== Axis Settings ======
14906
- {
14907
- name: 'showXAxis',
14908
- title: 'Show X Axis',
14909
- group: AXP_APPEARANCE_PROPERTY_GROUP,
14910
- schema: {
14911
- defaultValue: true,
14912
- dataType: 'boolean',
14913
- interface: {
14914
- name: 'showXAxis',
14915
- path: 'options.showXAxis',
14916
- type: AXPWidgetsCatalog.toggle,
14917
- },
14918
- },
14919
- visible: true,
14920
- },
14921
- {
14922
- name: 'showYAxis',
14923
- title: 'Show Y Axis',
14924
- group: AXP_APPEARANCE_PROPERTY_GROUP,
14925
- schema: {
14926
- defaultValue: true,
14927
- dataType: 'boolean',
14928
- interface: {
14929
- name: 'showYAxis',
14930
- path: 'options.showYAxis',
14931
- type: AXPWidgetsCatalog.toggle,
14932
- },
14933
- },
14934
- visible: true,
14935
- },
14936
- {
14937
- name: 'showGrid',
14938
- title: 'Show Grid Lines',
14939
- group: AXP_APPEARANCE_PROPERTY_GROUP,
14940
- schema: {
14941
- defaultValue: true,
14942
- dataType: 'boolean',
14943
- interface: {
14944
- name: 'showGrid',
14945
- path: 'options.showGrid',
14946
- type: AXPWidgetsCatalog.toggle,
14947
- },
14948
- },
14949
- visible: true,
14950
- },
14951
- // ====== Tooltip Settings ======
14952
- {
14953
- name: 'showTooltip',
14954
- title: 'Show Tooltip',
14955
- group: AXP_APPEARANCE_PROPERTY_GROUP,
14956
- schema: {
14957
- defaultValue: true,
14958
- dataType: 'boolean',
14959
- interface: {
14960
- name: 'showTooltip',
14961
- path: 'options.showTooltip',
14962
- type: AXPWidgetsCatalog.toggle,
14963
- },
14964
- },
14965
- visible: true,
14966
- },
14967
- // ====== Line Appearance ======
14968
- {
14969
- name: 'lineWidth',
14970
- title: 'Line Width',
14971
- group: AXP_APPEARANCE_PROPERTY_GROUP,
14972
- schema: {
14973
- defaultValue: 2,
14974
- dataType: 'number',
14975
- interface: {
14976
- name: 'lineWidth',
14977
- path: 'options.lineWidth',
14978
- type: AXPWidgetsCatalog.number,
14979
- options: {
14980
- placeholder: '1-10',
14981
- minValue: 1,
14982
- maxValue: 10,
14983
- },
14984
- },
14985
- },
14986
- visible: true,
14987
- },
14988
- {
14989
- name: 'showPoints',
14990
- title: 'Show Points',
14991
- group: AXP_APPEARANCE_PROPERTY_GROUP,
14992
- schema: {
14993
- defaultValue: true,
14994
- dataType: 'boolean',
14995
- interface: {
14996
- name: 'showPoints',
14997
- path: 'options.showPoints',
14998
- type: AXPWidgetsCatalog.toggle,
14999
- },
15000
- },
15001
- visible: true,
15002
- },
15003
- {
15004
- name: 'pointRadius',
15005
- title: 'Point Size',
15006
- group: AXP_APPEARANCE_PROPERTY_GROUP,
15007
- schema: {
15008
- defaultValue: 4,
15009
- dataType: 'number',
15010
- interface: {
15011
- name: 'pointRadius',
15012
- path: 'options.pointRadius',
15013
- type: AXPWidgetsCatalog.number,
15014
- options: {
15015
- placeholder: '1-10',
15016
- minValue: 1,
15017
- maxValue: 10,
15018
- },
15019
- },
15020
- },
15021
- visible: true,
15022
- },
15023
- {
15024
- name: 'smoothLine',
15025
- title: 'Smooth Line',
15026
- group: AXP_APPEARANCE_PROPERTY_GROUP,
15027
- schema: {
15028
- defaultValue: true,
15029
- dataType: 'boolean',
15030
- interface: {
15031
- name: 'smoothLine',
15032
- path: 'options.smoothLine',
15033
- type: AXPWidgetsCatalog.toggle,
15034
- },
15035
- },
15036
- visible: true,
15037
- },
15038
- {
15039
- name: 'fillArea',
15040
- title: 'Fill Area',
15041
- group: AXP_APPEARANCE_PROPERTY_GROUP,
15042
- schema: {
15043
- defaultValue: false,
15044
- dataType: 'boolean',
15045
- interface: {
15046
- name: 'fillArea',
15047
- path: 'options.fillArea',
15048
- type: AXPWidgetsCatalog.toggle,
15049
- },
15050
- },
15051
- visible: true,
15052
- },
15053
- {
15054
- name: 'fillOpacity',
15055
- title: 'Fill Opacity',
15056
- group: AXP_APPEARANCE_PROPERTY_GROUP,
15057
- schema: {
15058
- defaultValue: 10,
15059
- dataType: 'number',
15060
- interface: {
15061
- name: 'fillOpacity',
15062
- path: 'options.fillOpacity',
15063
- type: AXPWidgetsCatalog.number,
15064
- options: {
15065
- placeholder: '0-100',
15066
- minValue: 0,
15067
- maxValue: 100,
15068
- },
15069
- },
15070
- },
15071
- visible: true,
15072
- },
15073
- ],
15074
- components: {
15075
- view: {
15076
- component: () => Promise.resolve().then(function () { return lineChartWidget_component; }).then((c) => c.AXPLineChartWidgetViewComponent),
15077
- },
15078
- },
15079
- meta: {
15080
- dimensions: {
15081
- width: 5,
15082
- height: 6,
15083
- minWidth: 2,
15084
- minHeight: 2,
15085
- maxWidth: 6,
15086
- maxHeight: 7,
15087
- },
15088
- },
15089
- };
15090
-
15091
- /**
15092
- * Notification Widget Component
15093
- * Displays notifications in a card with tabs
15094
- */
15095
- class AXPNotificationWidgetViewComponent extends AXPValueWidgetComponent {
15096
- constructor() {
15097
- super(...arguments);
15098
- // Outputs
15099
- this.notificationClick = output();
15100
- this.markAsRead = output();
15101
- // Dependencies
15102
- this.cdr = inject(ChangeDetectorRef);
15103
- this.datePipe = inject(DatePipe);
15104
- // State
15105
- this.activeTab = signal('new');
15106
- // Configuration
15107
- this.maxItems = computed(() => this.options()?.maxItems ?? 10);
15108
- this.showAvatar = computed(() => this.options()?.showAvatar ?? true);
15109
- this.showDate = computed(() => this.options()?.showDate ?? true);
15110
- // Computed data
15111
- this.notificationItems = computed(() => {
15112
- const value = this.getValue();
15113
- if (!value?.data?.length)
15114
- return [];
15115
- // Filter by active tab
15116
- const filtered = this.activeTab() === 'new' ? value.data.filter((n) => !n.readAt) : value.data;
15117
- return filtered.slice(0, this.maxItems());
15118
- });
15119
- }
15120
- /**
15121
- * Get the count of new messages for the badge
15122
- */
15123
- getNewMessageCount() {
15124
- const value = this.getValue();
15125
- if (!value?.data?.length)
15126
- return 0;
15127
- return value.data.filter((n) => !n.readAt).length;
15128
- }
15129
- /**
15130
- * Handle tab change event from ax-tabs component
15131
- * @param index The index of the tab that was activated
15132
- */
15133
- handleTabChange(data) {
15134
- const index = data.index;
15135
- // Map index to tab name: 0 = 'new', 1 = 'all'
15136
- const tabName = index === 0 ? 'new' : 'all';
15137
- this.onTabChange(tabName);
15138
- }
15139
- /**
15140
- * Mark all notifications as read
15141
- */
15142
- markAllAsRead() {
15143
- const value = this.getValue();
15144
- if (!value?.data?.length)
15145
- return;
15146
- const now = new Date();
15147
- const updatedNotifications = value.data.map((n) => {
15148
- if (n.readAt)
15149
- return n;
15150
- return { ...n, readAt: now };
15151
- });
15152
- this.setValue({
15153
- ...value,
15154
- data: updatedNotifications,
15155
- });
15156
- this.cdr.detectChanges();
15157
- }
15158
- /**
15159
- * Change the active tab
15160
- */
15161
- onTabChange(tabName) {
15162
- this.activeTab.set(tabName);
15163
- this.cdr.detectChanges();
15164
- }
15165
- /**
15166
- * Handle notification click event
15167
- */
15168
- onNotificationClick(notification) {
15169
- this.markAsReadIfNeeded(notification);
15170
- this.notificationClick.emit(notification);
15171
- }
15172
- /**
15173
- * Format the timestamp in a user-friendly way
15174
- */
15175
- formatTime(date) {
15176
- if (!date)
15177
- return '';
15178
- const dateObj = typeof date === 'string' ? new Date(date) : date;
15179
- const diffDays = this.getDaysDifference(dateObj);
15180
- // Format based on how recent the date is
15181
- if (diffDays === 0)
15182
- return this.datePipe.transform(dateObj, 'h:mm a') || ''; // Today
15183
- if (diffDays === 1)
15184
- return 'Yesterday';
15185
- if (diffDays < 7)
15186
- return this.datePipe.transform(dateObj, 'EEE') || ''; // Day of week
15187
- return this.datePipe.transform(dateObj, 'MM/dd/yyyy') || ''; // Older date
15188
- }
15189
- /**
15190
- * Mark notification as read if needed
15191
- */
15192
- markAsReadIfNeeded(notification) {
15193
- // Only mark as read if not already read
15194
- if (notification.readAt)
15195
- return;
15196
- const updatedNotification = {
15197
- ...notification,
15198
- readAt: new Date(),
15199
- };
15200
- // Update the model
15201
- const value = this.getValue();
15202
- if (!value?.data)
15203
- return;
15204
- const updatedNotifications = value.data.map((n) => (n.id === notification.id ? updatedNotification : n));
15205
- this.setValue({
15206
- ...value,
15207
- data: updatedNotifications,
15208
- });
15209
- // Notify about the change
15210
- this.markAsRead.emit(updatedNotification);
15211
- }
15212
- /**
15213
- * Calculate days difference from now
15214
- */
15215
- getDaysDifference(date) {
15216
- const now = new Date();
15217
- const diffMs = now.getTime() - date.getTime();
15218
- return Math.floor(diffMs / (1000 * 60 * 60 * 24));
15219
- }
15220
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: AXPNotificationWidgetViewComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
15221
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.3", type: AXPNotificationWidgetViewComponent, isStandalone: true, selector: "ng-component", outputs: { notificationClick: "notificationClick", markAsRead: "markAsRead" }, providers: [DatePipe], usesInheritance: true, ngImport: i0, template: "<div class=\"ax-p-4 ax-size-full\">\n <ax-tabs\n class=\"ax-bg-light-start ax-border-b ax-border-default\"\n [fitParent]=\"true\"\n location=\"bottom\"\n (onActiveTabChanged)=\"handleTabChange($event)\"\n >\n <ax-tab-item [text]=\"('widget.notification.new' | translate | async) ?? 'New'\" class=\"ax-font-medium\">\n <ax-suffix>\n @if (getNewMessageCount() > 0) {\n <ax-badge color=\"primary\" [text]=\"getNewMessageCount().toString()\" size=\"sm\" class=\"ax-ml-1\"></ax-badge>\n }\n </ax-suffix>\n </ax-tab-item>\n <ax-tab-item [text]=\"('widget.notification.all' | translate | async) ?? 'All'\" class=\"ax-font-medium\"></ax-tab-item>\n </ax-tabs>\n <div class=\"ax-space-y-4 ax-mt-4 ax-px-2\">\n @for (item of notificationItems(); track item.id) {\n <ng-container [ngTemplateOutlet]=\"chatItemTemplateRef\" [ngTemplateOutletContext]=\"{ $implicit: item }\">\n </ng-container>\n } @empty {\n <div class=\"ax-flex ax-flex-col ax-items-center ax-justify-center ax-py-12 ax-px-4 ax-text-gray-400\">\n <ax-icon class=\"ax-text-4xl ax-mb-3 ax-text-gray-300\">\n <i class=\"fa-light fa-bell-slash\"></i>\n </ax-icon>\n <p class=\"ax-text-center\">{{ 'widget.notification.noNotifications' | translate | async }}</p>\n </div>\n }\n </div>\n</div>\n\n<ng-template #chatItemTemplateRef let-data>\n <div class=\"ax-flex ax-gap-3\">\n @if(showAvatar()){\n <div class=\"ax-rounded-full ax-size-10\">\n <ax-avatar shape=\"rounded\" class=\"ax-shrink-0\" [size]=\"40\">\n @if(data.user?.image){\n <ax-image\n [src]=\"data.user.image\"\n [alt]=\"data.user?.name || ('widget.notification.user' | translate | async)\"\n ></ax-image>\n } @else {\n <ax-icon>\n <i class=\"fa-light fa-user\"></i>\n </ax-icon>\n }\n </ax-avatar>\n </div>\n }\n <div class=\"ax-overflow-hidden ax-grow ax-text-start\">\n <h6 class=\"ax-pb-2 ax-font-semibold ax-truncate\">{{ data.user?.name || data.title }}</h6>\n <p class=\"ax-text-xs ax-truncate\">{{ data.body }}</p>\n </div>\n @if(showDate()){\n <div class=\"ax-text-xs ax-shrink-0\">\n <span>\n {{ formatTime(data.createdAt) }}\n </span>\n </div>\n }\n </div>\n</ng-template>\n", styles: [":host{display:block;width:100%;height:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "ngmodule", type: AXTabsModule }, { kind: "component", type: i4$1.AXTabsComponent, selector: "ax-tabs", inputs: ["look", "location", "fitParent", "minWidth", "content"], outputs: ["onActiveTabChanged"] }, { kind: "component", type: i4$1.AXTabItemComponent, selector: "ax-tab-item", inputs: ["disabled", "text", "key", "headerTemplate", "active"], outputs: ["disabledChange", "onClick", "onBlur", "onFocus", "activeChange"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i3.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i3.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXButtonModule }, { kind: "ngmodule", type: AXBadgeModule }, { kind: "component", type: i1$2.AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }, { kind: "ngmodule", type: AXAvatarModule }, { kind: "component", type: i5$4.AXAvatarComponent, selector: "ax-avatar", inputs: ["color", "size", "shape", "look"], outputs: ["sizeChange"] }, { kind: "ngmodule", type: AXImageModule }, { kind: "component", type: i2$7.AXImageComponent, selector: "ax-image", inputs: ["width", "height", "overlayMode", "src", "alt", "priority", "lazy"], outputs: ["onLoad", "onError"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "pipe", type: i5$1.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
15222
- }
15223
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: AXPNotificationWidgetViewComponent, decorators: [{
15224
- type: Component,
15225
- args: [{ standalone: true, imports: [
15226
- CommonModule,
15227
- AXTabsModule,
15228
- AXDecoratorModule,
15229
- AXButtonModule,
15230
- AXBadgeModule,
15231
- AXAvatarModule,
15232
- AXImageModule,
15233
- AXTranslationModule,
15234
- ], providers: [DatePipe], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"ax-p-4 ax-size-full\">\n <ax-tabs\n class=\"ax-bg-light-start ax-border-b ax-border-default\"\n [fitParent]=\"true\"\n location=\"bottom\"\n (onActiveTabChanged)=\"handleTabChange($event)\"\n >\n <ax-tab-item [text]=\"('widget.notification.new' | translate | async) ?? 'New'\" class=\"ax-font-medium\">\n <ax-suffix>\n @if (getNewMessageCount() > 0) {\n <ax-badge color=\"primary\" [text]=\"getNewMessageCount().toString()\" size=\"sm\" class=\"ax-ml-1\"></ax-badge>\n }\n </ax-suffix>\n </ax-tab-item>\n <ax-tab-item [text]=\"('widget.notification.all' | translate | async) ?? 'All'\" class=\"ax-font-medium\"></ax-tab-item>\n </ax-tabs>\n <div class=\"ax-space-y-4 ax-mt-4 ax-px-2\">\n @for (item of notificationItems(); track item.id) {\n <ng-container [ngTemplateOutlet]=\"chatItemTemplateRef\" [ngTemplateOutletContext]=\"{ $implicit: item }\">\n </ng-container>\n } @empty {\n <div class=\"ax-flex ax-flex-col ax-items-center ax-justify-center ax-py-12 ax-px-4 ax-text-gray-400\">\n <ax-icon class=\"ax-text-4xl ax-mb-3 ax-text-gray-300\">\n <i class=\"fa-light fa-bell-slash\"></i>\n </ax-icon>\n <p class=\"ax-text-center\">{{ 'widget.notification.noNotifications' | translate | async }}</p>\n </div>\n }\n </div>\n</div>\n\n<ng-template #chatItemTemplateRef let-data>\n <div class=\"ax-flex ax-gap-3\">\n @if(showAvatar()){\n <div class=\"ax-rounded-full ax-size-10\">\n <ax-avatar shape=\"rounded\" class=\"ax-shrink-0\" [size]=\"40\">\n @if(data.user?.image){\n <ax-image\n [src]=\"data.user.image\"\n [alt]=\"data.user?.name || ('widget.notification.user' | translate | async)\"\n ></ax-image>\n } @else {\n <ax-icon>\n <i class=\"fa-light fa-user\"></i>\n </ax-icon>\n }\n </ax-avatar>\n </div>\n }\n <div class=\"ax-overflow-hidden ax-grow ax-text-start\">\n <h6 class=\"ax-pb-2 ax-font-semibold ax-truncate\">{{ data.user?.name || data.title }}</h6>\n <p class=\"ax-text-xs ax-truncate\">{{ data.body }}</p>\n </div>\n @if(showDate()){\n <div class=\"ax-text-xs ax-shrink-0\">\n <span>\n {{ formatTime(data.createdAt) }}\n </span>\n </div>\n }\n </div>\n</ng-template>\n", styles: [":host{display:block;width:100%;height:100%}\n"] }]
15235
- }] });
15236
-
15237
- var notificationWidget_component = /*#__PURE__*/Object.freeze({
15238
- __proto__: null,
15239
- AXPNotificationWidgetViewComponent: AXPNotificationWidgetViewComponent
15240
- });
15241
-
15242
- /**
15243
- * Configuration for the Notification Widget
15244
- */
15245
- const AXPNotificationWidget = {
15246
- name: 'notification',
15247
- title: 'Notification Widget',
15248
- categories: [AXP_WIDGETS_UTILITY_CATEGORY],
15249
- groups: [AXPWidgetGroupEnum.DashboardWidget],
15250
- type: 'dashboard',
15251
- description: 'Displays notifications in a widget format',
15252
- icon: 'fa-regular fa-bell',
15253
- properties: [
15254
- cloneProperty(AXP_DATA_PATH_PROPERTY, { visible: false }),
15255
- {
15256
- name: 'maxItems',
15257
- title: 'Max Items',
15258
- description: 'Maximum number of notification items to display',
15259
- group: AXP_STYLING_PROPERTY_GROUP,
15260
- schema: {
15261
- defaultValue: 10,
15262
- dataType: 'number',
15263
- interface: {
15264
- name: 'maxItems',
15265
- path: 'options.maxItems',
15266
- type: AXPWidgetsCatalog.number,
15267
- options: {
15268
- minValue: 1,
15269
- maxValue: 100,
15270
- },
15271
- },
15272
- },
15273
- visible: true,
15274
- },
15275
- {
15276
- name: 'showAvatar',
15277
- title: 'Show Avatar',
15278
- description: 'Whether to show avatars in notifications',
15279
- group: AXP_APPEARANCE_PROPERTY_GROUP,
15280
- schema: {
15281
- defaultValue: true,
15282
- dataType: 'boolean',
15283
- interface: {
15284
- name: 'showAvatar',
15285
- path: 'options.showAvatar',
15286
- type: AXPWidgetsCatalog.toggle,
15287
- },
15288
- },
15289
- visible: true,
15290
- },
15291
- {
15292
- name: 'showDate',
15293
- title: 'Show Date',
15294
- description: 'Whether to show date in notifications',
15295
- group: AXP_APPEARANCE_PROPERTY_GROUP,
15296
- schema: {
15297
- defaultValue: true,
15298
- dataType: 'boolean',
15299
- interface: {
15300
- name: 'showDate',
15301
- path: 'options.showDate',
15302
- type: AXPWidgetsCatalog.toggle,
15303
- },
15304
- },
15305
- visible: true,
15306
- },
15307
- ],
15308
- components: {
15309
- view: {
15310
- component: () => Promise.resolve().then(function () { return notificationWidget_component; }).then((c) => c.AXPNotificationWidgetViewComponent),
15311
- },
15312
- },
15313
- meta: {
15314
- dimensions: {
15315
- width: 3,
15316
- height: 5,
15317
- minWidth: 2,
15318
- minHeight: 4,
15319
- maxWidth: 4,
15320
- maxHeight: 7,
15321
- },
15322
- },
15323
- };
15324
-
15325
- class AXPStickyNoteWidgetViewComponent extends AXPValueWidgetComponent {
15326
- constructor() {
15327
- super(...arguments);
15328
- this.isEditing = signal(false);
15329
- this.wysiwyg = viewChild('wysiwyg');
15330
- this.value = computed(() => this.getValue());
15331
- this.date = computed(() => this.options()?.date ?? new Date());
15332
- this.bgColor = computed(() => this.options()?.backgroundColor ?? '#FFF8B8');
15333
- this.color = signal('#333333');
15334
- this.el = inject(ElementRef);
15335
- // Modern color palette with pastel and vibrant options
15336
- this.colorPresets = [
15337
- '#FFF8B8', // Soft yellow
15338
- '#FFD8E6', // Soft pink
15339
- '#D1F0FF', // Soft blue
15340
- '#E2FFD1', // Soft green
15341
- '#FFE8D1', // Soft orange
15342
- '#F0D1FF', // Soft purple
15343
- '#FFCDD2', // Soft red
15344
- '#D1FFF0', // Soft teal
15345
- '#F5F5F5', // Light gray
15346
- '#FFFFFF', // White
15347
- ];
15348
- }
15349
- // Handle clicks outside the component to cancel editing
15350
- handleClickOutside(event) {
15351
- const clickedInside = this.el.nativeElement.contains(event.target);
15352
- if (!clickedInside && this.isEditing()) {
15353
- this.saveChanges();
15354
- }
15355
- }
15356
- // Handle double-click to activate editing
15357
- activateEdit() {
15358
- //TODO FOCUS WYSIWYG
15359
- this.wysiwyg()?.focus();
15360
- this.isEditing.set(true);
15361
- }
15362
- // Save changes and exit edit mode
15363
- saveChanges() {
15364
- this.isEditing.set(false);
15365
- }
15366
- setColor(color) {
15367
- this.setOptions({ backgroundColor: color });
15368
- }
15369
- valueChange(event) {
15370
- if (event.isUserInteraction) {
15371
- this.setValue(event.value);
15372
- }
15373
- }
15374
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: AXPStickyNoteWidgetViewComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
15375
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.3", type: AXPStickyNoteWidgetViewComponent, isStandalone: true, selector: "ng-component", host: { listeners: { "document:click": "handleClickOutside($event)" } }, providers: [
15376
- {
15377
- provide: AXGridLayoutWidgetComponent,
15378
- useExisting: AXPStickyNoteWidgetViewComponent,
15379
- },
15380
- ], viewQueries: [{ propertyName: "wysiwyg", first: true, predicate: ["wysiwyg"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div\n class=\"sticky-note-container ax-size-full ax-rounded-lg ax-flex ax-flex-col ax-p-4 ax-shadow-md ax-transition-all ax-duration-300 hover:ax-shadow-lg\"\n [style.background-color]=\"bgColor()\"\n [style.color]=\"color()\"\n [class.ax-shadow-lg]=\"isEditing()\"\n [class.ax-scale-[1.02]]=\"isEditing()\"\n (dblclick)=\"activateEdit()\"\n>\n <!-- Header with timestamp -->\n <div class=\"ax-flex ax-justify-between ax-items-center ax-mb-3\">\n <div class=\"ax-text-xs ax-opacity-70 ax-font-medium ax-flex ax-items-center ax-gap-1\">\n <i class=\"fa-regular fa-clock ax-text-[0.65rem]\"></i>\n {{ date() | format : 'datetime' : 'dd MMM, YY HH:mm' | async }}\n </div>\n </div>\n\n <!-- Content area -->\n <div class=\"ax-flex-1 ax-overflow-auto\">\n <ax-wysiwyg-container\n #wysiwyg\n [class]=\"isEditing() ? 'ax-pointer-events-auto ax-cursor-text' : 'ax-pointer-events-none !ax-cursor-pointer'\"\n class=\"ax-h-full\"\n placeHolder=\"start writing with double click...\"\n look=\"none\"\n (onValueChanged)=\"valueChange($event)\"\n [ngModel]=\"value()\"\n >\n <ax-wysiwyg-view class=\"!ax-size-full ax-border-b-0\"></ax-wysiwyg-view>\n </ax-wysiwyg-container>\n </div>\n\n <!-- Footer with color selector and save button -->\n @if (isEditing()) {\n <div class=\"ax-absolute ax-bottom-2 ax-left-2 ax-right-2 ax-flex ax-flex-wrap ax-gap-1 ax-text-xs\">\n <!-- Color selection bar when in edit mode -->\n <div class=\"ax-flex ax-w-full ax-mt-2 ax-border ax-border-gray-200 ax-overflow-hidden ax-rounded-md\">\n @for (preset of colorPresets; track preset) {\n <div\n class=\"ax-h-6 ax-flex-1 ax-cursor-pointer ax-transition-all ax-duration-200 ax-border-r ax-border-gray-200 last:ax-border-r-0 hover:ax-brightness-95\"\n [style.background-color]=\"preset\"\n [class.ax-ring-inset]=\"bgColor() === preset\"\n [class.ax-ring-2]=\"bgColor() === preset\"\n [class.ax-ring-primary]=\"bgColor() === preset\"\n (click)=\"setColor(preset)\"\n ></div>\n }\n </div>\n </div>\n }\n</div>\n", styles: [":host{display:block;width:100%;height:100%}:host ::ng-deep ax-wysiwyg-view{color:#2e2e2e!important}\n"], dependencies: [{ kind: "ngmodule", type: AXWysiwygModule }, { kind: "component", type: i2$3.AXWysiwygContainerComponent, selector: "ax-wysiwyg-container", inputs: ["look", "placeHolder"], outputs: ["onValueChanged"] }, { kind: "component", type: i2$3.AXWysiwygViewComponent, selector: "ax-wysiwyg-view", inputs: ["class"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "ngmodule", type: AXToolBarModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: AXDateTimeModule }, { kind: "ngmodule", type: AXFormatModule }, { kind: "pipe", type: i2$9.AXFormatPipe, name: "format" }, { kind: "ngmodule", type: AXPopoverModule }, { kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "ngmodule", type: AXColorBoxModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
15381
- }
15382
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: AXPStickyNoteWidgetViewComponent, decorators: [{
15383
- type: Component,
15384
- args: [{ standalone: true, imports: [
15385
- AXWysiwygModule,
15386
- AXDecoratorModule,
15387
- AXToolBarModule,
15388
- FormsModule,
15389
- AXDateTimeModule,
15390
- AXFormatModule,
15391
- AXPopoverModule,
15392
- CommonModule,
15393
- FormsModule,
15394
- AXColorBoxModule,
15395
- ], changeDetection: ChangeDetectionStrategy.OnPush, providers: [
15396
- {
15397
- provide: AXGridLayoutWidgetComponent,
15398
- useExisting: AXPStickyNoteWidgetViewComponent,
15399
- },
15400
- ], template: "<div\n class=\"sticky-note-container ax-size-full ax-rounded-lg ax-flex ax-flex-col ax-p-4 ax-shadow-md ax-transition-all ax-duration-300 hover:ax-shadow-lg\"\n [style.background-color]=\"bgColor()\"\n [style.color]=\"color()\"\n [class.ax-shadow-lg]=\"isEditing()\"\n [class.ax-scale-[1.02]]=\"isEditing()\"\n (dblclick)=\"activateEdit()\"\n>\n <!-- Header with timestamp -->\n <div class=\"ax-flex ax-justify-between ax-items-center ax-mb-3\">\n <div class=\"ax-text-xs ax-opacity-70 ax-font-medium ax-flex ax-items-center ax-gap-1\">\n <i class=\"fa-regular fa-clock ax-text-[0.65rem]\"></i>\n {{ date() | format : 'datetime' : 'dd MMM, YY HH:mm' | async }}\n </div>\n </div>\n\n <!-- Content area -->\n <div class=\"ax-flex-1 ax-overflow-auto\">\n <ax-wysiwyg-container\n #wysiwyg\n [class]=\"isEditing() ? 'ax-pointer-events-auto ax-cursor-text' : 'ax-pointer-events-none !ax-cursor-pointer'\"\n class=\"ax-h-full\"\n placeHolder=\"start writing with double click...\"\n look=\"none\"\n (onValueChanged)=\"valueChange($event)\"\n [ngModel]=\"value()\"\n >\n <ax-wysiwyg-view class=\"!ax-size-full ax-border-b-0\"></ax-wysiwyg-view>\n </ax-wysiwyg-container>\n </div>\n\n <!-- Footer with color selector and save button -->\n @if (isEditing()) {\n <div class=\"ax-absolute ax-bottom-2 ax-left-2 ax-right-2 ax-flex ax-flex-wrap ax-gap-1 ax-text-xs\">\n <!-- Color selection bar when in edit mode -->\n <div class=\"ax-flex ax-w-full ax-mt-2 ax-border ax-border-gray-200 ax-overflow-hidden ax-rounded-md\">\n @for (preset of colorPresets; track preset) {\n <div\n class=\"ax-h-6 ax-flex-1 ax-cursor-pointer ax-transition-all ax-duration-200 ax-border-r ax-border-gray-200 last:ax-border-r-0 hover:ax-brightness-95\"\n [style.background-color]=\"preset\"\n [class.ax-ring-inset]=\"bgColor() === preset\"\n [class.ax-ring-2]=\"bgColor() === preset\"\n [class.ax-ring-primary]=\"bgColor() === preset\"\n (click)=\"setColor(preset)\"\n ></div>\n }\n </div>\n </div>\n }\n</div>\n", styles: [":host{display:block;width:100%;height:100%}:host ::ng-deep ax-wysiwyg-view{color:#2e2e2e!important}\n"] }]
15401
- }], propDecorators: { handleClickOutside: [{
15402
- type: HostListener,
15403
- args: ['document:click', ['$event']]
15404
- }] } });
15405
-
15406
- var stickyNoteWidget_component = /*#__PURE__*/Object.freeze({
15407
- __proto__: null,
15408
- AXPStickyNoteWidgetViewComponent: AXPStickyNoteWidgetViewComponent
15409
- });
15410
-
15411
- const AXPStickyNoteWidget = {
15412
- name: 'sticky-note',
15413
- title: 'Sticky Note Widget',
15414
- categories: AXP_WIDGETS_UTILITY_CATEGORY,
15415
- groups: [AXPWidgetGroupEnum.DashboardWidget],
15416
- type: 'dashboard',
15417
- icon: 'fa-light fa-sticky-note',
15418
- properties: [AXP_DATA_PATH_PROPERTY, AXP_BG_COLOR_PROPERTY, plainTextDefaultProperty()],
15419
- components: {
15420
- view: {
15421
- component: () => Promise.resolve().then(function () { return stickyNoteWidget_component; }).then((c) => c.AXPStickyNoteWidgetViewComponent),
15422
- },
15423
- },
15424
- meta: {
15425
- dimensions: {
15426
- width: 2,
15427
- height: 3,
15428
- minWidth: 2,
15429
- minHeight: 2,
15430
- maxWidth: 4,
15431
- maxHeight: 4,
15432
- },
15433
- },
15434
- };
15435
-
15436
- /**
15437
- * Task List Widget Component
15438
- * Displays a list of tasks with checkboxes and filtering options
15439
- */
15440
- class AXPTaskListWidgetViewComponent extends AXPValueWidgetComponent {
15441
- constructor() {
15442
- super(...arguments);
15443
- // Outputs
15444
- this.taskClick = output();
15445
- this.taskCompleted = output();
15446
- // Dependencies
15447
- this.cdr = inject(ChangeDetectorRef);
15448
- this.datePipe = inject(DatePipe);
15449
- // Configuration options
15450
- this.maxItems = computed(() => this.options()?.maxItems ?? 10);
15451
- this.showDate = computed(() => this.options()?.showDate ?? true);
15452
- this.showAssignee = computed(() => this.options()?.showAssignee ?? true);
15453
- this.showPriority = computed(() => this.options()?.showPriority ?? true);
15454
- this.allowMarkComplete = computed(() => this.options()?.allowMarkComplete ?? true);
15455
- this.showCategories = computed(() => this.options()?.groupByCategory ?? true);
15456
- // Data computed properties
15457
- this.taskItems = computed(() => {
15458
- const value = this.getValue();
15459
- if (!value?.data?.length)
15460
- return [];
15461
- return value.data.slice(0, this.maxItems());
15462
- });
15463
- }
15464
- // Task counting methods
15465
- getPendingTaskCount() {
15466
- return this.getFilteredTasks((task) => !task.completed).length;
15467
- }
15468
- getCompletedTaskCount() {
15469
- return this.getFilteredTasks((task) => task.completed).length;
15470
- }
15471
- // Category-related methods
15472
- hasCategories() {
15473
- return this.taskItems().some((task) => !!task.category);
15474
- }
15475
- getCategories() {
15476
- const tasks = this.taskItems();
15477
- const categories = new Set();
15478
- let hasUncategorized = false;
15479
- tasks.forEach((task) => {
15480
- if (task.category) {
15481
- categories.add(task.category);
15482
- }
15483
- else {
15484
- hasUncategorized = true;
15485
- }
15486
- });
15487
- const result = Array.from(categories);
15488
- if (hasUncategorized) {
15489
- result.push('Uncategorized');
15490
- }
15491
- return result;
15492
- }
15493
- getTasksByCategory(category) {
15494
- const tasks = this.taskItems();
15495
- if (category === 'Uncategorized') {
15496
- return tasks.filter((task) => !task.category);
15497
- }
15498
- return tasks.filter((task) => task.category === category);
15499
- }
15500
- getCategoryTaskCount(category) {
15501
- return this.getTasksByCategory(category).filter((task) => !task.completed).length;
15502
- }
15503
- // Event handlers
15504
- onTaskClick(task) {
15505
- this.taskClick.emit(task);
15506
- }
15507
- onTaskCompletionChange(task, isCompleted) {
15508
- const updatedTask = { ...task, completed: isCompleted };
15509
- const value = this.getValue();
15510
- if (!value?.data)
15511
- return;
15512
- const updatedTasks = value.data.map((t) => (t.id === task.id ? updatedTask : t));
15513
- this.setValue({ ...value, data: updatedTasks });
15514
- this.taskCompleted.emit(updatedTask);
15515
- this.cdr.detectChanges();
15516
- }
15517
- // Formatting and utility methods
15518
- formatDueDate(date) {
15519
- if (!date)
15520
- return '';
15521
- const dateObj = typeof date === 'string' ? new Date(date) : date;
15522
- const diffDays = this.getDaysDifference(dateObj);
15523
- if (diffDays < 0)
15524
- return 'Overdue!';
15525
- if (diffDays === 0)
15526
- return 'Today';
15527
- if (diffDays === 1)
15528
- return 'Tomorrow';
15529
- if (diffDays < 7)
15530
- return this.datePipe.transform(dateObj, 'EEE') || '';
15531
- return this.datePipe.transform(dateObj, 'MM/dd/yyyy') || '';
15532
- }
15533
- getPriorityColor(priority) {
15534
- if (!priority)
15535
- return '';
15536
- const priorityColors = {
15537
- high: 'danger',
15538
- medium: 'warning',
15539
- low: 'success',
15540
- };
15541
- return priorityColors[priority] || '';
15542
- }
15543
- // Helper methods
15544
- getFilteredTasks(filterFn) {
15545
- const value = this.getValue();
15546
- if (!value?.data?.length)
15547
- return [];
15548
- return value.data.filter(filterFn);
15549
- }
15550
- getDaysDifference(date) {
15551
- const now = new Date();
15552
- now.setHours(0, 0, 0, 0);
15553
- const targetDate = new Date(date);
15554
- targetDate.setHours(0, 0, 0, 0);
15555
- const diffMs = targetDate.getTime() - now.getTime();
15556
- return Math.floor(diffMs / (1000 * 60 * 60 * 24));
15557
- }
15558
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: AXPTaskListWidgetViewComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
15559
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.3", type: AXPTaskListWidgetViewComponent, isStandalone: true, selector: "ng-component", outputs: { taskClick: "taskClick", taskCompleted: "taskCompleted" }, providers: [DatePipe], usesInheritance: true, ngImport: i0, template: "<div class=\"ax-size-full ax-p-4\">\n <!-- Header -->\n <div class=\"ax-flex ax-justify-between ax-items-center ax-mb-3\">\n <h3 class=\"ax-text-lg ax-font-semibold\">{{ 'widget.tasklist.title' | translate | async }}</h3>\n <div class=\"ax-flex ax-gap-2\">\n @if(getPendingTaskCount() > 0){\n <ax-badge\n [text]=\"getPendingTaskCount() + ' ' + ('widget.tasklist.pending' | translate | async)\"\n [color]=\"'warning'\"\n size=\"sm\"\n class=\"ax-ml-1\"\n >\n </ax-badge>\n } @if(getCompletedTaskCount() > 0){\n <ax-badge\n [text]=\"getCompletedTaskCount() + ' ' + ('widget.tasklist.completed' | translate | async)\"\n [color]=\"'success'\"\n size=\"sm\"\n class=\"ax-ml-1\"\n >\n </ax-badge>\n }\n </div>\n </div>\n\n <!-- Task List -->\n <div class=\"ax-space-y-4 ax-my-4 ax-px-2\">\n @if (showCategories() && hasCategories()) {\n <!-- Categorized Tasks -->\n @for (category of getCategories(); track category) {\n <div class=\"ax-mb-3\">\n <!-- Category Header -->\n <div class=\"ax-flex ax-justify-between ax-items-center ax-mb-2 ax-h-5\">\n <h4 class=\"ax-font-medium ax-text-gray-700\">{{ category }}</h4>\n @if(getCategoryTaskCount(category)){\n <ax-badge [text]=\"getCategoryTaskCount(category).toString()\" [color]=\"'primary'\" size=\"sm\"></ax-badge>\n }\n </div>\n <!-- Tasks in Category -->\n @for (task of getTasksByCategory(category); track task.id) {\n <ng-container\n [ngTemplateOutlet]=\"taskItemTemplateRef\"\n [ngTemplateOutletContext]=\"{ $implicit: task }\"\n ></ng-container>\n }\n </div>\n } } @else {\n <!-- Uncategorized Tasks -->\n @for (task of taskItems(); track task.id) {\n <ng-container\n [ngTemplateOutlet]=\"taskItemTemplateRef\"\n [ngTemplateOutletContext]=\"{ $implicit: task }\"\n ></ng-container>\n } @empty {\n <!-- Empty State -->\n <div class=\"ax-flex ax-flex-col ax-items-center ax-justify-center ax-py-12 ax-px-4 ax-text-gray-400\">\n <ax-icon class=\"ax-text-4xl ax-mb-3 ax-text-gray-300\">\n <i class=\"fa-light fa-clipboard-list-check\"></i>\n </ax-icon>\n <p class=\"ax-text-center\">{{ 'widget.tasklist.noTasks' | translate | async }}</p>\n </div>\n } }\n </div>\n</div>\n\n<!-- Task Item Template -->\n<ng-template #taskItemTemplateRef let-task>\n <div class=\"ax-flex ax-gap-3 ax-items-center ax-py-2 ax-border-b ax-border-gray-100 last:ax-border-0\">\n <!-- Checkbox -->\n <ax-check-box\n class=\"ax-flex-shrink-0\"\n [value]=\"task.completed\"\n [disabled]=\"!allowMarkComplete()\"\n (valueChange)=\"onTaskCompletionChange(task, $event)\"\n >\n </ax-check-box>\n\n <!-- Task Details -->\n <div class=\"ax-overflow-hidden ax-grow ax-text-start\" (click)=\"onTaskClick(task)\">\n <!-- Title and Priority -->\n <div class=\"ax-flex ax-items-center ax-gap-2\">\n <h6\n class=\"ax-font-semibold ax-truncate ax-pb-1\"\n [class.ax-line-through]=\"task.completed\"\n [class.ax-text-gray-400]=\"task.completed\"\n >\n {{ task.title }}\n </h6>\n @if(showPriority() && task.priority) {\n <ax-badge [color]=\"getPriorityColor(task.priority)\" [text]=\"task.priority\" size=\"sm\" class=\"ax-ml-1\"></ax-badge>\n }\n </div>\n\n <!-- Metadata -->\n <div class=\"ax-flex ax-flex-wrap ax-gap-x-3 ax-gap-y-1 ax-mt-1 ax-text-xs ax-text-gray-500\">\n @if(showDate() && task.dueDate) {\n <span class=\"ax-flex ax-items-center ax-gap-1\" [class.ax-text-danger-500]=\"getDaysDifference(task.dueDate) < 0\">\n <ax-icon><i class=\"fa-light fa-calendar\"></i></ax-icon>\n {{ formatDueDate(task.dueDate) }}\n </span>\n } @if(showAssignee() && task.assignedTo) {\n <span class=\"ax-flex ax-items-center ax-gap-1\">\n <ax-icon><i class=\"fa-light fa-user\"></i></ax-icon>\n {{ task.assignedTo.name }}\n </span>\n }\n </div>\n </div>\n </div>\n</ng-template>\n", styles: [":host{display:block;height:100%;width:100%}.task-completed{text-decoration:line-through;color:var(--ax-text-secondary)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "ngmodule", type: AXTabsModule }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i3.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "ngmodule", type: AXBadgeModule }, { kind: "component", type: i1$2.AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }, { kind: "ngmodule", type: AXAvatarModule }, { kind: "ngmodule", type: AXImageModule }, { kind: "ngmodule", type: AXCheckBoxModule }, { kind: "component", type: i1.AXCheckBoxComponent, selector: "ax-check-box", inputs: ["disabled", "tabIndex", "readonly", "color", "value", "name", "id", "checked", "indeterminate"], outputs: ["onBlur", "onFocus", "valueChange", "onValueChanged"] }, { kind: "ngmodule", type: AXLabelModule }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "pipe", type: i5$1.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
15560
- }
15561
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: AXPTaskListWidgetViewComponent, decorators: [{
15562
- type: Component,
15563
- args: [{ standalone: true, imports: [
15564
- CommonModule,
15565
- AXTabsModule,
15566
- AXDecoratorModule,
15567
- AXButtonModule,
15568
- AXBadgeModule,
15569
- AXAvatarModule,
15570
- AXImageModule,
15571
- AXCheckBoxModule,
15572
- AXLabelModule,
15573
- AXTranslationModule,
15574
- ], providers: [DatePipe], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"ax-size-full ax-p-4\">\n <!-- Header -->\n <div class=\"ax-flex ax-justify-between ax-items-center ax-mb-3\">\n <h3 class=\"ax-text-lg ax-font-semibold\">{{ 'widget.tasklist.title' | translate | async }}</h3>\n <div class=\"ax-flex ax-gap-2\">\n @if(getPendingTaskCount() > 0){\n <ax-badge\n [text]=\"getPendingTaskCount() + ' ' + ('widget.tasklist.pending' | translate | async)\"\n [color]=\"'warning'\"\n size=\"sm\"\n class=\"ax-ml-1\"\n >\n </ax-badge>\n } @if(getCompletedTaskCount() > 0){\n <ax-badge\n [text]=\"getCompletedTaskCount() + ' ' + ('widget.tasklist.completed' | translate | async)\"\n [color]=\"'success'\"\n size=\"sm\"\n class=\"ax-ml-1\"\n >\n </ax-badge>\n }\n </div>\n </div>\n\n <!-- Task List -->\n <div class=\"ax-space-y-4 ax-my-4 ax-px-2\">\n @if (showCategories() && hasCategories()) {\n <!-- Categorized Tasks -->\n @for (category of getCategories(); track category) {\n <div class=\"ax-mb-3\">\n <!-- Category Header -->\n <div class=\"ax-flex ax-justify-between ax-items-center ax-mb-2 ax-h-5\">\n <h4 class=\"ax-font-medium ax-text-gray-700\">{{ category }}</h4>\n @if(getCategoryTaskCount(category)){\n <ax-badge [text]=\"getCategoryTaskCount(category).toString()\" [color]=\"'primary'\" size=\"sm\"></ax-badge>\n }\n </div>\n <!-- Tasks in Category -->\n @for (task of getTasksByCategory(category); track task.id) {\n <ng-container\n [ngTemplateOutlet]=\"taskItemTemplateRef\"\n [ngTemplateOutletContext]=\"{ $implicit: task }\"\n ></ng-container>\n }\n </div>\n } } @else {\n <!-- Uncategorized Tasks -->\n @for (task of taskItems(); track task.id) {\n <ng-container\n [ngTemplateOutlet]=\"taskItemTemplateRef\"\n [ngTemplateOutletContext]=\"{ $implicit: task }\"\n ></ng-container>\n } @empty {\n <!-- Empty State -->\n <div class=\"ax-flex ax-flex-col ax-items-center ax-justify-center ax-py-12 ax-px-4 ax-text-gray-400\">\n <ax-icon class=\"ax-text-4xl ax-mb-3 ax-text-gray-300\">\n <i class=\"fa-light fa-clipboard-list-check\"></i>\n </ax-icon>\n <p class=\"ax-text-center\">{{ 'widget.tasklist.noTasks' | translate | async }}</p>\n </div>\n } }\n </div>\n</div>\n\n<!-- Task Item Template -->\n<ng-template #taskItemTemplateRef let-task>\n <div class=\"ax-flex ax-gap-3 ax-items-center ax-py-2 ax-border-b ax-border-gray-100 last:ax-border-0\">\n <!-- Checkbox -->\n <ax-check-box\n class=\"ax-flex-shrink-0\"\n [value]=\"task.completed\"\n [disabled]=\"!allowMarkComplete()\"\n (valueChange)=\"onTaskCompletionChange(task, $event)\"\n >\n </ax-check-box>\n\n <!-- Task Details -->\n <div class=\"ax-overflow-hidden ax-grow ax-text-start\" (click)=\"onTaskClick(task)\">\n <!-- Title and Priority -->\n <div class=\"ax-flex ax-items-center ax-gap-2\">\n <h6\n class=\"ax-font-semibold ax-truncate ax-pb-1\"\n [class.ax-line-through]=\"task.completed\"\n [class.ax-text-gray-400]=\"task.completed\"\n >\n {{ task.title }}\n </h6>\n @if(showPriority() && task.priority) {\n <ax-badge [color]=\"getPriorityColor(task.priority)\" [text]=\"task.priority\" size=\"sm\" class=\"ax-ml-1\"></ax-badge>\n }\n </div>\n\n <!-- Metadata -->\n <div class=\"ax-flex ax-flex-wrap ax-gap-x-3 ax-gap-y-1 ax-mt-1 ax-text-xs ax-text-gray-500\">\n @if(showDate() && task.dueDate) {\n <span class=\"ax-flex ax-items-center ax-gap-1\" [class.ax-text-danger-500]=\"getDaysDifference(task.dueDate) < 0\">\n <ax-icon><i class=\"fa-light fa-calendar\"></i></ax-icon>\n {{ formatDueDate(task.dueDate) }}\n </span>\n } @if(showAssignee() && task.assignedTo) {\n <span class=\"ax-flex ax-items-center ax-gap-1\">\n <ax-icon><i class=\"fa-light fa-user\"></i></ax-icon>\n {{ task.assignedTo.name }}\n </span>\n }\n </div>\n </div>\n </div>\n</ng-template>\n", styles: [":host{display:block;height:100%;width:100%}.task-completed{text-decoration:line-through;color:var(--ax-text-secondary)}\n"] }]
15575
- }] });
15576
-
15577
- var tasklistWidget_component = /*#__PURE__*/Object.freeze({
15578
- __proto__: null,
15579
- AXPTaskListWidgetViewComponent: AXPTaskListWidgetViewComponent
15580
- });
15581
-
15582
- const AXPTaskListWidget = {
15583
- name: 'task-list',
15584
- title: 'Task List Widget',
15585
- categories: [AXP_WIDGETS_CHART_CATEGORY],
15586
- groups: [AXPWidgetGroupEnum.DashboardWidget],
15587
- type: 'dashboard',
15588
- icon: 'fa-light fa-clipboard-list-check',
15589
- properties: [
15590
- cloneProperty(AXP_DATA_PATH_PROPERTY, { visible: false }),
15591
- // Display options
15592
- {
15593
- name: 'maxItems',
15594
- title: 'Max Items',
15595
- group: AXP_APPEARANCE_PROPERTY_GROUP,
15596
- schema: {
15597
- defaultValue: 10,
15598
- dataType: 'number',
15599
- interface: {
15600
- name: 'maxItems',
15601
- path: 'options.maxItems',
15602
- type: AXPWidgetsCatalog.number,
15603
- options: {
15604
- minValue: 1,
15605
- maxValue: 50,
15606
- },
15607
- },
15608
- },
15609
- visible: true,
15610
- },
15611
- {
15612
- name: 'showDate',
15613
- title: 'Show Date',
15614
- group: AXP_APPEARANCE_PROPERTY_GROUP,
15615
- schema: {
15616
- defaultValue: true,
15617
- dataType: 'boolean',
15618
- interface: {
15619
- name: 'showDate',
15620
- path: 'options.showDate',
15621
- type: AXPWidgetsCatalog.toggle,
15622
- },
15623
- },
15624
- visible: true,
15625
- },
15626
- {
15627
- name: 'showAssignee',
15628
- title: 'Show Assignee',
15629
- group: AXP_APPEARANCE_PROPERTY_GROUP,
15630
- schema: {
15631
- defaultValue: true,
15632
- dataType: 'boolean',
15633
- interface: {
15634
- name: 'showAssignee',
15635
- path: 'options.showAssignee',
15636
- type: AXPWidgetsCatalog.toggle,
15637
- },
15638
- },
15639
- visible: true,
15640
- },
15641
- {
15642
- name: 'groupByCategory',
15643
- title: 'Group by Category',
15644
- group: AXP_APPEARANCE_PROPERTY_GROUP,
15645
- schema: {
15646
- defaultValue: true,
15647
- dataType: 'boolean',
15648
- interface: {
15649
- name: 'groupByCategory',
15650
- path: 'options.groupByCategory',
15651
- type: AXPWidgetsCatalog.toggle,
15652
- },
15653
- },
15654
- visible: true,
15655
- },
15656
- {
15657
- name: 'showPriority',
15658
- title: 'Show Priority',
15659
- group: AXP_APPEARANCE_PROPERTY_GROUP,
15660
- schema: {
15661
- defaultValue: true,
15662
- dataType: 'boolean',
15663
- interface: {
15664
- name: 'showPriority',
15665
- path: 'options.showPriority',
15666
- type: AXPWidgetsCatalog.toggle,
15667
- },
15668
- },
15669
- visible: true,
15670
- },
15671
- {
15672
- name: 'allowMarkComplete',
15673
- title: 'Allow Complete',
15674
- group: AXP_APPEARANCE_PROPERTY_GROUP,
15675
- schema: {
15676
- defaultValue: true,
15677
- dataType: 'boolean',
15678
- interface: {
15679
- name: 'allowMarkComplete',
15680
- path: 'options.allowMarkComplete',
15681
- type: AXPWidgetsCatalog.toggle,
15682
- },
15683
- },
15684
- visible: true,
15685
- },
15686
- ],
15687
- components: {
15688
- view: {
15689
- component: () => Promise.resolve().then(function () { return tasklistWidget_component; }).then((c) => c.AXPTaskListWidgetViewComponent),
15690
- },
15691
- },
15692
- meta: {
15693
- dimensions: {
15694
- width: 5,
15695
- height: 7,
15696
- minWidth: 3,
15697
- minHeight: 4,
15698
- maxWidth: 6,
15699
- maxHeight: 8,
15700
- },
15701
- },
15702
- };
15703
-
15704
- /**
15705
- * Abstract Weather API Service
15706
- * Base class that defines the interface and common functionality
15707
- * for weather data providers
15708
- */
15709
- class AXPWeatherApiAbstract {
15710
- constructor() {
15711
- /** Weather condition definitions mapping */
15712
- this.weatherConditions = {
15713
- sunny: {
15714
- id: 'sunny',
15715
- name: 'Sunny',
15716
- icon: 'fa-solid fa-sun',
15717
- color: '#ff9d00',
15718
- },
15719
- clearNight: {
15720
- id: 'clearNight',
15721
- name: 'Clear Night',
15722
- icon: 'fa-solid fa-moon',
15723
- color: '#5d639e',
15724
- },
15725
- partlyCloudy: {
15726
- id: 'partlyCloudy',
15727
- name: 'Partly Cloudy',
15728
- icon: 'fa-solid fa-cloud-sun',
15729
- color: '#6ba4e8',
15730
- },
15731
- partlyCloudyNight: {
15732
- id: 'partlyCloudyNight',
15733
- name: 'Partly Cloudy Night',
15734
- icon: 'fa-solid fa-cloud-moon',
15735
- color: '#5d639e',
15736
- },
15737
- cloudy: {
15738
- id: 'cloudy',
15739
- name: 'Cloudy',
15740
- icon: 'fa-solid fa-cloud',
15741
- color: '#72869d',
15742
- },
15743
- rain: {
15744
- id: 'rain',
15745
- name: 'Rain',
15746
- icon: 'fa-solid fa-cloud-rain',
15747
- color: '#3a74ad',
15748
- },
15749
- showers: {
15750
- id: 'showers',
15751
- name: 'Showers',
15752
- icon: 'fa-solid fa-cloud-showers-heavy',
15753
- color: '#2c5d8c',
15754
- },
15755
- thunderstorm: {
15756
- id: 'thunderstorm',
15757
- name: 'Thunderstorm',
15758
- icon: 'fa-solid fa-bolt-lightning',
15759
- color: '#8834af',
15760
- },
15761
- snow: {
15762
- id: 'snow',
15763
- name: 'Snow',
15764
- icon: 'fa-solid fa-snowflake',
15765
- color: '#68a9cd',
15766
- },
15767
- mist: {
15768
- id: 'mist',
15769
- name: 'Mist',
15770
- icon: 'fa-solid fa-smog',
15771
- color: '#94a3b8',
15772
- },
15773
- };
15774
- /** Day of week mapping */
15775
- this.dayNames = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
15776
- }
15777
- /**
15778
- * Get weather condition info by ID
15779
- * @param id Condition ID
15780
- * @returns Weather condition info or default if not found
15781
- */
15782
- getCondition(id) {
15783
- // Normalize condition ID by removing spaces and converting to lowercase
15784
- const normalizedId = id.toLowerCase().replace(/\s+/g, '');
15785
- // Direct match first
15786
- if (this.weatherConditions[normalizedId]) {
15787
- return this.weatherConditions[normalizedId];
15788
- }
15789
- // Check for partial matches or common variants
15790
- if (normalizedId.includes('partly') && normalizedId.includes('cloud')) {
15791
- return this.weatherConditions['partlyCloudy'];
15792
- }
15793
- if (normalizedId.includes('cloud')) {
15794
- return this.weatherConditions['cloudy'];
15795
- }
15796
- if (normalizedId.includes('sun')) {
15797
- return this.weatherConditions['sunny'];
15798
- }
15799
- if (normalizedId.includes('rain')) {
15800
- return this.weatherConditions['rain'];
15801
- }
15802
- if (normalizedId.includes('snow')) {
15803
- return this.weatherConditions['snow'];
15804
- }
15805
- // If no match found, return default unknown condition
15806
- return {
15807
- id: 'unknown',
15808
- name: 'Unknown',
15809
- icon: 'fa-solid fa-question',
15810
- color: '#999999',
15811
- };
15812
- }
15813
- /**
15814
- * Parse location string into city and display components
15815
- * @param location Location query string
15816
- * @returns Parsed location parts
15817
- */
15818
- parseLocation(location) {
15819
- if (!location || location.trim() === '') {
15820
- return { city: 'New York', country: 'USA', display: 'New York' };
15821
- }
15822
- const city = location.trim();
15823
- return { city, country: '', display: city };
15824
- }
15825
- /**
15826
- * Format location name for display
15827
- * @param locationParts Parsed location parts
15828
- * @returns Formatted location name
15829
- */
15830
- formatLocationName(locationParts) {
15831
- return locationParts.city || 'Unknown Location';
15832
- }
15833
- /**
15834
- * Map API condition text to our internal condition IDs
15835
- * @param conditionText Condition text from API
15836
- * @returns Internal condition ID
15837
- */
15838
- mapApiConditionToId(conditionText) {
15839
- const text = conditionText.toLowerCase();
15840
- // Match WeatherAPI.com condition text
15841
- // Reference: https://www.weatherapi.com/docs/#weather-icons
15842
- if (text.includes('sunny') || text.includes('clear')) {
15843
- return 'sunny';
15844
- }
15845
- if (text === 'partly cloudy') {
15846
- return 'partlyCloudy';
15847
- }
15848
- if (text.includes('cloudy') || text.includes('overcast')) {
15849
- return 'cloudy';
15850
- }
15851
- if (text.includes('rain') || text.includes('drizzle') || text.includes('shower')) {
15852
- return 'rain';
15853
- }
15854
- if (text.includes('thunder') || text.includes('lightning')) {
15855
- return 'thunderstorm';
15856
- }
15857
- if (text.includes('snow') || text.includes('sleet') || text.includes('blizzard')) {
15858
- return 'snow';
15859
- }
15860
- if (text.includes('mist') || text.includes('fog')) {
15861
- return 'mist';
15862
- }
15863
- // Default to partly cloudy if we don't have a specific mapping
15864
- return 'partlyCloudy';
15865
- }
15866
- /**
15867
- * Generate random integer between min and max (inclusive)
15868
- * Helper method for implementations
15869
- * @param min Minimum value
15870
- * @param max Maximum value
15871
- * @returns Random integer
15872
- */
15873
- getRandomInt(min, max) {
15874
- return Math.floor(Math.random() * (max - min + 1)) + min;
15875
- }
15876
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: AXPWeatherApiAbstract, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
15877
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: AXPWeatherApiAbstract }); }
15878
- }
15879
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: AXPWeatherApiAbstract, decorators: [{
15880
- type: Injectable
15881
- }] });
15882
-
15883
- /**
15884
- * Mock Weather API Service
15885
- * Provides simulated weather data for development and testing
15886
- */
15887
- class AXPWeatherApiMockService extends AXPWeatherApiAbstract {
15888
- constructor() {
15889
- super(...arguments);
15890
- // Mock configuration
15891
- this.mockDelay = 500;
15892
- }
15893
- /**
15894
- * Get current weather data for given location
15895
- * @param options Weather request options
15896
- * @returns Observable with weather data
15897
- */
15898
- getWeather(options) {
15899
- const locationParts = this.parseLocation(options.location || '');
15900
- const displayName = this.formatLocationName(locationParts);
15901
- return this.getMockWeatherData(locationParts.city, options).pipe(map((mockData) => ({
15902
- ...mockData,
15903
- location: {
15904
- city: locationParts.city,
15905
- country: '',
15906
- displayName: displayName,
15907
- },
15908
- })));
15909
- }
15910
- /**
15911
- * Get weather forecast for a location
15912
- * @param options Weather request options with days
15913
- * @returns Observable of weather data with forecast
15914
- */
15915
- getForecast(options) {
15916
- const days = options.days || 5;
15917
- return this.getMockForecast(options.location || '', days, options);
15918
- }
15919
- /**
15920
- * Get mock weather data for demo/development
15921
- * @param location Location query
15922
- * @param options Request options
15923
- * @returns Observable of mock weather data
15924
- */
15925
- getMockWeatherData(location, options) {
15926
- return new Observable((observer) => {
15927
- setTimeout(() => {
15928
- try {
15929
- const locationParts = this.parseLocation(location);
15930
- const tempUnit = options?.tempUnit || '°C';
15931
- const isCelsius = tempUnit === '°C';
15932
- // Generate more realistic weather data based on location and current date
15933
- const now = new Date();
15934
- const month = now.getMonth(); // 0-11
15935
- // Seasonally appropriate temperature range
15936
- let minRange = 15;
15937
- let maxRange = 25;
15938
- // Northern hemisphere seasonal adjustments
15939
- if (month >= 11 || month <= 1) {
15940
- // Winter
15941
- minRange = -5;
15942
- maxRange = 10;
15943
- }
15944
- else if (month >= 2 && month <= 4) {
15945
- // Spring
15946
- minRange = 10;
15947
- maxRange = 20;
15948
- }
15949
- else if (month >= 5 && month <= 7) {
15950
- // Summer
15951
- minRange = 20;
15952
- maxRange = 35;
15953
- }
15954
- else if (month >= 8 && month <= 10) {
15955
- // Fall/Autumn
15956
- minRange = 10;
15957
- maxRange = 25;
15958
- }
15959
- // Generate current temperature - more likely to be in the middle of the range
15960
- const tempC = Math.round(minRange + (this.getRandomInt(4, 8) / 10) * (maxRange - minRange));
15961
- const tempF = Math.round((tempC * 9) / 5 + 32);
15962
- const humidity = this.getRandomInt(30, 90);
15963
- const windKph = this.getRandomInt(5, 30);
15964
- const windMph = Math.round(windKph * 0.621371);
15965
- // Temperature-based conditions with randomization
15966
- let conditionId = 'partlyCloudy';
15967
- if (tempC > 25)
15968
- conditionId = 'sunny';
15969
- else if (tempC < 10)
15970
- conditionId = 'cloudy';
15971
- if (this.getRandomInt(1, 10) > 7)
15972
- conditionId = 'rain';
15973
- observer.next({
15974
- location: {
15975
- city: locationParts.city,
15976
- country: '',
15977
- displayName: this.formatLocationName(locationParts),
15978
- },
15979
- current: {
15980
- condition: conditionId,
15981
- conditionCode: 0,
15982
- iconUrl: '', // Would be set from API
15983
- tempC: tempC,
15984
- tempF: tempF,
15985
- feelsLikeC: tempC + this.getRandomInt(-3, 2),
15986
- feelsLikeF: tempF + this.getRandomInt(-5, 3),
15987
- humidity: humidity,
15988
- windKph: windKph,
15989
- windMph: windMph,
15990
- windDirection: ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW'][this.getRandomInt(0, 7)],
15991
- uv: this.getRandomInt(0, 10),
15992
- lastUpdated: new Date().toISOString(),
15993
- },
15994
- forecast: [],
15995
- });
15996
- observer.complete();
15997
- }
15998
- catch (error) {
15999
- observer.error(error);
16000
- }
16001
- }, this.mockDelay);
16002
- }).pipe(catchError((error) => throwError(() => new Error(`Failed to get weather data: ${error.message}`))));
16003
- }
16004
- /**
16005
- * Get mock forecast data for demo/development
16006
- * @param location Location query
16007
- * @param days Number of forecast days
16008
- * @param options Request options
16009
- * @returns Observable of mock weather data with forecast
16010
- */
16011
- getMockForecast(location, days = 5, options) {
16012
- return this.getMockWeatherData(location, options).pipe(map((data) => {
16013
- const currentCondition = data.current.condition;
16014
- const currentTemp = data.current.tempC;
16015
- // Use the current weather as a base for the forecast trend
16016
- const forecast = Array.from({ length: days }, (_, i) => {
16017
- // Create forecast date for each day (starting from tomorrow)
16018
- const forecastDate = new Date();
16019
- forecastDate.setDate(new Date().getDate() + i + 1);
16020
- const dateStr = forecastDate.toISOString().split('T')[0];
16021
- const dayName = this.dayNames[forecastDate.getDay()];
16022
- // Generate condition with some continuity from current weather
16023
- // Weather tends to be similar for a few days with gradual changes
16024
- let conditionId;
16025
- if (i === 0) {
16026
- // Tomorrow's weather has 60% chance of being similar to today
16027
- conditionId =
16028
- this.getRandomInt(1, 10) <= 6
16029
- ? currentCondition
16030
- : ['sunny', 'partlyCloudy', 'cloudy', 'rain', 'snow'][this.getRandomInt(0, 4)];
16031
- }
16032
- else {
16033
- // Subsequent days have 70% chance of being similar to previous day
16034
- const previousCondition = forecast[i - 1]?.condition || currentCondition;
16035
- conditionId =
16036
- this.getRandomInt(1, 10) <= 7
16037
- ? previousCondition
16038
- : ['sunny', 'partlyCloudy', 'cloudy', 'rain', 'snow'][this.getRandomInt(0, 4)];
16039
- }
16040
- // Generate temperatures with realistic variance
16041
- // Max is typically higher than current temperature
16042
- // Min is typically lower than current temperature
16043
- const tempVariance = this.getRandomInt(-3, 3); // Small day-to-day change
16044
- const baseMaxC = currentTemp + this.getRandomInt(0, 5); // Max is higher than current
16045
- const maxTempC = baseMaxC + tempVariance;
16046
- const minTempC = maxTempC - this.getRandomInt(5, 10); // Min is lower than max
16047
- const maxTempF = Math.round((maxTempC * 9) / 5 + 32);
16048
- const minTempF = Math.round((minTempC * 9) / 5 + 32);
16049
- return {
16050
- date: dateStr,
16051
- day: dayName,
16052
- condition: conditionId,
16053
- iconUrl: '', // Would be set from API
16054
- maxTempC,
16055
- maxTempF,
16056
- minTempC,
16057
- minTempF,
16058
- };
16059
- });
16060
- return { ...data, forecast };
16061
- }));
16062
- }
16063
- /**
16064
- * Set the API key for the weather service
16065
- * No-op in the mock implementation
16066
- * @param key The API key
16067
- */
16068
- setApiKey(key) {
16069
- // No-op for mock service
16070
- console.log('API key setting is ignored in mock weather service');
16071
- }
16072
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: AXPWeatherApiMockService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
16073
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: AXPWeatherApiMockService }); }
16074
- }
16075
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: AXPWeatherApiMockService, decorators: [{
16076
- type: Injectable
16077
- }] });
16078
-
16079
- const AXP_WEATHER_API_KEY = new InjectionToken('AXP_WEATHER_API_KEY', {
16080
- providedIn: 'root',
16081
- factory: () => {
16082
- return '40281dc1e31749edb6c104828250604';
16083
- },
16084
- });
16085
-
16086
- /**
16087
- * Real Weather API Service
16088
- * Fetches actual weather data from a weather API service
16089
- */
16090
- class AXPWeatherApiService extends AXPWeatherApiAbstract {
16091
- constructor() {
16092
- super(...arguments);
16093
- this.http = inject(HttpClient);
16094
- // API configuration
16095
- this.baseApiUrl = 'https://api.weatherapi.com/v1';
16096
- this.apiKeyToken = inject(AXP_WEATHER_API_KEY, { optional: true });
16097
- }
16098
- /**
16099
- * Set the API key for the weather service
16100
- * @param key The API key
16101
- */
16102
- setApiKey(key) {
16103
- if (key && key.trim() !== '') {
16104
- this.apiKeyToken = key;
16105
- }
16106
- }
16107
- /**
16108
- * Get current weather data for given location
16109
- * @param options Weather request options
16110
- * @returns Observable with weather data
16111
- */
16112
- getWeather(options) {
16113
- // If forecast is likely needed, we should fetch it all at once
16114
- if (options.useMockData === false || options.useMockData === undefined) {
16115
- // For real API, use the forecast endpoint which includes current data
16116
- return this.getForecast({
16117
- ...options,
16118
- days: 1, // Request minimal forecast data
16119
- });
16120
- }
16121
- // For mock data, we'll continue using separate calls for backward compatibility
16122
- const locationParts = this.parseLocation(options.location || '');
16123
- const displayName = this.formatLocationName(locationParts);
16124
- // Use real API with query based on city only
16125
- const query = encodeURIComponent(locationParts.city);
16126
- const url = `${this.baseApiUrl}/current.json?key=${this.apiKeyToken}&q=${query}&aqi=no`;
16127
- return this.http.get(url).pipe(map((data) => this.transformApiResponse(data, displayName)), catchError((error) => {
16128
- console.error('Weather API error:', error);
16129
- // Check for location not found error
16130
- if (error.error?.error?.code === 1006) {
16131
- return throwError(() => new Error(`Location "${locationParts.city}" not found. Please check the city name and try again.`));
16132
- }
16133
- return throwError(() => new Error(`Failed to fetch weather data: ${error.message}`));
16134
- }));
16135
- }
16136
- /**
16137
- * Get weather forecast for a location
16138
- * @param options Weather request options with days
16139
- * @returns Observable of weather data with forecast
16140
- */
16141
- getForecast(options) {
16142
- const locationParts = this.parseLocation(options.location || '');
16143
- const displayName = this.formatLocationName(locationParts);
16144
- const days = options.days || 5;
16145
- // Build forecast API URL
16146
- const query = encodeURIComponent(locationParts.city);
16147
- const url = `${this.baseApiUrl}/forecast.json?key=${this.apiKeyToken}&q=${query}&days=${days}&aqi=no`;
16148
- return this.http.get(url).pipe(map((data) => this.transformForecastResponse(data, displayName, days)), catchError((error) => {
16149
- console.error('Weather API error:', error);
16150
- // Check for location not found error
16151
- if (error.error?.error?.code === 1006) {
16152
- return throwError(() => new Error(`Location "${locationParts.city}" not found. Please check the city name and try again.`));
16153
- }
16154
- return throwError(() => new Error(`Failed to fetch forecast data: ${error.message}`));
16155
- }));
16156
- }
16157
- /**
16158
- * Transform API response to our internal data model
16159
- * @param apiData Raw API response
16160
- * @param displayName Formatted location name
16161
- * @returns Normalized weather data
16162
- */
16163
- transformApiResponse(apiData, displayName) {
16164
- // Map API condition text to our condition IDs
16165
- const conditionText = apiData.current?.condition?.text || '';
16166
- const conditionId = this.mapApiConditionToId(conditionText);
16167
- return {
16168
- location: {
16169
- city: apiData.location?.name || '',
16170
- country: apiData.location?.country || '',
16171
- displayName: displayName,
16172
- },
16173
- current: {
16174
- condition: conditionId, // Use our mapped condition ID
16175
- conditionCode: apiData.current?.condition?.code || 0,
16176
- iconUrl: apiData.current?.condition?.icon || '',
16177
- tempC: apiData.current?.temp_c || 0,
16178
- tempF: apiData.current?.temp_f || 0,
16179
- feelsLikeC: apiData.current?.feelslike_c || 0,
16180
- feelsLikeF: apiData.current?.feelslike_f || 0,
16181
- humidity: apiData.current?.humidity || 0,
16182
- windKph: apiData.current?.wind_kph || 0,
16183
- windMph: apiData.current?.wind_mph || 0,
16184
- windDirection: apiData.current?.wind_dir || '',
16185
- uv: apiData.current?.uv || 0,
16186
- lastUpdated: apiData.current?.last_updated || new Date().toISOString(),
16187
- },
16188
- forecast: [],
16189
- };
16190
- }
16191
- /**
16192
- * Transform forecast API response to our internal data model
16193
- * @param apiData Raw API response with forecast
16194
- * @param displayName Formatted location name
16195
- * @param days Number of forecast days
16196
- * @returns Normalized weather data with forecast
16197
- */
16198
- transformForecastResponse(apiData, displayName, days) {
16199
- // First transform the current weather data using the current field of the forecast response
16200
- const weatherData = this.transformApiResponse(apiData, displayName);
16201
- // Then add the forecast data
16202
- const forecastDays = apiData.forecast?.forecastday || [];
16203
- weatherData.forecast = forecastDays.slice(0, days).map((day) => {
16204
- // Parse the date for day name
16205
- const date = new Date(day.date);
16206
- const dayName = date.toLocaleDateString('en-US', { weekday: 'short' });
16207
- // Map condition to our internal ID
16208
- const conditionText = day.day?.condition?.text || '';
16209
- const conditionId = this.mapApiConditionToId(conditionText);
16210
- return {
16211
- date: day.date,
16212
- day: dayName,
16213
- condition: conditionId,
16214
- iconUrl: day.day?.condition?.icon || '',
16215
- maxTempC: day.day?.maxtemp_c || 0,
16216
- maxTempF: day.day?.maxtemp_f || 0,
16217
- minTempC: day.day?.mintemp_c || 0,
16218
- minTempF: day.day?.mintemp_f || 0,
16219
- };
16220
- });
16221
- return weatherData;
16222
- }
16223
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: AXPWeatherApiService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
16224
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: AXPWeatherApiService }); }
16225
- }
16226
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: AXPWeatherApiService, decorators: [{
16227
- type: Injectable
16228
- }] });
16229
-
16230
- /**
16231
- * Weather Widget Component
16232
- * Displays current weather conditions and optional forecast data
16233
- * for a specified location with customizable display options.
16234
- */
16235
- class AXPWeatherWidgetViewComponent extends AXPValueWidgetComponent {
16236
- /**
16237
- * Component constructor
16238
- * @param cdr ChangeDetectorRef for triggering view updates
16239
- */
16240
- constructor(cdr) {
16241
- super();
16242
- this.cdr = cdr;
16243
- // Container element reference
16244
- this.containerEl = viewChild.required('containerElement');
16245
- // Weather API service instance
16246
- this.weatherService = inject(AXPWeatherApiAbstract);
16247
- // State signals
16248
- this.weatherData = signal(null);
16249
- this.isLoading = signal(true);
16250
- this.hasError = signal(false);
16251
- this.errorMessage = signal('');
16252
- this.isForecastLoading = signal(false);
16253
- // Auto-refresh subscription
16254
- this.refreshSubscription = null;
16255
- // Option-derived computed properties
16256
- this.city = computed(() => {
16257
- return this.options()['city'] || 'Newyork';
16258
- });
16259
- this.temperatureUnit = computed(() => this.options()['temperatureUnit']?.id || '°C');
16260
- this.windSpeedUnit = computed(() => this.options()['windSpeedUnit']?.id || 'km/h');
16261
- // Display option flags
16262
- this.showCurrentCondition = computed(() => this.options()['showCurrentCondition'] !== false);
16263
- this.showTemperature = computed(() => this.options()['showTemperature'] !== false);
16264
- this.showHumidity = computed(() => this.options()['showHumidity'] !== false);
16265
- this.showWind = computed(() => this.options()['showWind'] !== false);
16266
- this.showForecast = computed(() => this.options()['showForecast'] !== false);
16267
- this.forecastDays = computed(() => this.options()['forecastDays'] ?? 5);
16268
- // Refresh settings
16269
- this.autoRefresh = computed(() => this.options()['autoRefresh'] !== false);
16270
- this.refreshInterval = computed(() => this.options()['refreshInterval']?.id ?? 15);
16271
- // Reactivity effects
16272
- this.optionsEffect = effect(() => {
16273
- const opts = this.options();
16274
- this.loadWeatherData();
16275
- this.setupRefreshTimer();
16276
- });
16277
- this.valueEffect = effect(() => {
16278
- this.city();
16279
- this.loadWeatherData();
16280
- });
16281
- this.displayedForecast = computed(() => {
16282
- const weatherData = this.weatherData();
16283
- if (!weatherData?.forecast)
16284
- return [];
16285
- const forecastDays = this.forecastDays();
16286
- return weatherData.forecast.slice(0, forecastDays);
16287
- });
16288
- // Inject the abstract service which will resolve to either the mock or real implementation
16289
- this.weatherService = inject(AXPWeatherApiAbstract);
16290
- setTimeout(() => {
16291
- this.loadWeatherData();
16292
- this.setupRefreshTimer();
16293
- }, 0);
16294
- }
16295
- /**
16296
- * Component cleanup on destroy
16297
- */
16298
- ngOnDestroy() {
16299
- this.clearRefreshTimer();
16300
- }
16301
- /**
16302
- * Loads weather data from the API
16303
- * Sets loading state and handles errors
16304
- */
16305
- loadWeatherData() {
16306
- this.isLoading.set(true);
16307
- this.hasError.set(false);
16308
- this.errorMessage.set('');
16309
- const locationQuery = this.city();
16310
- const shouldShowForecast = this.showForecast();
16311
- // If we need forecast, request it directly to avoid duplicate calls
16312
- // The forecast endpoint also returns current weather
16313
- if (shouldShowForecast) {
16314
- const forecastOptions = {
16315
- location: locationQuery,
16316
- tempUnit: this.temperatureUnit(),
16317
- useMockData: false, // Use real API, signals to use forecast endpoint
16318
- days: this.forecastDays(),
16319
- };
16320
- this.isForecastLoading.set(true);
16321
- this.weatherService.getForecast(forecastOptions).subscribe({
16322
- next: (data) => {
16323
- this.weatherData.set(data);
16324
- this.isLoading.set(false);
16325
- this.isForecastLoading.set(false);
16326
- setTimeout(() => this.cdr.detectChanges());
16327
- },
16328
- error: (error) => {
16329
- this.hasError.set(true);
16330
- this.errorMessage.set(error.message || 'Failed to load weather data. Please try again.');
16331
- this.isLoading.set(false);
16332
- this.isForecastLoading.set(false);
16333
- this.cdr.detectChanges();
16334
- },
16335
- });
16336
- return;
16337
- }
16338
- // If we only need current weather, use getWeather
16339
- const requestOptions = {
16340
- location: locationQuery,
16341
- tempUnit: this.temperatureUnit(),
16342
- useMockData: false, // Use real API
16343
- };
16344
- this.weatherService.getWeather(requestOptions).subscribe({
16345
- next: (data) => {
16346
- this.weatherData.set(data);
16347
- this.isLoading.set(false);
16348
- setTimeout(() => this.cdr.detectChanges());
16349
- },
16350
- error: (error) => {
16351
- this.hasError.set(true);
16352
- this.errorMessage.set(error.message || 'Failed to load weather data. Please try again.');
16353
- this.isLoading.set(false);
16354
- this.cdr.detectChanges();
16355
- },
16356
- });
16357
- }
16358
- /**
16359
- * Sets up the auto-refresh timer based on configuration
16360
- */
16361
- setupRefreshTimer() {
16362
- this.clearRefreshTimer();
16363
- if (!this.autoRefresh())
16364
- return;
16365
- const minInterval = 5; // 5 minutes minimum
16366
- const intervalValue = Math.max(this.refreshInterval(), minInterval);
16367
- const intervalMs = intervalValue * 60 * 1000;
16368
- this.refreshSubscription = interval(intervalMs)
16369
- .pipe(switchMap(() => {
16370
- // Always use the loadWeatherData method to determine the correct API call
16371
- this.loadWeatherData();
16372
- // Return an empty observable to satisfy the switchMap typing
16373
- return new Observable((observer) => {
16374
- observer.complete();
16375
- });
16376
- }))
16377
- .subscribe();
16378
- }
16379
- /**
16380
- * Clears the refresh timer subscription
16381
- */
16382
- clearRefreshTimer() {
16383
- if (this.refreshSubscription) {
16384
- this.refreshSubscription.unsubscribe();
16385
- this.refreshSubscription = null;
16386
- }
16387
- }
16388
- /**
16389
- * Manually refreshes the weather data
16390
- */
16391
- refreshWeather() {
16392
- this.loadWeatherData();
16393
- }
16394
- /**
16395
- * Gets the current temperature based on selected unit
16396
- */
16397
- getCurrentTemperature() {
16398
- if (!this.weatherData())
16399
- return 0;
16400
- return this.temperatureUnit() === '°C' ? this.weatherData().current.tempC : this.weatherData().current.tempF;
16401
- }
16402
- /**
16403
- * Gets the feels like temperature based on selected unit
16404
- */
16405
- getFeelsLikeTemperature() {
16406
- if (!this.weatherData())
16407
- return 0;
16408
- return this.temperatureUnit() === '°C'
16409
- ? this.weatherData().current.feelsLikeC
16410
- : this.weatherData().current.feelsLikeF;
16411
- }
16412
- /**
16413
- * Gets the humidity percentage
16414
- */
16415
- getHumidity() {
16416
- return this.weatherData()?.current.humidity || 0;
16417
- }
16418
- /**
16419
- * Gets the wind speed based on selected unit
16420
- */
16421
- getWindSpeed() {
16422
- if (!this.weatherData())
16423
- return 0;
16424
- return this.windSpeedUnit() === 'km/h' ? this.weatherData().current.windKph : this.weatherData().current.windMph;
16425
- }
16426
- /**
16427
- * Gets the current weather condition
16428
- */
16429
- getCurrentCondition() {
16430
- const conditionId = this.weatherData()?.current.condition || '';
16431
- return this.getConditionName(conditionId);
16432
- }
16433
- /**
16434
- * Gets the formatted last updated time
16435
- */
16436
- getLastUpdated() {
16437
- if (!this.weatherData()?.current.lastUpdated)
16438
- return '';
16439
- return new Date(this.weatherData().current.lastUpdated).toLocaleTimeString();
16440
- }
16441
- /**
16442
- * Gets the icon class for a weather condition
16443
- * @param conditionId Weather condition ID
16444
- * @returns Font Awesome icon class
16445
- */
16446
- getConditionIcon(conditionId) {
16447
- const condition = this.weatherService.getCondition(conditionId);
16448
- return condition?.icon || 'fa-solid fa-question';
16449
- }
16450
- /**
16451
- * Gets the display name for a weather condition
16452
- * @param conditionId Weather condition ID
16453
- * @returns Condition display name
16454
- */
16455
- getConditionName(conditionId) {
16456
- const condition = this.weatherService.getCondition(conditionId);
16457
- return condition?.name || 'Unknown';
16458
- }
16459
- /**
16460
- * Gets the color for a weather condition
16461
- * @param conditionId Weather condition ID
16462
- * @returns CSS color value
16463
- */
16464
- getConditionColor(conditionId) {
16465
- const condition = this.weatherService.getCondition(conditionId);
16466
- return condition?.color || '#999999';
16467
- }
16468
- /**
16469
- * Cleans up chart resources
16470
- * Required by AXPChartBaseComponent
16471
- */
16472
- cleanupChart() {
16473
- this.clearRefreshTimer();
16474
- }
16475
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: AXPWeatherWidgetViewComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
16476
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.3", type: AXPWeatherWidgetViewComponent, isStandalone: true, selector: "ng-component", providers: [
16477
- {
16478
- provide: AXPWeatherApiAbstract,
16479
- useClass: AXPWeatherApiService,
16480
- },
16481
- ], viewQueries: [{ propertyName: "containerEl", first: true, predicate: ["containerElement"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<!-- Weather Widget Component Template -->\n<div class=\"axp-weather-container\" #containerElement>\n <!-- Loading indicator -->\n @if (isLoading()) {\n <div class=\"axp-weather-loading-overlay\">\n <div class=\"axp-weather-loading-spinner\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span>Loading weather data...</span>\n </div>\n </div>\n }\n\n <!-- Error message -->\n @if (hasError()) {\n <div class=\"axp-weather-error-overlay\">\n <div class=\"axp-weather-error-message\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n <span>{{ errorMessage() }}</span>\n <button class=\"axp-weather-retry-button\" (click)=\"refreshWeather()\">\n <i class=\"fa-solid fa-sync-alt\"></i>\n <span>Retry</span>\n </button>\n </div>\n </div>\n }\n\n <!-- Weather content - only show when we have data -->\n @if (weatherData()) {\n <!-- Background decorations based on weather condition -->\n <div class=\"axp-weather-background-decorations\">\n <div class=\"axp-weather-decoration\" [ngClass]=\"weatherData()?.current?.condition?.toLowerCase()\"></div>\n <div class=\"axp-weather-gradient-overlay\"></div>\n </div>\n\n <div class=\"axp-weather-inner\">\n <!-- Location information section -->\n <div class=\"axp-weather-location-info\">\n <div class=\"axp-weather-location-icon\">\n <i class=\"fa-solid fa-location-dot\"></i>\n </div>\n <div class=\"axp-weather-location-text\">\n <h2 class=\"axp-weather-location-name\">{{ city() }}</h2>\n </div>\n </div>\n\n <!-- Current weather conditions section -->\n <div class=\"axp-weather-current-weather\">\n <!-- Weather icon and condition name -->\n @if (showCurrentCondition()) {\n <div class=\"axp-weather-condition\">\n <div class=\"axp-weather-icon-wrapper\">\n <div class=\"axp-weather-icon\">\n <i\n [class]=\"getConditionIcon(weatherData()?.current?.condition || '')\"\n [style.color]=\"getConditionColor(weatherData()?.current?.condition || '')\"\n ></i>\n </div>\n <div\n class=\"axp-weather-icon-glow\"\n [style.background-color]=\"getConditionColor(weatherData()?.current?.condition || '')\"\n ></div>\n </div>\n <div class=\"axp-weather-condition-name\">{{ getCurrentCondition() }}</div>\n </div>\n }\n\n <!-- Temperature display -->\n @if (showTemperature()) {\n <div class=\"axp-weather-temperature\">\n <span class=\"axp-weather-temperature-value\">{{ getCurrentTemperature() }}</span>\n <span class=\"axp-weather-temperature-unit\">{{ temperatureUnit() }}</span>\n </div>\n }\n </div>\n\n <!-- Weather details section (humidity and wind) -->\n @if (showHumidity() || showWind()) {\n <div class=\"axp-weather-details\">\n <!-- Humidity information -->\n @if (showHumidity()) {\n <div class=\"axp-weather-detail-item\">\n <div class=\"axp-weather-detail-icon\">\n <i class=\"fa-solid fa-droplet\"></i>\n </div>\n <div class=\"axp-weather-detail-info\">\n <div class=\"axp-weather-detail-label\">Humidity</div>\n <div class=\"axp-weather-detail-value\">{{ getHumidity() }}%</div>\n </div>\n </div>\n }\n\n <!-- Wind speed information -->\n @if (showWind()) {\n <div class=\"axp-weather-detail-item\">\n <div class=\"axp-weather-detail-icon\">\n <i class=\"fa-solid fa-wind\"></i>\n </div>\n <div class=\"axp-weather-detail-info\">\n <div class=\"axp-weather-detail-label\">Wind</div>\n <div class=\"axp-weather-detail-value\">{{ getWindSpeed() }} {{ windSpeedUnit() }}</div>\n </div>\n </div>\n }\n </div>\n }\n\n <!-- Weather forecast section -->\n @if (showForecast() && weatherData()?.forecast) {\n <div class=\"axp-weather-forecast\">\n <div class=\"axp-weather-forecast-header\">\n <h3 class=\"axp-weather-forecast-title\">\n <i class=\"fa-solid fa-calendar-days\"></i>\n <span>Forecast</span>\n </h3>\n <!-- <div class=\"axp-weather-scroll-indicator\">\n <i class=\"fa-solid fa-chevron-right\"></i>\n </div> -->\n </div>\n <!-- Loading indicator for forecast -->\n @if (isForecastLoading()) {\n <div class=\"axp-weather-forecast-loading\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span>Loading forecast...</span>\n </div>\n }\n <!-- Scrollable forecast days display -->\n <div class=\"axp-weather-forecast-items\">\n @for (day of displayedForecast(); track day.day) {\n <div class=\"axp-weather-forecast-day\">\n <div class=\"axp-weather-forecast-day-name\">{{ day.day }}</div>\n <div class=\"axp-weather-forecast-icon\" title=\"{{ getConditionName(day.condition) }}\">\n <i [class]=\"getConditionIcon(day.condition)\" [style.color]=\"getConditionColor(day.condition)\"></i>\n </div>\n <div class=\"axp-weather-forecast-temps\">\n <span class=\"axp-weather-forecast-low\">\n {{ temperatureUnit() === '\u00B0C' ? day.minTempC : day.minTempF }}\u00B0\n </span>\n <span class=\"axp-weather-forecast-high\">\n {{ temperatureUnit() === '\u00B0C' ? day.maxTempC : day.maxTempF }}\u00B0\n </span>\n </div>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Last updated timestamp -->\n <div class=\"axp-weather-last-updated\">\n <span>Last updated: {{ getLastUpdated() }}</span>\n </div>\n\n <!-- Manual refresh button -->\n <div class=\"axp-weather-refresh-action\">\n <button\n class=\"axp-weather-refresh-button\"\n (click)=\"refreshWeather()\"\n [attr.aria-label]=\"'Refresh weather data'\"\n title=\"Refresh weather data\"\n >\n <i class=\"fa-solid fa-sync-alt\"></i>\n <span>Refresh</span>\n </button>\n </div>\n </div>\n } @else if (!isLoading() && !hasError()) {\n <!-- No data state (not loading, no error) -->\n <div class=\"axp-weather-no-data-state\">\n <i class=\"fa-solid fa-cloud-sun\"></i>\n <p>No weather data available</p>\n <button class=\"axp-weather-refresh-button\" (click)=\"refreshWeather()\">\n <i class=\"fa-solid fa-sync-alt\"></i>\n <span>Load Data</span>\n </button>\n </div>\n }\n</div>\n", styles: [":host{display:block;width:100%;height:100%;--primary-gradient-start: #2196f3;--primary-gradient-end: #1976d2;--shadow-color: rgba(0, 0, 0, .2);--glass-bg: rgba(255, 255, 255, .15);--glass-border: rgba(255, 255, 255, .2);--text-primary: rgba(255, 255, 255, .95);--text-secondary: rgba(255, 255, 255, .75);--transition-speed: .3s}.axp-weather-container{width:100%;height:100%;border-radius:8px;overflow:hidden;position:relative;box-shadow:0 4px 8px rgba(0,0,0,.1);color:#fff;min-height:300px;background-color:#2c3e50}.axp-weather-loading-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background-color:rgba(44,62,80,.85);display:flex;justify-content:center;align-items:center;z-index:100}.axp-weather-loading-spinner{text-align:center}.axp-weather-loading-spinner i{font-size:2.5rem;color:#fff;margin-bottom:.5rem}.axp-weather-loading-spinner span{display:block;color:#fff;font-size:1rem}.axp-weather-error-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background-color:rgba(189,54,47,.85);display:flex;justify-content:center;align-items:center;z-index:100}.axp-weather-error-message{text-align:center;padding:1rem}.axp-weather-error-message i{font-size:2.5rem;color:#fff;margin-bottom:.5rem}.axp-weather-error-message span{display:block;color:#fff;font-size:1.1rem;margin-bottom:1rem}.axp-weather-error-message .axp-weather-retry-button{color:#bd362f;border:none;border-radius:4px;padding:.5rem 1rem;font-size:1rem;cursor:pointer;transition:all .3s ease;display:inline-flex;align-items:center;gap:.5rem}.axp-weather-error-message .axp-weather-retry-button:hover{transform:translateY(-1px)}.axp-weather-no-data-state{display:flex;flex-direction:column;justify-content:center;align-items:center;height:100%;padding:2rem;text-align:center}.axp-weather-no-data-state i{font-size:3rem;margin-bottom:1rem;color:rgba(255,255,255,.8)}.axp-weather-no-data-state p{margin-bottom:1.5rem;font-size:1.1rem}.axp-weather-no-data-state .axp-weather-refresh-button{background-color:rgba(255,255,255,.2);color:#fff;border:none;border-radius:4px;padding:.5rem 1rem;font-size:1rem;cursor:pointer;transition:all .3s ease;display:inline-flex;align-items:center;gap:.5rem}.axp-weather-no-data-state .axp-weather-refresh-button:hover{background-color:rgba(255,255,255,.3);transform:translateY(-1px)}.axp-weather-background-decorations{position:absolute;top:0;left:0;width:100%;height:100%;z-index:1}.axp-weather-decoration{position:absolute;top:0;left:0;width:100%;height:100%;background-size:cover;background-position:center}.axp-weather-decoration.sunny{background:linear-gradient(135deg,#ff7e00,#f7d358)}.axp-weather-decoration.partlyCloudy{background:linear-gradient(135deg,#7ba2e7,#b4d2f7)}.axp-weather-decoration.cloudy{background:linear-gradient(135deg,#717e8c,#919eab)}.axp-weather-decoration.rain{background:linear-gradient(135deg,#6a8caf,#567a9e)}.axp-weather-decoration.snow{background:linear-gradient(135deg,#99b3cc,#c6d4e1)}.axp-weather-decoration.thunder{background:linear-gradient(135deg,#425777,#2c3e50)}.axp-weather-decoration.mist{background:linear-gradient(135deg,#94a3b8,#cbd5e1)}.axp-weather-gradient-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background:linear-gradient(rgba(0,0,0,.2),rgba(0,0,0,.5))}.axp-weather-inner{position:relative;z-index:2;height:100%;padding:1.5rem;display:flex;flex-direction:column}.axp-weather-location-info{display:flex;align-items:center;margin-bottom:1.5rem}.axp-weather-location-icon{width:32px;height:32px;display:flex;align-items:center;justify-content:center;background-color:rgba(255,255,255,.2);border-radius:50%;margin-right:.75rem}.axp-weather-location-icon i{color:#fff}.axp-weather-location-name{margin:0;font-size:1.5rem;font-weight:500;text-shadow:1px 1px 3px rgba(0,0,0,.2);text-transform:capitalize}.axp-weather-current-weather{display:flex;align-items:center;justify-content:space-between;margin-bottom:1.5rem;flex-wrap:wrap}.axp-weather-condition{display:flex;flex-direction:column;align-items:center}.axp-weather-icon-wrapper{position:relative;margin-bottom:.5rem}.axp-weather-icon{position:relative;z-index:2}.axp-weather-icon i{font-size:2.75rem;filter:drop-shadow(0 0 8px rgba(0,0,0,.3))}.axp-weather-icon-glow{position:absolute;z-index:1;top:50%;left:50%;transform:translate(-50%,-50%);width:45px;height:45px;border-radius:50%;filter:blur(15px);opacity:.75}.axp-weather-condition-name{font-size:1.1rem;text-align:center;text-shadow:1px 1px 2px rgba(0,0,0,.2)}.axp-weather-temperature{display:flex;align-items:flex-start;text-shadow:1px 1px 3px rgba(0,0,0,.3)}.axp-weather-temperature-value{font-size:3.5rem;font-weight:500;line-height:1}.axp-weather-temperature-unit{font-size:1.5rem;margin-top:.25rem}.axp-weather-details{display:flex;flex-wrap:wrap;gap:1.5rem;margin-bottom:1.5rem;padding:1rem;background-color:rgba(0,0,0,.15);border-radius:8px}.axp-weather-detail-item{display:flex;align-items:center;flex:1;min-width:120px}.axp-weather-detail-icon{width:40px;height:40px;border-radius:50%;background-color:rgba(255,255,255,.2);display:flex;align-items:center;justify-content:center;margin-right:.75rem}.axp-weather-detail-icon i{font-size:1.25rem}.axp-weather-detail-info{display:flex;flex-direction:column}.axp-weather-detail-label{font-size:.875rem;color:rgba(255,255,255,.8);margin-bottom:.25rem}.axp-weather-detail-value{font-size:1.125rem;font-weight:500}.axp-weather-forecast{margin-top:auto;margin-bottom:1rem;background-color:rgba(0,0,0,.15);border-radius:8px;padding:1rem}.axp-weather-forecast-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:.75rem}.axp-weather-forecast-title{margin:0;font-size:1.125rem;font-weight:500;display:flex;align-items:center}.axp-weather-forecast-title i{margin-right:.5rem;opacity:.8}.axp-weather-scroll-indicator{color:rgba(255,255,255,.6)}.axp-weather-forecast-items{display:flex;overflow-x:auto;gap:.75rem;padding-bottom:.5rem}.axp-weather-forecast-items::-webkit-scrollbar{height:4px}.axp-weather-forecast-items::-webkit-scrollbar-thumb{background-color:rgba(255,255,255,.3);border-radius:4px}.axp-weather-forecast-items::-webkit-scrollbar-track{background-color:rgba(0,0,0,.1);border-radius:4px}.axp-weather-forecast-day{min-width:80px;display:flex;flex-direction:column;align-items:center;padding:.75rem .5rem;background-color:rgba(255,255,255,.1);border-radius:6px;transition:all .3s ease}.axp-weather-forecast-day:hover{background-color:rgba(255,255,255,.15);transform:translateY(-2px)}.axp-weather-forecast-day-name{font-size:.875rem;margin-bottom:.5rem;font-weight:500}.axp-weather-forecast-icon{font-size:1.5rem;margin-bottom:.5rem}.axp-weather-forecast-icon i{filter:drop-shadow(0 0 5px rgba(0,0,0,.2))}.axp-weather-forecast-temps{display:flex;flex-direction:row;gap:.75rem;align-items:center;font-size:.875rem}.axp-weather-last-updated{text-align:center;font-size:.75rem;opacity:.7;margin-bottom:.5rem}.axp-weather-refresh-action{text-align:center}.axp-weather-refresh-button{background-color:rgba(255,255,255,.2);color:#fff;border:none;border-radius:4px;padding:.5rem 1rem;font-size:.875rem;cursor:pointer;transition:all .3s ease;display:inline-flex;align-items:center;gap:.5rem}.axp-weather-refresh-button:hover{background-color:rgba(255,255,255,.3);transform:translateY(-1px)}.axp-weather-refresh-button i{font-size:.875rem}@media (max-width: 576px){.axp-weather-inner{padding:.8rem;gap:.4rem}.axp-weather-location-name{font-size:1.1rem}.axp-weather-temperature{font-size:2.5rem}.axp-weather-temperature-unit{font-size:1.1rem}.axp-weather-weather-details{flex-direction:column;gap:.5rem}.axp-weather-forecast-items{flex-wrap:nowrap;overflow-x:auto;padding-bottom:.5rem;margin:0 -.4rem;scroll-behavior:smooth;-webkit-overflow-scrolling:touch;scrollbar-width:thin;mask-image:linear-gradient(to right,#000 95%,rgba(0,0,0,0));-webkit-mask-image:linear-gradient(to right,rgb(0,0,0) 95%,rgba(0,0,0,0))}.axp-weather-forecast-items::-webkit-scrollbar{height:4px}.axp-weather-forecast-items::-webkit-scrollbar-thumb{background-color:rgba(255,255,255,.2);border-radius:4px}.axp-weather-forecast-day{min-width:60px;flex:0 0 auto;padding:.4rem .6rem}.axp-weather-forecast-day-name{font-size:.7rem}.axp-weather-forecast-icon{padding:.3rem;height:1.6rem}.axp-weather-forecast-temps{font-size:.7rem}.axp-weather-scroll-indicator{display:block}}:host-context(.theme-dark){--glass-bg: rgba(0, 0, 0, .3);--glass-border: rgba(255, 255, 255, .1)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: AXDateTimeModule }, { kind: "ngmodule", type: AXFormatModule }, { kind: "ngmodule", type: HttpClientModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
16482
- }
16483
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: AXPWeatherWidgetViewComponent, decorators: [{
16484
- type: Component,
16485
- args: [{ standalone: true, imports: [CommonModule, AXDateTimeModule, AXFormatModule, HttpClientModule], providers: [
16486
- {
16487
- provide: AXPWeatherApiAbstract,
16488
- useClass: AXPWeatherApiService,
16489
- },
16490
- ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- Weather Widget Component Template -->\n<div class=\"axp-weather-container\" #containerElement>\n <!-- Loading indicator -->\n @if (isLoading()) {\n <div class=\"axp-weather-loading-overlay\">\n <div class=\"axp-weather-loading-spinner\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span>Loading weather data...</span>\n </div>\n </div>\n }\n\n <!-- Error message -->\n @if (hasError()) {\n <div class=\"axp-weather-error-overlay\">\n <div class=\"axp-weather-error-message\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n <span>{{ errorMessage() }}</span>\n <button class=\"axp-weather-retry-button\" (click)=\"refreshWeather()\">\n <i class=\"fa-solid fa-sync-alt\"></i>\n <span>Retry</span>\n </button>\n </div>\n </div>\n }\n\n <!-- Weather content - only show when we have data -->\n @if (weatherData()) {\n <!-- Background decorations based on weather condition -->\n <div class=\"axp-weather-background-decorations\">\n <div class=\"axp-weather-decoration\" [ngClass]=\"weatherData()?.current?.condition?.toLowerCase()\"></div>\n <div class=\"axp-weather-gradient-overlay\"></div>\n </div>\n\n <div class=\"axp-weather-inner\">\n <!-- Location information section -->\n <div class=\"axp-weather-location-info\">\n <div class=\"axp-weather-location-icon\">\n <i class=\"fa-solid fa-location-dot\"></i>\n </div>\n <div class=\"axp-weather-location-text\">\n <h2 class=\"axp-weather-location-name\">{{ city() }}</h2>\n </div>\n </div>\n\n <!-- Current weather conditions section -->\n <div class=\"axp-weather-current-weather\">\n <!-- Weather icon and condition name -->\n @if (showCurrentCondition()) {\n <div class=\"axp-weather-condition\">\n <div class=\"axp-weather-icon-wrapper\">\n <div class=\"axp-weather-icon\">\n <i\n [class]=\"getConditionIcon(weatherData()?.current?.condition || '')\"\n [style.color]=\"getConditionColor(weatherData()?.current?.condition || '')\"\n ></i>\n </div>\n <div\n class=\"axp-weather-icon-glow\"\n [style.background-color]=\"getConditionColor(weatherData()?.current?.condition || '')\"\n ></div>\n </div>\n <div class=\"axp-weather-condition-name\">{{ getCurrentCondition() }}</div>\n </div>\n }\n\n <!-- Temperature display -->\n @if (showTemperature()) {\n <div class=\"axp-weather-temperature\">\n <span class=\"axp-weather-temperature-value\">{{ getCurrentTemperature() }}</span>\n <span class=\"axp-weather-temperature-unit\">{{ temperatureUnit() }}</span>\n </div>\n }\n </div>\n\n <!-- Weather details section (humidity and wind) -->\n @if (showHumidity() || showWind()) {\n <div class=\"axp-weather-details\">\n <!-- Humidity information -->\n @if (showHumidity()) {\n <div class=\"axp-weather-detail-item\">\n <div class=\"axp-weather-detail-icon\">\n <i class=\"fa-solid fa-droplet\"></i>\n </div>\n <div class=\"axp-weather-detail-info\">\n <div class=\"axp-weather-detail-label\">Humidity</div>\n <div class=\"axp-weather-detail-value\">{{ getHumidity() }}%</div>\n </div>\n </div>\n }\n\n <!-- Wind speed information -->\n @if (showWind()) {\n <div class=\"axp-weather-detail-item\">\n <div class=\"axp-weather-detail-icon\">\n <i class=\"fa-solid fa-wind\"></i>\n </div>\n <div class=\"axp-weather-detail-info\">\n <div class=\"axp-weather-detail-label\">Wind</div>\n <div class=\"axp-weather-detail-value\">{{ getWindSpeed() }} {{ windSpeedUnit() }}</div>\n </div>\n </div>\n }\n </div>\n }\n\n <!-- Weather forecast section -->\n @if (showForecast() && weatherData()?.forecast) {\n <div class=\"axp-weather-forecast\">\n <div class=\"axp-weather-forecast-header\">\n <h3 class=\"axp-weather-forecast-title\">\n <i class=\"fa-solid fa-calendar-days\"></i>\n <span>Forecast</span>\n </h3>\n <!-- <div class=\"axp-weather-scroll-indicator\">\n <i class=\"fa-solid fa-chevron-right\"></i>\n </div> -->\n </div>\n <!-- Loading indicator for forecast -->\n @if (isForecastLoading()) {\n <div class=\"axp-weather-forecast-loading\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span>Loading forecast...</span>\n </div>\n }\n <!-- Scrollable forecast days display -->\n <div class=\"axp-weather-forecast-items\">\n @for (day of displayedForecast(); track day.day) {\n <div class=\"axp-weather-forecast-day\">\n <div class=\"axp-weather-forecast-day-name\">{{ day.day }}</div>\n <div class=\"axp-weather-forecast-icon\" title=\"{{ getConditionName(day.condition) }}\">\n <i [class]=\"getConditionIcon(day.condition)\" [style.color]=\"getConditionColor(day.condition)\"></i>\n </div>\n <div class=\"axp-weather-forecast-temps\">\n <span class=\"axp-weather-forecast-low\">\n {{ temperatureUnit() === '\u00B0C' ? day.minTempC : day.minTempF }}\u00B0\n </span>\n <span class=\"axp-weather-forecast-high\">\n {{ temperatureUnit() === '\u00B0C' ? day.maxTempC : day.maxTempF }}\u00B0\n </span>\n </div>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Last updated timestamp -->\n <div class=\"axp-weather-last-updated\">\n <span>Last updated: {{ getLastUpdated() }}</span>\n </div>\n\n <!-- Manual refresh button -->\n <div class=\"axp-weather-refresh-action\">\n <button\n class=\"axp-weather-refresh-button\"\n (click)=\"refreshWeather()\"\n [attr.aria-label]=\"'Refresh weather data'\"\n title=\"Refresh weather data\"\n >\n <i class=\"fa-solid fa-sync-alt\"></i>\n <span>Refresh</span>\n </button>\n </div>\n </div>\n } @else if (!isLoading() && !hasError()) {\n <!-- No data state (not loading, no error) -->\n <div class=\"axp-weather-no-data-state\">\n <i class=\"fa-solid fa-cloud-sun\"></i>\n <p>No weather data available</p>\n <button class=\"axp-weather-refresh-button\" (click)=\"refreshWeather()\">\n <i class=\"fa-solid fa-sync-alt\"></i>\n <span>Load Data</span>\n </button>\n </div>\n }\n</div>\n", styles: [":host{display:block;width:100%;height:100%;--primary-gradient-start: #2196f3;--primary-gradient-end: #1976d2;--shadow-color: rgba(0, 0, 0, .2);--glass-bg: rgba(255, 255, 255, .15);--glass-border: rgba(255, 255, 255, .2);--text-primary: rgba(255, 255, 255, .95);--text-secondary: rgba(255, 255, 255, .75);--transition-speed: .3s}.axp-weather-container{width:100%;height:100%;border-radius:8px;overflow:hidden;position:relative;box-shadow:0 4px 8px rgba(0,0,0,.1);color:#fff;min-height:300px;background-color:#2c3e50}.axp-weather-loading-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background-color:rgba(44,62,80,.85);display:flex;justify-content:center;align-items:center;z-index:100}.axp-weather-loading-spinner{text-align:center}.axp-weather-loading-spinner i{font-size:2.5rem;color:#fff;margin-bottom:.5rem}.axp-weather-loading-spinner span{display:block;color:#fff;font-size:1rem}.axp-weather-error-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background-color:rgba(189,54,47,.85);display:flex;justify-content:center;align-items:center;z-index:100}.axp-weather-error-message{text-align:center;padding:1rem}.axp-weather-error-message i{font-size:2.5rem;color:#fff;margin-bottom:.5rem}.axp-weather-error-message span{display:block;color:#fff;font-size:1.1rem;margin-bottom:1rem}.axp-weather-error-message .axp-weather-retry-button{color:#bd362f;border:none;border-radius:4px;padding:.5rem 1rem;font-size:1rem;cursor:pointer;transition:all .3s ease;display:inline-flex;align-items:center;gap:.5rem}.axp-weather-error-message .axp-weather-retry-button:hover{transform:translateY(-1px)}.axp-weather-no-data-state{display:flex;flex-direction:column;justify-content:center;align-items:center;height:100%;padding:2rem;text-align:center}.axp-weather-no-data-state i{font-size:3rem;margin-bottom:1rem;color:rgba(255,255,255,.8)}.axp-weather-no-data-state p{margin-bottom:1.5rem;font-size:1.1rem}.axp-weather-no-data-state .axp-weather-refresh-button{background-color:rgba(255,255,255,.2);color:#fff;border:none;border-radius:4px;padding:.5rem 1rem;font-size:1rem;cursor:pointer;transition:all .3s ease;display:inline-flex;align-items:center;gap:.5rem}.axp-weather-no-data-state .axp-weather-refresh-button:hover{background-color:rgba(255,255,255,.3);transform:translateY(-1px)}.axp-weather-background-decorations{position:absolute;top:0;left:0;width:100%;height:100%;z-index:1}.axp-weather-decoration{position:absolute;top:0;left:0;width:100%;height:100%;background-size:cover;background-position:center}.axp-weather-decoration.sunny{background:linear-gradient(135deg,#ff7e00,#f7d358)}.axp-weather-decoration.partlyCloudy{background:linear-gradient(135deg,#7ba2e7,#b4d2f7)}.axp-weather-decoration.cloudy{background:linear-gradient(135deg,#717e8c,#919eab)}.axp-weather-decoration.rain{background:linear-gradient(135deg,#6a8caf,#567a9e)}.axp-weather-decoration.snow{background:linear-gradient(135deg,#99b3cc,#c6d4e1)}.axp-weather-decoration.thunder{background:linear-gradient(135deg,#425777,#2c3e50)}.axp-weather-decoration.mist{background:linear-gradient(135deg,#94a3b8,#cbd5e1)}.axp-weather-gradient-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background:linear-gradient(rgba(0,0,0,.2),rgba(0,0,0,.5))}.axp-weather-inner{position:relative;z-index:2;height:100%;padding:1.5rem;display:flex;flex-direction:column}.axp-weather-location-info{display:flex;align-items:center;margin-bottom:1.5rem}.axp-weather-location-icon{width:32px;height:32px;display:flex;align-items:center;justify-content:center;background-color:rgba(255,255,255,.2);border-radius:50%;margin-right:.75rem}.axp-weather-location-icon i{color:#fff}.axp-weather-location-name{margin:0;font-size:1.5rem;font-weight:500;text-shadow:1px 1px 3px rgba(0,0,0,.2);text-transform:capitalize}.axp-weather-current-weather{display:flex;align-items:center;justify-content:space-between;margin-bottom:1.5rem;flex-wrap:wrap}.axp-weather-condition{display:flex;flex-direction:column;align-items:center}.axp-weather-icon-wrapper{position:relative;margin-bottom:.5rem}.axp-weather-icon{position:relative;z-index:2}.axp-weather-icon i{font-size:2.75rem;filter:drop-shadow(0 0 8px rgba(0,0,0,.3))}.axp-weather-icon-glow{position:absolute;z-index:1;top:50%;left:50%;transform:translate(-50%,-50%);width:45px;height:45px;border-radius:50%;filter:blur(15px);opacity:.75}.axp-weather-condition-name{font-size:1.1rem;text-align:center;text-shadow:1px 1px 2px rgba(0,0,0,.2)}.axp-weather-temperature{display:flex;align-items:flex-start;text-shadow:1px 1px 3px rgba(0,0,0,.3)}.axp-weather-temperature-value{font-size:3.5rem;font-weight:500;line-height:1}.axp-weather-temperature-unit{font-size:1.5rem;margin-top:.25rem}.axp-weather-details{display:flex;flex-wrap:wrap;gap:1.5rem;margin-bottom:1.5rem;padding:1rem;background-color:rgba(0,0,0,.15);border-radius:8px}.axp-weather-detail-item{display:flex;align-items:center;flex:1;min-width:120px}.axp-weather-detail-icon{width:40px;height:40px;border-radius:50%;background-color:rgba(255,255,255,.2);display:flex;align-items:center;justify-content:center;margin-right:.75rem}.axp-weather-detail-icon i{font-size:1.25rem}.axp-weather-detail-info{display:flex;flex-direction:column}.axp-weather-detail-label{font-size:.875rem;color:rgba(255,255,255,.8);margin-bottom:.25rem}.axp-weather-detail-value{font-size:1.125rem;font-weight:500}.axp-weather-forecast{margin-top:auto;margin-bottom:1rem;background-color:rgba(0,0,0,.15);border-radius:8px;padding:1rem}.axp-weather-forecast-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:.75rem}.axp-weather-forecast-title{margin:0;font-size:1.125rem;font-weight:500;display:flex;align-items:center}.axp-weather-forecast-title i{margin-right:.5rem;opacity:.8}.axp-weather-scroll-indicator{color:rgba(255,255,255,.6)}.axp-weather-forecast-items{display:flex;overflow-x:auto;gap:.75rem;padding-bottom:.5rem}.axp-weather-forecast-items::-webkit-scrollbar{height:4px}.axp-weather-forecast-items::-webkit-scrollbar-thumb{background-color:rgba(255,255,255,.3);border-radius:4px}.axp-weather-forecast-items::-webkit-scrollbar-track{background-color:rgba(0,0,0,.1);border-radius:4px}.axp-weather-forecast-day{min-width:80px;display:flex;flex-direction:column;align-items:center;padding:.75rem .5rem;background-color:rgba(255,255,255,.1);border-radius:6px;transition:all .3s ease}.axp-weather-forecast-day:hover{background-color:rgba(255,255,255,.15);transform:translateY(-2px)}.axp-weather-forecast-day-name{font-size:.875rem;margin-bottom:.5rem;font-weight:500}.axp-weather-forecast-icon{font-size:1.5rem;margin-bottom:.5rem}.axp-weather-forecast-icon i{filter:drop-shadow(0 0 5px rgba(0,0,0,.2))}.axp-weather-forecast-temps{display:flex;flex-direction:row;gap:.75rem;align-items:center;font-size:.875rem}.axp-weather-last-updated{text-align:center;font-size:.75rem;opacity:.7;margin-bottom:.5rem}.axp-weather-refresh-action{text-align:center}.axp-weather-refresh-button{background-color:rgba(255,255,255,.2);color:#fff;border:none;border-radius:4px;padding:.5rem 1rem;font-size:.875rem;cursor:pointer;transition:all .3s ease;display:inline-flex;align-items:center;gap:.5rem}.axp-weather-refresh-button:hover{background-color:rgba(255,255,255,.3);transform:translateY(-1px)}.axp-weather-refresh-button i{font-size:.875rem}@media (max-width: 576px){.axp-weather-inner{padding:.8rem;gap:.4rem}.axp-weather-location-name{font-size:1.1rem}.axp-weather-temperature{font-size:2.5rem}.axp-weather-temperature-unit{font-size:1.1rem}.axp-weather-weather-details{flex-direction:column;gap:.5rem}.axp-weather-forecast-items{flex-wrap:nowrap;overflow-x:auto;padding-bottom:.5rem;margin:0 -.4rem;scroll-behavior:smooth;-webkit-overflow-scrolling:touch;scrollbar-width:thin;mask-image:linear-gradient(to right,#000 95%,rgba(0,0,0,0));-webkit-mask-image:linear-gradient(to right,rgb(0,0,0) 95%,rgba(0,0,0,0))}.axp-weather-forecast-items::-webkit-scrollbar{height:4px}.axp-weather-forecast-items::-webkit-scrollbar-thumb{background-color:rgba(255,255,255,.2);border-radius:4px}.axp-weather-forecast-day{min-width:60px;flex:0 0 auto;padding:.4rem .6rem}.axp-weather-forecast-day-name{font-size:.7rem}.axp-weather-forecast-icon{padding:.3rem;height:1.6rem}.axp-weather-forecast-temps{font-size:.7rem}.axp-weather-scroll-indicator{display:block}}:host-context(.theme-dark){--glass-bg: rgba(0, 0, 0, .3);--glass-border: rgba(255, 255, 255, .1)}\n"] }]
16491
- }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }] });
16492
-
16493
- var weatherWidget_component = /*#__PURE__*/Object.freeze({
16494
- __proto__: null,
16495
- AXPWeatherWidgetViewComponent: AXPWeatherWidgetViewComponent
16496
- });
16497
-
16498
- /**
16499
- * Weather Widget Configuration
16500
- * Provides customization options for displaying weather data and forecast
16501
- */
16502
- const AXPWeatherWidget = {
16503
- name: 'weather',
16504
- title: 'Weather Widget',
16505
- categories: [AXP_WIDGETS_UTILITY_CATEGORY],
16506
- groups: [AXPWidgetGroupEnum.DashboardWidget],
16507
- type: 'dashboard',
16508
- icon: 'fa-light fa-cloud-sun',
16509
- properties: [
16510
- /* Location Settings */
16511
- {
16512
- name: 'city',
16513
- title: 'City',
16514
- group: AXP_DATA_PROPERTY_GROUP,
16515
- schema: {
16516
- defaultValue: 'New York',
16517
- dataType: 'string',
16518
- interface: {
16519
- name: 'city',
16520
- path: 'options.city',
16521
- type: AXPWidgetsCatalog.text,
16522
- },
16523
- },
16524
- visible: true,
16525
- },
16526
- /* Display Options */
16527
- // {
16528
- // name: 'showCurrentCondition',
16529
- // title: 'Show Current Condition',
16530
- // group: AXP_APPEARANCE_PROPERTY_GROUP,
16531
- // schema: {
16532
- // defaultValue: true,
16533
- // dataType: 'boolean',
16534
- // interface: {
16535
- // name: 'showCurrentCondition',
16536
- // path: 'options.showCurrentCondition',
16537
- // type: AXPWidgetsCatalog.toggle,
16538
- // },
16539
- // },
16540
- // visible: true,
16541
- // },
16542
- // {
16543
- // name: 'showTemperature',
16544
- // title: 'Show Temperature',
16545
- // group: AXP_APPEARANCE_PROPERTY_GROUP,
16546
- // schema: {
16547
- // defaultValue: true,
16548
- // dataType: 'boolean',
16549
- // interface: {
16550
- // name: 'showTemperature',
16551
- // path: 'options.showTemperature',
16552
- // type: AXPWidgetsCatalog.toggle,
16553
- // },
16554
- // },
16555
- // visible: true,
16556
- // },
16557
- // {
16558
- // name: 'showCurrentCondition',
16559
- // title: 'Show Current Condition',
16560
- // group: AXP_APPEARANCE_PROPERTY_GROUP,
16561
- // schema: {
16562
- // defaultValue: true,
16563
- // dataType: 'boolean',
16564
- // interface: {
16565
- // name: 'showCurrentCondition',
16566
- // path: 'options.showCurrentCondition',
16567
- // type: AXPWidgetsCatalog.toggle,
16568
- // },
16569
- // },
16570
- // visible: true,
16571
- // },
16572
- // {
16573
- // name: 'showTemperature',
16574
- // title: 'Show Temperature',
16575
- // group: AXP_APPEARANCE_PROPERTY_GROUP,
16576
- // schema: {
16577
- // defaultValue: true,
16578
- // dataType: 'boolean',
16579
- // interface: {
16580
- // name: 'showTemperature',
16581
- // path: 'options.showTemperature',
16582
- // type: AXPWidgetsCatalog.toggle,
16583
- // },
16584
- // },
16585
- // visible: true,
16586
- // },
16587
- {
16588
- name: 'showHumidity',
16589
- title: 'Show Humidity',
16590
- group: AXP_APPEARANCE_PROPERTY_GROUP,
16591
- schema: {
16592
- defaultValue: true,
16593
- dataType: 'boolean',
16594
- interface: {
16595
- name: 'showHumidity',
16596
- path: 'options.showHumidity',
16597
- type: AXPWidgetsCatalog.toggle,
16598
- },
16599
- },
16600
- visible: true,
16601
- },
16602
- {
16603
- name: 'showWind',
16604
- title: 'Show Wind Speed',
16605
- group: AXP_APPEARANCE_PROPERTY_GROUP,
16606
- schema: {
16607
- defaultValue: true,
16608
- dataType: 'boolean',
16609
- interface: {
16610
- name: 'showWind',
16611
- path: 'options.showWind',
16612
- type: AXPWidgetsCatalog.toggle,
16613
- },
16614
- },
16615
- visible: true,
16616
- },
16617
- {
16618
- name: 'showForecast',
16619
- title: 'Show Forecast',
16620
- group: AXP_APPEARANCE_PROPERTY_GROUP,
16621
- schema: {
16622
- defaultValue: true,
16623
- dataType: 'boolean',
16624
- interface: {
16625
- name: 'showForecast',
16626
- path: 'options.showForecast',
16627
- type: AXPWidgetsCatalog.toggle,
16628
- },
16629
- },
16630
- visible: true,
16631
- },
16632
- {
16633
- name: 'forecastDays',
16634
- title: 'Forecast Days',
16635
- group: AXP_APPEARANCE_PROPERTY_GROUP,
16636
- schema: {
16637
- defaultValue: 5,
16638
- dataType: 'number',
16639
- interface: {
16640
- name: 'forecastDays',
16641
- path: 'options.forecastDays',
16642
- type: AXPWidgetsCatalog.number,
16643
- options: {
16644
- minValue: 1,
16645
- maxValue: 7,
16646
- step: 1,
16647
- },
16648
- },
16649
- },
16650
- visible: true,
16651
- },
16652
- /* Units Settings */
16653
- {
16654
- name: 'temperatureUnit',
16655
- title: 'Temperature Unit',
16656
- group: AXP_DATA_PROPERTY_GROUP,
16657
- schema: {
16658
- defaultValue: '°C',
16659
- dataType: 'string',
16660
- interface: {
16661
- name: 'temperatureUnit',
16662
- path: 'options.temperatureUnit',
16663
- type: AXPWidgetsCatalog.select,
16664
- options: {
16665
- dataSource: ['°C', '°F'],
16666
- },
16667
- },
16668
- },
16669
- visible: true,
16670
- },
16671
- {
16672
- name: 'windSpeedUnit',
16673
- title: 'Wind Speed Unit',
16674
- group: AXP_DATA_PROPERTY_GROUP,
16675
- schema: {
16676
- defaultValue: 'km/h',
16677
- dataType: 'string',
16678
- interface: {
16679
- name: 'windSpeedUnit',
16680
- path: 'options.windSpeedUnit',
16681
- type: AXPWidgetsCatalog.select,
16682
- options: {
16683
- dataSource: ['km/h', 'mph', 'm/s'],
16684
- },
16685
- },
16686
- },
16687
- visible: true,
16688
- },
16689
- /* Refresh Settings */
16690
- {
16691
- name: 'autoRefresh',
16692
- title: 'Auto Refresh',
16693
- group: AXP_BEHAVIOR_PROPERTY_GROUP,
16694
- schema: {
16695
- defaultValue: true,
16696
- dataType: 'boolean',
16697
- interface: {
16698
- name: 'autoRefresh',
16699
- path: 'options.autoRefresh',
16700
- type: AXPWidgetsCatalog.toggle,
16701
- },
16702
- },
16703
- visible: true,
16704
- },
16705
- {
16706
- name: 'refreshInterval',
16707
- title: 'Refresh Interval (minutes)',
16708
- group: AXP_BEHAVIOR_PROPERTY_GROUP,
16709
- schema: {
16710
- defaultValue: 15,
16711
- dataType: 'number',
16712
- interface: {
16713
- name: 'refreshInterval',
16714
- path: 'options.refreshInterval',
16715
- type: AXPWidgetsCatalog.select,
16716
- options: {
16717
- dataSource: [5, 10, 15, 30, 60],
16718
- },
16719
- },
16720
- },
16721
- visible: true,
16722
- },
16723
- ],
16724
- components: {
16725
- view: {
16726
- component: () => Promise.resolve().then(function () { return weatherWidget_component; }).then((c) => c.AXPWeatherWidgetViewComponent),
16727
- },
16728
- },
16729
- meta: {
16730
- dimensions: {
16731
- width: 3,
16732
- height: 7,
16733
- minWidth: 2,
16734
- minHeight: 4,
16735
- maxWidth: 4,
16736
- maxHeight: 8,
16737
- },
16738
- },
16739
- };
16740
-
16741
- // export interface AXPWidgetOptions {
16742
- // }
16743
- // export interface AXPWidgetConfig<T extends AXPWidgetOptions> {
16744
- // name: string;
16745
- // options: T;
16746
- // }
16747
- // export interface AXpTextBoxWidget {
16748
- // }
16749
- // export interface AXPTextBoxWidgetOptions {
16750
- // disabled?: boolean;
16751
- // readonly?: boolean;
16752
- // hasClearButton?: boolean;
16753
- // placeholder?: string;
16754
- // }
16755
-
16756
12389
  class AXPCronJobWidgetViewComponent extends AXPValueWidgetComponent {
16757
12390
  constructor() {
16758
12391
  super(...arguments);
@@ -17819,7 +13452,7 @@ class AXPTabularDataWidgetEditComponent extends AXPValueWidgetComponent {
17819
13452
  this.platform = inject(AXPlatform);
17820
13453
  }
17821
13454
  async openPopup() {
17822
- const { AXPTabularDataPopupComponent } = await import('./acorex-platform-widgets-tabular-data-edit-popup.component-CybYV1Kf.mjs');
13455
+ const { AXPTabularDataPopupComponent } = await import('./acorex-platform-widgets-tabular-data-edit-popup.component-1IseEVXQ.mjs');
17823
13456
  const popupData = await this.popupService.open(AXPTabularDataPopupComponent, {
17824
13457
  size: this.platform.is('Mobile') || this.platform.is('SM') ? 'full' : this.columns().length > 3 ? 'lg' : 'md',
17825
13458
  header: true,
@@ -19974,20 +15607,10 @@ class AXPWidgetsModule {
19974
15607
  AXPBetweenExpressionValidationWidget,
19975
15608
  AXPEqualValidationWidget,
19976
15609
  AXPCallbackValidationWidget,
19977
- //charts
19978
- AXPDonutChartWidget,
19979
- AXPBarChartWidget,
19980
- AXPLineChartWidget,
19981
- AXPGaugeChartWidget,
19982
- AXPStickyNoteWidget,
19983
- AXPClockCalendarWidget,
19984
- AXPWeatherWidget,
19985
15610
  AXPMetaDataWidget,
19986
15611
  //Custom Widgets
19987
15612
  AXPNumberUnitBoxWidget,
19988
15613
  AXPPanelWidget,
19989
- AXPNotificationWidget,
19990
- AXPTaskListWidget,
19991
15614
  ],
19992
15615
  })] }); }
19993
15616
  }
@@ -20055,20 +15678,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.3", ngImpor
20055
15678
  AXPBetweenExpressionValidationWidget,
20056
15679
  AXPEqualValidationWidget,
20057
15680
  AXPCallbackValidationWidget,
20058
- //charts
20059
- AXPDonutChartWidget,
20060
- AXPBarChartWidget,
20061
- AXPLineChartWidget,
20062
- AXPGaugeChartWidget,
20063
- AXPStickyNoteWidget,
20064
- AXPClockCalendarWidget,
20065
- AXPWeatherWidget,
20066
15681
  AXPMetaDataWidget,
20067
15682
  //Custom Widgets
20068
15683
  AXPNumberUnitBoxWidget,
20069
15684
  AXPPanelWidget,
20070
- AXPNotificationWidget,
20071
- AXPTaskListWidget,
20072
15685
  ],
20073
15686
  }),
20074
15687
  ],
@@ -20085,5 +15698,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.3", ngImpor
20085
15698
  * Generated bundle index. Do not edit.
20086
15699
  */
20087
15700
 
20088
- export { AXPAdvancedGridItemWidget, AXPAdvancedGridItemWidgetDesignerComponent, AXPAdvancedGridItemWidgetPrintComponent, AXPAdvancedGridItemWidgetViewComponent, AXPAdvancedGridOptionsWidget, AXPAdvancedGridOptionsWidgetEditComponent, AXPAdvancedGridWidget, AXPAdvancedGridWidgetDesignerComponent, AXPAdvancedGridWidgetViewComponent, AXPAvatarWidget, AXPAvatarWidgetColumnComponent, AXPAvatarWidgetDesignerComponent, AXPAvatarWidgetEditComponent, AXPAvatarWidgetPrintComponent, AXPAvatarWidgetViewComponent, AXPBarChartWidget, AXPBarChartWidgetViewComponent, AXPBetweenExpressionValidationWidget, AXPBetweenValidationWidgetEditComponent, AXPBlockWidget, AXPBlockWidgetDesignerComponent, AXPBlockWidgetViewComponent, AXPBorderWidget, AXPBorderWidgetEditComponent, AXPButtonWidget, AXPButtonWidgetViewComponent, AXPCallbackValidationWidget, AXPCallbackValidationWidgetEditComponent, AXPChartBaseComponent, AXPChartColors, AXPChartTooltipComponent, AXPCheckBoxWidget, AXPCheckBoxWidgetEditComponent, AXPClockCalendarWidget, AXPClockCalendarWidgetViewComponent, AXPContactWidget, AXPContactWidgetColumnComponent, AXPContactWidgetEditComponent, AXPContactWidgetPrintComponent, AXPContactWidgetViewComponent, AXPDateTimeBoxWidget, AXPDateTimeBoxWidgetColumnComponent, AXPDateTimeBoxWidgetEditComponent, AXPDateTimeBoxWidgetFilterComponent, AXPDateTimeBoxWidgetPrintComponent, AXPDateTimeBoxWidgetViewComponent, AXPDonutChartWidget, AXPDonutChartWidgetViewComponent, AXPEmailBoxWidget, AXPEmailBoxWidgetColumnComponent, AXPEmailBoxWidgetEditComponent, AXPEmailBoxWidgetFilterComponent, AXPEmailBoxWidgetPrintComponent, AXPEmailBoxWidgetViewComponent, AXPEqualValidationWidget, AXPEqualValidationWidgetEditComponent, AXPFileBoxWidget, AXPFileBoxWidgetColumnComponent, AXPFileBoxWidgetEditComponent, AXPFileBoxWidgetFilterComponent, AXPFileBoxWidgetPrintComponent, AXPFileBoxWidgetViewComponent, AXPFileManagementService, AXPFlexOptionsWidget, AXPFlexOptionsWidgetEditComponent, AXPGalleryWidget, AXPGalleryWidgetEditComponent, AXPGalleryWidgetPrintComponent, AXPGalleryWidgetViewComponent, AXPGaugeChartWidget, AXPGaugeChartWidgetViewComponent, AXPGreaterThanExpressionValidationWidget, AXPGreaterThanValidationWidgetEditComponent, AXPGridOptionsWidget, AXPGridOptionsWidgetEditComponent, AXPLargeTextWidget, AXPLargeTextWidgetColumnComponent, AXPLargeTextWidgetEditComponent, AXPLargeTextWidgetFilterComponent, AXPLargeTextWidgetPrintComponent, AXPLargeTextWidgetViewComponent, AXPLessThanExpressionValidationWidget, AXPLessThanValidationWidgetEditComponent, AXPLineChartWidget, AXPLineChartWidgetViewComponent, AXPLinkWidget, AXPLinkWidgetColumnComponent, AXPLinkWidgetEditComponent, AXPLinkWidgetFilterComponent, AXPLinkWidgetPrintComponent, AXPLinkWidgetViewComponent, AXPMapBoxWidget, AXPMapBoxWidgetEditComponent, AXPMapBoxWidgetViewComponent, AXPMaxLengthExpressionValidationWidget, AXPMaxLengthValidationWidgetEditComponent, AXPMinLengthExpressionValidationWidget, AXPMinLengthValidationWidgetEditComponent, AXPNotificationWidget, AXPNotificationWidgetViewComponent, AXPNumberBoxWidget, AXPNumberBoxWidgetColumnComponent, AXPNumberBoxWidgetEditComponent, AXPNumberBoxWidgetFilterComponent, AXPNumberBoxWidgetPrintComponent, AXPNumberBoxWidgetViewComponent, AXPPageWidget, AXPPageWidgetViewComponent, AXPPasswordBoxWidget, AXPPasswordBoxWidgetColumnComponent, AXPPasswordBoxWidgetEditComponent, AXPPasswordBoxWidgetFilterComponent, AXPPasswordBoxWidgetPrintComponent, AXPPasswordBoxWidgetViewComponent, AXPPhoneBoxWidget, AXPPhoneBoxWidgetColumnComponent, AXPPhoneBoxWidgetEditComponent, AXPPhoneBoxWidgetFilterComponent, AXPPhoneBoxWidgetPrintComponent, AXPPhoneBoxWidgetViewComponent, AXPPropertyEditorHelper, AXPRegularExpressionValidationWidget, AXPRegularExpressionValidationWidgetEditComponent, AXPRepeaterWidget, AXPRepeaterWidgetDesignerComponent, AXPRepeaterWidgetEditComponent, AXPRepeaterWidgetPrintComponent, AXPRepeaterWidgetViewComponent, AXPRequiredValidationWidget, AXPRequiredValidationWidgetEditComponent, AXPRichTextWidget, AXPRichTextWidgetColumnComponent, AXPRichTextWidgetEditComponent, AXPRichTextWidgetFilterComponent, AXPRichTextWidgetPrintComponent, AXPRichTextWidgetViewComponent, AXPSelectBoxWidget, AXPSelectBoxWidgetColumnComponent, AXPSelectBoxWidgetEditComponent, AXPSelectBoxWidgetFilterComponent, AXPSelectBoxWidgetPrintComponent, AXPSelectBoxWidgetViewComponent, AXPSelectionListWidget, AXPSelectionListWidgetColumnComponent, AXPSelectionListWidgetDesignerComponent, AXPSelectionListWidgetEditComponent, AXPSelectionListWidgetFilterComponent, AXPSelectionListWidgetPrintComponent, AXPSelectionListWidgetViewComponent, AXPSignatureWidget, AXPSignatureWidgetColumnComponent, AXPSignatureWidgetEditComponent, AXPSignatureWidgetFilterComponent, AXPSignatureWidgetPrintComponent, AXPSignatureWidgetViewComponent, AXPSingleFileBoxWidget, AXPSingleFileBoxWidgetColumnComponent, AXPSingleFileBoxWidgetEditComponent, AXPSingleFileBoxWidgetFilterComponent, AXPSingleFileBoxWidgetPrintComponent, AXPSingleFileBoxWidgetViewComponent, AXPSpacingWidget, AXPSpacingWidgetEditComponent, AXPStickyNoteWidget, AXPStickyNoteWidgetViewComponent, AXPTaskListWidget, AXPTaskListWidgetViewComponent, AXPTemplateBoxWidget, AXPTemplateBoxWidgetColumnComponent, AXPTemplateBoxWidgetEditComponent, AXPTemplateBoxWidgetFilterComponent, AXPTemplateBoxWidgetPrintComponent, AXPTemplateBoxWidgetViewComponent, AXPTextBoxWidget, AXPTextBoxWidgetColumnComponent, AXPTextBoxWidgetEditComponent, AXPTextBoxWidgetFilterComponent, AXPTextBoxWidgetPrintComponent$1 as AXPTextBoxWidgetPrintComponent, AXPTextBoxWidgetViewComponent, AXPToggleWidget, AXPToggleWidgetColumnComponent, AXPToggleWidgetEditComponent, AXPToggleWidgetFilterComponent, AXPToggleWidgetPrintComponent, AXPToggleWidgetViewComponent, AXPWeatherApiAbstract, AXPWeatherApiMockService, AXPWeatherApiService, AXPWeatherWidget, AXPWeatherWidgetViewComponent, AXPWidgetsModule, AXP_ALLOW_MULTIPLE_PROPERTY, AXP_ALLOW_SEARCH_PROPERTY, AXP_ANIMATION_PROPERTY_GROUP, AXP_APPEARANCE_PROPERTY_GROUP, AXP_BEHAVIOR_PROPERTY_GROUP, AXP_BETWEEN_VALIDATION_PROPERTY, AXP_BG_COLOR_PROPERTY, AXP_BOX_MODEL_PROPERTY_GROUP, AXP_CALLBACK_VALIDATION_PROPERTY, AXP_COLOR_PROPERTY, AXP_CONTENT_PROPERTY, AXP_DATA_PATH_PROPERTY, AXP_DATA_PROPERTY_GROUP, AXP_DATA_SOURCE_PROPERTIES, AXP_DATA_SOURCE_PROPERTY, AXP_DATA_SOURCE_TEXT_FIELD, AXP_DATA_SOURCE_VALUE_FIELD, AXP_DATE_FORMAT_OPTIONS, AXP_DATE_FORMAT_PROPERTY, AXP_DESCRIPTION_PROPERTY, AXP_DIRECTION_PROPERTY, AXP_DISABLED_PROPERTY, AXP_DOWNLOADABLE_PROPERTY, AXP_EQUAL_VALIDATION_PROPERTY, AXP_FALSY_TEXT_PROPERTY, AXP_FONT_SIZE_PROPERTY, AXP_Flex_Box_Align_Options, AXP_Flex_Box_Alignments, AXP_Flex_Box_Justify_Options, AXP_GREATER_THAN_VALIDATION_PROPERTY, AXP_Grid_Box_Align_Items_Options, AXP_Grid_Box_Alignments, AXP_Grid_Box_Justify_Items_Options, AXP_HAS_CLEAR_BUTTON_PROPERTY, AXP_HAS_COPY_ICON_PROPERTY, AXP_HAS_EYE_ICON_PROPERTY, AXP_HAS_ICON_PROPERTY, AXP_HAS_LABEL_PROPERTY, AXP_ICON_PROPERTY, AXP_IS_LOADING_PROPERTY, AXP_LABEL_PROPERTY, AXP_LAYOUT_ADVANCED_GRID_PROPERTY, AXP_LAYOUT_BORDER_PROPERTY, AXP_LAYOUT_COLUMNS_PROPERTY, AXP_LAYOUT_COL_END_PROPERTY, AXP_LAYOUT_COL_SPAN_PROPERTY, AXP_LAYOUT_COL_START_PROPERTY, AXP_LAYOUT_FLEX_PROPERTY, AXP_LAYOUT_FLEX_PROPERTY_GROUP, AXP_LAYOUT_GAP_PROPERTY, AXP_LAYOUT_GRID_ITEM_PROPERTIES, AXP_LAYOUT_GRID_PROPERTIES, AXP_LAYOUT_GRID_PROPERTY, AXP_LAYOUT_GRID_PROPERTY_GROUP, AXP_LAYOUT_GRID_ROW_PROPERTIES, AXP_LAYOUT_ROWS_PROPERTY, AXP_LAYOUT_SPACING_PROPERTY, AXP_LESS_THAN_VALIDATION_PROPERTY, AXP_MAX_LENGTH_VALIDATION_PROPERTY, AXP_MIN_LENGTH_VALIDATION_PROPERTY, AXP_NAME_PROPERTY, AXP_PLACEHOLDER_PROPERTY, AXP_READONLY_PROPERTY, AXP_REGULAR_EXPRESSION_VALIDATION_PROPERTY, AXP_REQUIRED_VALIDATION_PROPERTY, AXP_STYLE_COLOR_PROPERTY, AXP_STYLE_LOOK_PROPERTY, AXP_STYLING_PROPERTY_GROUP, AXP_TABLE_COLUMN_HEIGHT_PROPERTY, AXP_TABLE_COLUMN_WIDTH_PROPERTY, AXP_TEXT_FIELD_PROPERTY, AXP_TEXT_PROPERTY, AXP_THEME_PROPERTY, AXP_TIMEZONE_OPTIONS, AXP_TITLE_PROPERTY, AXP_TRULY_TEXT_PROPERTY, AXP_VALIDATION_PROPERTY_GROUP, AXP_VALUE_FIELD_PROPERTY, AXP_WIDGETS_CHART_CATEGORY, AXP_WIDGETS_UTILITY_CATEGORY, AXP_WIDGET_PROPERTY_GROUP, AXP_default_Border_Box_Units, AXP_default_Border_Box_Value, AXP_default_Spacing_Box_Units, AXP_default_Spacing_Box_Value, DEFAULT_STRATEGY_CONFIG, STRATEGY_CONFIG_TOKEN, booleanDefaultProperty, findNonEmptyBreakpoints, largeTextDefaultProperty, loadD3, numberDefaultProperty, numberMaxValueProperty, numberMinValueProperty, plainTextDefaultProperty };
15701
+ export { AXPAdvancedGridItemWidget, AXPAdvancedGridItemWidgetDesignerComponent, AXPAdvancedGridItemWidgetPrintComponent, AXPAdvancedGridItemWidgetViewComponent, AXPAdvancedGridOptionsWidget, AXPAdvancedGridOptionsWidgetEditComponent, AXPAdvancedGridWidget, AXPAdvancedGridWidgetDesignerComponent, AXPAdvancedGridWidgetViewComponent, AXPAvatarWidget, AXPAvatarWidgetColumnComponent, AXPAvatarWidgetDesignerComponent, AXPAvatarWidgetEditComponent, AXPAvatarWidgetPrintComponent, AXPAvatarWidgetViewComponent, AXPBetweenExpressionValidationWidget, AXPBetweenValidationWidgetEditComponent, AXPBlockWidget, AXPBlockWidgetDesignerComponent, AXPBlockWidgetViewComponent, AXPBorderWidget, AXPBorderWidgetEditComponent, AXPButtonWidget, AXPButtonWidgetViewComponent, AXPCallbackValidationWidget, AXPCallbackValidationWidgetEditComponent, AXPCheckBoxWidget, AXPCheckBoxWidgetEditComponent, AXPContactWidget, AXPContactWidgetColumnComponent, AXPContactWidgetEditComponent, AXPContactWidgetPrintComponent, AXPContactWidgetViewComponent, AXPDateTimeBoxWidget, AXPDateTimeBoxWidgetColumnComponent, AXPDateTimeBoxWidgetEditComponent, AXPDateTimeBoxWidgetFilterComponent, AXPDateTimeBoxWidgetPrintComponent, AXPDateTimeBoxWidgetViewComponent, AXPEmailBoxWidget, AXPEmailBoxWidgetColumnComponent, AXPEmailBoxWidgetEditComponent, AXPEmailBoxWidgetFilterComponent, AXPEmailBoxWidgetPrintComponent, AXPEmailBoxWidgetViewComponent, AXPEqualValidationWidget, AXPEqualValidationWidgetEditComponent, AXPFileBoxWidget, AXPFileBoxWidgetColumnComponent, AXPFileBoxWidgetEditComponent, AXPFileBoxWidgetFilterComponent, AXPFileBoxWidgetPrintComponent, AXPFileBoxWidgetViewComponent, AXPFileManagementService, AXPFlexOptionsWidget, AXPFlexOptionsWidgetEditComponent, AXPGalleryWidget, AXPGalleryWidgetEditComponent, AXPGalleryWidgetPrintComponent, AXPGalleryWidgetViewComponent, AXPGreaterThanExpressionValidationWidget, AXPGreaterThanValidationWidgetEditComponent, AXPGridOptionsWidget, AXPGridOptionsWidgetEditComponent, AXPLargeTextWidget, AXPLargeTextWidgetColumnComponent, AXPLargeTextWidgetEditComponent, AXPLargeTextWidgetFilterComponent, AXPLargeTextWidgetPrintComponent, AXPLargeTextWidgetViewComponent, AXPLessThanExpressionValidationWidget, AXPLessThanValidationWidgetEditComponent, AXPLinkWidget, AXPLinkWidgetColumnComponent, AXPLinkWidgetEditComponent, AXPLinkWidgetFilterComponent, AXPLinkWidgetPrintComponent, AXPLinkWidgetViewComponent, AXPMapBoxWidget, AXPMapBoxWidgetEditComponent, AXPMapBoxWidgetViewComponent, AXPMaxLengthExpressionValidationWidget, AXPMaxLengthValidationWidgetEditComponent, AXPMinLengthExpressionValidationWidget, AXPMinLengthValidationWidgetEditComponent, AXPNumberBoxWidget, AXPNumberBoxWidgetColumnComponent, AXPNumberBoxWidgetEditComponent, AXPNumberBoxWidgetFilterComponent, AXPNumberBoxWidgetPrintComponent, AXPNumberBoxWidgetViewComponent, AXPPageWidget, AXPPageWidgetViewComponent, AXPPasswordBoxWidget, AXPPasswordBoxWidgetColumnComponent, AXPPasswordBoxWidgetEditComponent, AXPPasswordBoxWidgetFilterComponent, AXPPasswordBoxWidgetPrintComponent, AXPPasswordBoxWidgetViewComponent, AXPPhoneBoxWidget, AXPPhoneBoxWidgetColumnComponent, AXPPhoneBoxWidgetEditComponent, AXPPhoneBoxWidgetFilterComponent, AXPPhoneBoxWidgetPrintComponent, AXPPhoneBoxWidgetViewComponent, AXPPropertyEditorHelper, AXPRegularExpressionValidationWidget, AXPRegularExpressionValidationWidgetEditComponent, AXPRepeaterWidget, AXPRepeaterWidgetDesignerComponent, AXPRepeaterWidgetEditComponent, AXPRepeaterWidgetPrintComponent, AXPRepeaterWidgetViewComponent, AXPRequiredValidationWidget, AXPRequiredValidationWidgetEditComponent, AXPRichTextWidget, AXPRichTextWidgetColumnComponent, AXPRichTextWidgetEditComponent, AXPRichTextWidgetFilterComponent, AXPRichTextWidgetPrintComponent, AXPRichTextWidgetViewComponent, AXPSelectBoxWidget, AXPSelectBoxWidgetColumnComponent, AXPSelectBoxWidgetEditComponent, AXPSelectBoxWidgetFilterComponent, AXPSelectBoxWidgetPrintComponent, AXPSelectBoxWidgetViewComponent, AXPSelectionListWidget, AXPSelectionListWidgetColumnComponent, AXPSelectionListWidgetDesignerComponent, AXPSelectionListWidgetEditComponent, AXPSelectionListWidgetFilterComponent, AXPSelectionListWidgetPrintComponent, AXPSelectionListWidgetViewComponent, AXPSignatureWidget, AXPSignatureWidgetColumnComponent, AXPSignatureWidgetEditComponent, AXPSignatureWidgetFilterComponent, AXPSignatureWidgetPrintComponent, AXPSignatureWidgetViewComponent, AXPSingleFileBoxWidget, AXPSingleFileBoxWidgetColumnComponent, AXPSingleFileBoxWidgetEditComponent, AXPSingleFileBoxWidgetFilterComponent, AXPSingleFileBoxWidgetPrintComponent, AXPSingleFileBoxWidgetViewComponent, AXPSpacingWidget, AXPSpacingWidgetEditComponent, AXPTemplateBoxWidget, AXPTemplateBoxWidgetColumnComponent, AXPTemplateBoxWidgetEditComponent, AXPTemplateBoxWidgetFilterComponent, AXPTemplateBoxWidgetPrintComponent, AXPTemplateBoxWidgetViewComponent, AXPTextBoxWidget, AXPTextBoxWidgetColumnComponent, AXPTextBoxWidgetEditComponent, AXPTextBoxWidgetFilterComponent, AXPTextBoxWidgetPrintComponent$1 as AXPTextBoxWidgetPrintComponent, AXPTextBoxWidgetViewComponent, AXPToggleWidget, AXPToggleWidgetColumnComponent, AXPToggleWidgetEditComponent, AXPToggleWidgetFilterComponent, AXPToggleWidgetPrintComponent, AXPToggleWidgetViewComponent, AXPWidgetsModule, AXP_ALLOW_MULTIPLE_PROPERTY, AXP_ALLOW_SEARCH_PROPERTY, AXP_ANIMATION_PROPERTY_GROUP, AXP_APPEARANCE_PROPERTY_GROUP, AXP_BEHAVIOR_PROPERTY_GROUP, AXP_BETWEEN_VALIDATION_PROPERTY, AXP_BG_COLOR_PROPERTY, AXP_BOX_MODEL_PROPERTY_GROUP, AXP_CALLBACK_VALIDATION_PROPERTY, AXP_COLOR_PROPERTY, AXP_CONTENT_PROPERTY, AXP_DATA_PATH_PROPERTY, AXP_DATA_PROPERTY_GROUP, AXP_DATA_SOURCE_PROPERTIES, AXP_DATA_SOURCE_PROPERTY, AXP_DATA_SOURCE_TEXT_FIELD, AXP_DATA_SOURCE_VALUE_FIELD, AXP_DATE_FORMAT_PROPERTY, AXP_DESCRIPTION_PROPERTY, AXP_DIRECTION_PROPERTY, AXP_DISABLED_PROPERTY, AXP_DOWNLOADABLE_PROPERTY, AXP_EQUAL_VALIDATION_PROPERTY, AXP_FALSY_TEXT_PROPERTY, AXP_FONT_SIZE_PROPERTY, AXP_Flex_Box_Align_Options, AXP_Flex_Box_Alignments, AXP_Flex_Box_Justify_Options, AXP_GREATER_THAN_VALIDATION_PROPERTY, AXP_Grid_Box_Align_Items_Options, AXP_Grid_Box_Alignments, AXP_Grid_Box_Justify_Items_Options, AXP_HAS_CLEAR_BUTTON_PROPERTY, AXP_HAS_COPY_ICON_PROPERTY, AXP_HAS_EYE_ICON_PROPERTY, AXP_HAS_ICON_PROPERTY, AXP_HAS_LABEL_PROPERTY, AXP_ICON_PROPERTY, AXP_IS_LOADING_PROPERTY, AXP_LABEL_PROPERTY, AXP_LAYOUT_ADVANCED_GRID_PROPERTY, AXP_LAYOUT_BORDER_PROPERTY, AXP_LAYOUT_COLUMNS_PROPERTY, AXP_LAYOUT_COL_END_PROPERTY, AXP_LAYOUT_COL_SPAN_PROPERTY, AXP_LAYOUT_COL_START_PROPERTY, AXP_LAYOUT_FLEX_PROPERTY, AXP_LAYOUT_FLEX_PROPERTY_GROUP, AXP_LAYOUT_GAP_PROPERTY, AXP_LAYOUT_GRID_ITEM_PROPERTIES, AXP_LAYOUT_GRID_PROPERTIES, AXP_LAYOUT_GRID_PROPERTY, AXP_LAYOUT_GRID_PROPERTY_GROUP, AXP_LAYOUT_GRID_ROW_PROPERTIES, AXP_LAYOUT_ROWS_PROPERTY, AXP_LAYOUT_SPACING_PROPERTY, AXP_LESS_THAN_VALIDATION_PROPERTY, AXP_MAX_LENGTH_VALIDATION_PROPERTY, AXP_MIN_LENGTH_VALIDATION_PROPERTY, AXP_NAME_PROPERTY, AXP_PLACEHOLDER_PROPERTY, AXP_READONLY_PROPERTY, AXP_REGULAR_EXPRESSION_VALIDATION_PROPERTY, AXP_REQUIRED_VALIDATION_PROPERTY, AXP_STYLE_COLOR_PROPERTY, AXP_STYLE_LOOK_PROPERTY, AXP_STYLING_PROPERTY_GROUP, AXP_TABLE_COLUMN_HEIGHT_PROPERTY, AXP_TABLE_COLUMN_WIDTH_PROPERTY, AXP_TEXT_FIELD_PROPERTY, AXP_TEXT_PROPERTY, AXP_THEME_PROPERTY, AXP_TITLE_PROPERTY, AXP_TRULY_TEXT_PROPERTY, AXP_VALIDATION_PROPERTY_GROUP, AXP_VALUE_FIELD_PROPERTY, AXP_WIDGET_PROPERTY_GROUP, AXP_default_Border_Box_Units, AXP_default_Border_Box_Value, AXP_default_Spacing_Box_Units, AXP_default_Spacing_Box_Value, DEFAULT_STRATEGY_CONFIG, STRATEGY_CONFIG_TOKEN, booleanDefaultProperty, findNonEmptyBreakpoints, largeTextDefaultProperty, numberDefaultProperty, numberMaxValueProperty, numberMinValueProperty, plainTextDefaultProperty };
20089
15702
  //# sourceMappingURL=acorex-platform-widgets.mjs.map