@sneat/components 0.1.2 → 0.1.4

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 (170) hide show
  1. package/esm2022/index.js +15 -0
  2. package/esm2022/index.js.map +1 -0
  3. package/esm2022/lib/app-version/app-version.component.js +16 -0
  4. package/esm2022/lib/app-version/app-version.component.js.map +1 -0
  5. package/esm2022/lib/app-version/build-info.js +5 -0
  6. package/esm2022/lib/app-version/build-info.js.map +1 -0
  7. package/esm2022/lib/app-version/index.js +3 -0
  8. package/esm2022/lib/app-version/index.js.map +1 -0
  9. package/esm2022/lib/card-list/index.js +2 -0
  10. package/esm2022/lib/card-list/index.js.map +1 -0
  11. package/esm2022/lib/card-list/sneat-card-list.component.js +106 -0
  12. package/esm2022/lib/card-list/sneat-card-list.component.js.map +1 -0
  13. package/esm2022/lib/copyright/copyright.component.js +11 -0
  14. package/esm2022/lib/copyright/copyright.component.js.map +1 -0
  15. package/esm2022/lib/copyright/index.js +2 -0
  16. package/esm2022/lib/copyright/index.js.map +1 -0
  17. package/esm2022/lib/country-input/country-input.component.js +59 -0
  18. package/esm2022/lib/country-input/country-input.component.js.map +1 -0
  19. package/esm2022/lib/country-input/index.js +2 -0
  20. package/esm2022/lib/country-input/index.js.map +1 -0
  21. package/esm2022/lib/country-selector/countries-loader.service.js +91 -0
  22. package/esm2022/lib/country-selector/countries-loader.service.js.map +1 -0
  23. package/esm2022/lib/country-selector/countries.js +22 -0
  24. package/esm2022/lib/country-selector/countries.js.map +1 -0
  25. package/esm2022/lib/country-selector/country-selector.component.js +103 -0
  26. package/esm2022/lib/country-selector/country-selector.component.js.map +1 -0
  27. package/esm2022/lib/country-selector/country-selector.service.js +15 -0
  28. package/esm2022/lib/country-selector/country-selector.service.js.map +1 -0
  29. package/esm2022/lib/country-selector/index.js +5 -0
  30. package/esm2022/lib/country-selector/index.js.map +1 -0
  31. package/esm2022/lib/date-input/date-input.component.js +70 -0
  32. package/esm2022/lib/date-input/date-input.component.js.map +1 -0
  33. package/esm2022/lib/date-input/date-modal.component.js +53 -0
  34. package/esm2022/lib/date-input/date-modal.component.js.map +1 -0
  35. package/esm2022/lib/dialog-header/dialog-header.component.js +23 -0
  36. package/esm2022/lib/dialog-header/dialog-header.component.js.map +1 -0
  37. package/esm2022/lib/dialog-header/index.js +2 -0
  38. package/esm2022/lib/dialog-header/index.js.map +1 -0
  39. package/esm2022/lib/error-card/index.js +2 -0
  40. package/esm2022/lib/error-card/index.js.map +1 -0
  41. package/esm2022/lib/error-card/sneat-error-card.component.js +23 -0
  42. package/esm2022/lib/error-card/sneat-error-card.component.js.map +1 -0
  43. package/esm2022/lib/filter-item/filter-item.component.js +37 -0
  44. package/esm2022/lib/filter-item/filter-item.component.js.map +1 -0
  45. package/esm2022/lib/filter-item/index.js +2 -0
  46. package/esm2022/lib/filter-item/index.js.map +1 -0
  47. package/esm2022/lib/pipes/country-emoji.pipe.js +69 -0
  48. package/esm2022/lib/pipes/country-emoji.pipe.js.map +1 -0
  49. package/esm2022/lib/pipes/decimal64p2.pipe.js +47 -0
  50. package/esm2022/lib/pipes/decimal64p2.pipe.js.map +1 -0
  51. package/esm2022/lib/pipes/gender.pipes.js +69 -0
  52. package/esm2022/lib/pipes/gender.pipes.js.map +1 -0
  53. package/esm2022/lib/pipes/index.js +7 -0
  54. package/esm2022/lib/pipes/index.js.map +1 -0
  55. package/esm2022/lib/pipes/long-month-name.pipe.js +41 -0
  56. package/esm2022/lib/pipes/long-month-name.pipe.js.map +1 -0
  57. package/esm2022/lib/pipes/short-month-name.pipe.js +31 -0
  58. package/esm2022/lib/pipes/short-month-name.pipe.js.map +1 -0
  59. package/esm2022/lib/pipes/team-emoji.pipe.js +31 -0
  60. package/esm2022/lib/pipes/team-emoji.pipe.js.map +1 -0
  61. package/esm2022/lib/save-event.js +2 -0
  62. package/esm2022/lib/save-event.js.map +1 -0
  63. package/esm2022/lib/user-country/user-country.component.js +136 -0
  64. package/esm2022/lib/user-country/user-country.component.js.map +1 -0
  65. package/esm2022/lib/virtual-slider/virtual-slider.js +83 -0
  66. package/esm2022/lib/virtual-slider/virtual-slider.js.map +1 -0
  67. package/esm2022/sneat-components.js +5 -0
  68. package/esm2022/sneat-components.js.map +1 -0
  69. package/lib/app-version/app-version.component.d.ts +9 -0
  70. package/lib/app-version/build-info.d.ts +4 -0
  71. package/lib/card-list/sneat-card-list.component.d.ts +43 -0
  72. package/lib/copyright/copyright.component.d.ts +5 -0
  73. package/lib/country-input/country-input.component.d.ts +16 -0
  74. package/lib/country-selector/countries-loader.service.d.ts +43 -0
  75. package/lib/country-selector/countries.d.ts +25 -0
  76. package/lib/country-selector/country-selector.component.d.ts +31 -0
  77. package/lib/country-selector/country-selector.service.d.ts +8 -0
  78. package/lib/date-input/date-input.component.d.ts +21 -0
  79. package/lib/date-input/date-modal.component.d.ts +13 -0
  80. package/lib/dialog-header/dialog-header.component.d.ts +8 -0
  81. package/lib/error-card/sneat-error-card.component.d.ts +8 -0
  82. package/lib/filter-item/filter-item.component.d.ts +13 -0
  83. package/lib/pipes/country-emoji.pipe.d.ts +29 -0
  84. package/lib/pipes/decimal64p2.pipe.d.ts +12 -0
  85. package/lib/pipes/gender.pipes.d.ts +20 -0
  86. package/lib/pipes/long-month-name.pipe.d.ts +7 -0
  87. package/lib/pipes/short-month-name.pipe.d.ts +8 -0
  88. package/lib/pipes/team-emoji.pipe.d.ts +8 -0
  89. package/lib/save-event.d.ts +5 -0
  90. package/lib/user-country/user-country.component.d.ts +24 -0
  91. package/lib/virtual-slider/virtual-slider.d.ts +13 -0
  92. package/package.json +14 -2
  93. package/sneat-components.d.ts +5 -0
  94. package/eslint.config.js +0 -7
  95. package/ng-package.json +0 -7
  96. package/project.json +0 -38
  97. package/src/assets/data/countries.json +0 -1730
  98. package/src/lib/app-version/app-version.component.html +0 -15
  99. package/src/lib/app-version/app-version.component.spec.ts +0 -25
  100. package/src/lib/app-version/app-version.component.ts +0 -17
  101. package/src/lib/app-version/build-info.ts +0 -8
  102. package/src/lib/card-list/sneat-card-list.component.html +0 -108
  103. package/src/lib/card-list/sneat-card-list.component.spec.ts +0 -131
  104. package/src/lib/card-list/sneat-card-list.component.ts +0 -125
  105. package/src/lib/copyright/copyright.component.html +0 -1
  106. package/src/lib/copyright/copyright.component.spec.ts +0 -24
  107. package/src/lib/copyright/copyright.component.ts +0 -7
  108. package/src/lib/country-input/country-input.component.html +0 -33
  109. package/src/lib/country-input/country-input.component.spec.ts +0 -84
  110. package/src/lib/country-input/country-input.component.ts +0 -76
  111. package/src/lib/country-selector/countries-loader.service.spec.ts +0 -149
  112. package/src/lib/country-selector/countries-loader.service.ts +0 -100
  113. package/src/lib/country-selector/countries.ts +0 -42
  114. package/src/lib/country-selector/country-selector.component.html +0 -24
  115. package/src/lib/country-selector/country-selector.component.spec.ts +0 -5
  116. package/src/lib/country-selector/country-selector.component.ts +0 -125
  117. package/src/lib/country-selector/country-selector.service.spec.ts +0 -23
  118. package/src/lib/country-selector/country-selector.service.ts +0 -11
  119. package/src/lib/date-input/date-input.component.html +0 -39
  120. package/src/lib/date-input/date-input.component.spec.ts +0 -179
  121. package/src/lib/date-input/date-input.component.ts +0 -97
  122. package/src/lib/date-input/date-modal.component.html +0 -23
  123. package/src/lib/date-input/date-modal.component.spec.ts +0 -105
  124. package/src/lib/date-input/date-modal.component.ts +0 -67
  125. package/src/lib/dialog-header/dialog-header.component.html +0 -8
  126. package/src/lib/dialog-header/dialog-header.component.spec.ts +0 -67
  127. package/src/lib/dialog-header/dialog-header.component.ts +0 -26
  128. package/src/lib/dismissable.ts +0 -6
  129. package/src/lib/error-card/sneat-error-card.component.html +0 -20
  130. package/src/lib/error-card/sneat-error-card.component.spec.ts +0 -49
  131. package/src/lib/error-card/sneat-error-card.component.ts +0 -28
  132. package/src/lib/filter-item/filter-item.component.html +0 -19
  133. package/src/lib/filter-item/filter-item.component.spec.ts +0 -83
  134. package/src/lib/filter-item/filter-item.component.ts +0 -47
  135. package/src/lib/pipes/country-emoji.pipe.spec.ts +0 -116
  136. package/src/lib/pipes/country-emoji.pipe.ts +0 -67
  137. package/src/lib/pipes/decimal64p2.pipe.spec.ts +0 -61
  138. package/src/lib/pipes/decimal64p2.pipe.ts +0 -35
  139. package/src/lib/pipes/gender.pipes.spec.ts +0 -84
  140. package/src/lib/pipes/gender.pipes.ts +0 -65
  141. package/src/lib/pipes/long-month-name.pipe.spec.ts +0 -36
  142. package/src/lib/pipes/long-month-name.pipe.ts +0 -35
  143. package/src/lib/pipes/short-month-name.pipe.spec.ts +0 -28
  144. package/src/lib/pipes/short-month-name.pipe.ts +0 -26
  145. package/src/lib/pipes/team-emoji.pipe.spec.ts +0 -27
  146. package/src/lib/pipes/team-emoji.pipe.ts +0 -26
  147. package/src/lib/save-event.ts +0 -5
  148. package/src/lib/test-ide-bug.ts +0 -24
  149. package/src/lib/user-country/user-country.component.html +0 -44
  150. package/src/lib/user-country/user-country.component.spec.ts +0 -136
  151. package/src/lib/user-country/user-country.component.ts +0 -172
  152. package/src/lib/virtual-slider/odd-even-virtual-slider.ts +0 -1
  153. package/src/lib/virtual-slider/virtual-slider.ts +0 -130
  154. package/src/lib/webstorm-type-err-demo.component.ts +0 -35
  155. package/src/test-setup.ts +0 -3
  156. package/tsconfig.json +0 -13
  157. package/tsconfig.lib.json +0 -19
  158. package/tsconfig.lib.prod.json +0 -7
  159. package/tsconfig.spec.json +0 -31
  160. package/vite.config.mts +0 -10
  161. /package/{src/index.ts → index.d.ts} +0 -0
  162. /package/{src/lib/app-version/index.ts → lib/app-version/index.d.ts} +0 -0
  163. /package/{src/lib/card-list/index.ts → lib/card-list/index.d.ts} +0 -0
  164. /package/{src/lib/copyright/index.ts → lib/copyright/index.d.ts} +0 -0
  165. /package/{src/lib/country-input/index.ts → lib/country-input/index.d.ts} +0 -0
  166. /package/{src/lib/country-selector/index.ts → lib/country-selector/index.d.ts} +0 -0
  167. /package/{src/lib/dialog-header/index.ts → lib/dialog-header/index.d.ts} +0 -0
  168. /package/{src/lib/error-card/index.ts → lib/error-card/index.d.ts} +0 -0
  169. /package/{src/lib/filter-item/index.ts → lib/filter-item/index.d.ts} +0 -0
  170. /package/{src/lib/pipes/index.ts → lib/pipes/index.d.ts} +0 -0
@@ -1,149 +0,0 @@
1
- import { TestBed } from '@angular/core/testing';
2
- import { HttpClient } from '@angular/common/http';
3
- import { of, throwError } from 'rxjs';
4
- import { CountriesLoaderService } from './countries-loader.service';
5
- import { GeoRegion } from './countries';
6
-
7
- describe('CountriesLoaderService', () => {
8
- let service: CountriesLoaderService;
9
- let httpClient: { get: ReturnType<typeof vi.fn> };
10
-
11
- const mockCountriesData = {
12
- countriesByID: {
13
- US: {
14
- id: 'US',
15
- id3: 'USA',
16
- title: 'United States',
17
- geoRegions: ['Americas', 'Northern America'] as GeoRegion[],
18
- emoji: '🇺🇸',
19
- },
20
- UA: {
21
- id: 'UA',
22
- id3: 'UKR',
23
- title: 'Ukraine',
24
- geoRegions: ['Europe', 'Eastern Europe'] as GeoRegion[],
25
- emoji: '🇺🇦',
26
- },
27
- JP: {
28
- id: 'JP',
29
- id3: 'JPN',
30
- title: 'Japan',
31
- geoRegions: ['Asia', 'Eastern Asia'] as GeoRegion[],
32
- emoji: '🇯🇵',
33
- },
34
- },
35
- unknownCountry: {
36
- id: '--',
37
- id3: '---',
38
- title: 'Unknown',
39
- geoRegions: [] as GeoRegion[],
40
- emoji: '🏳️',
41
- },
42
- };
43
-
44
- beforeEach(() => {
45
- httpClient = {
46
- get: vi.fn(() => of(mockCountriesData)),
47
- };
48
-
49
- TestBed.configureTestingModule({
50
- providers: [
51
- CountriesLoaderService,
52
- { provide: HttpClient, useValue: httpClient },
53
- ],
54
- });
55
- service = TestBed.inject(CountriesLoaderService);
56
- });
57
-
58
- it('should be created', () => {
59
- expect(service).toBeTruthy();
60
- });
61
-
62
- it('should load countries from JSON file', async () => {
63
- const countries = await service.getCountries();
64
- expect(httpClient.get).toHaveBeenCalledWith('assets/data/countries.json');
65
- expect(countries.length).toBe(3);
66
- });
67
-
68
- it('should return countries by ID', async () => {
69
- const countriesByID = await service.getCountriesByID();
70
- expect(countriesByID['US']).toBeDefined();
71
- expect(countriesByID['US'].title).toBe('United States');
72
- expect(countriesByID['UA'].title).toBe('Ukraine');
73
- });
74
-
75
- it('should return specific country by ID', async () => {
76
- const country = await service.getCountryByID('US');
77
- expect(country).toBeDefined();
78
- expect(country?.title).toBe('United States');
79
- expect(country?.emoji).toBe('🇺🇸');
80
- });
81
-
82
- it('should return undefined for non-existent country', async () => {
83
- const country = await service.getCountryByID('XX');
84
- expect(country).toBeUndefined();
85
- });
86
-
87
- it('should return unknown country', async () => {
88
- const unknownCountry = await service.getUnknownCountry();
89
- expect(unknownCountry).toBeDefined();
90
- expect(unknownCountry.id).toBe('--');
91
- expect(unknownCountry.title).toBe('Unknown');
92
- });
93
-
94
- it('should filter countries by region', async () => {
95
- const europeanCountries = await service.getCountriesByRegion('Europe' as GeoRegion);
96
- expect(europeanCountries.length).toBe(1);
97
- expect(europeanCountries[0].id).toBe('UA');
98
- });
99
-
100
- it('should return empty array for region with no countries', async () => {
101
- const africaCountries = await service.getCountriesByRegion('Africa' as GeoRegion);
102
- expect(africaCountries.length).toBe(0);
103
- });
104
-
105
- it('should cache countries data and not reload', async () => {
106
- await service.getCountries();
107
- expect(httpClient.get).toHaveBeenCalledTimes(1);
108
-
109
- await service.getCountries();
110
- expect(httpClient.get).toHaveBeenCalledTimes(1); // Still only called once
111
- });
112
-
113
- it('should handle load error gracefully', async () => {
114
- const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => undefined);
115
- httpClient.get = vi.fn(() => throwError(() => new Error('Load failed')));
116
-
117
- const service2 = TestBed.inject(CountriesLoaderService);
118
- const countries = await service2.getCountries();
119
-
120
- expect(consoleSpy).toHaveBeenCalledWith('Failed to load countries:', expect.any(Error));
121
- expect(countries).toEqual([]);
122
- consoleSpy.mockRestore();
123
- });
124
-
125
- it('should return unknown country after load error', async () => {
126
- const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => undefined);
127
- httpClient.get = vi.fn(() => throwError(() => new Error('Load failed')));
128
-
129
- const service2 = TestBed.inject(CountriesLoaderService);
130
- const unknownCountry = await service2.getUnknownCountry();
131
-
132
- expect(unknownCountry).toBeDefined();
133
- expect(unknownCountry.id).toBe('--');
134
- consoleSpy.mockRestore();
135
- });
136
-
137
- it('should handle concurrent load requests', async () => {
138
- const [countries1, countries2, countries3] = await Promise.all([
139
- service.getCountries(),
140
- service.getCountriesByID(),
141
- service.getCountryByID('US'),
142
- ]);
143
-
144
- expect(httpClient.get).toHaveBeenCalledTimes(1); // Should only load once
145
- expect(countries1.length).toBe(3);
146
- expect(Object.keys(countries2).length).toBe(3);
147
- expect(countries3?.id).toBe('US');
148
- });
149
- });
@@ -1,100 +0,0 @@
1
- import { inject, Injectable } from '@angular/core';
2
- import { HttpClient } from '@angular/common/http';
3
- import { firstValueFrom } from 'rxjs';
4
- import { GeoRegion, ICountry } from './countries';
5
-
6
- interface CountriesData {
7
- countriesByID: Record<string, ICountry>;
8
- unknownCountry: ICountry;
9
- }
10
-
11
- /**
12
- * Service to lazy load country data.
13
- * This reduces the initial bundle size by loading country data on-demand.
14
- */
15
- @Injectable({ providedIn: 'root' })
16
- export class CountriesLoaderService {
17
- private readonly http = inject(HttpClient);
18
- private countriesData?: CountriesData;
19
- private loadPromise?: Promise<void>;
20
-
21
- /**
22
- * Loads country data from JSON file.
23
- * Subsequent calls return the same promise (singleton pattern).
24
- */
25
- private async loadCountries(): Promise<void> {
26
- if (this.countriesData) {
27
- return; // Already loaded
28
- }
29
-
30
- if (!this.loadPromise) {
31
- this.loadPromise = (async () => {
32
- try {
33
- this.countriesData = await firstValueFrom(
34
- this.http.get<CountriesData>('assets/data/countries.json'),
35
- );
36
- } catch (error) {
37
- console.error('Failed to load countries:', error);
38
- // Set to empty to avoid repeated failures
39
- this.countriesData = {
40
- countriesByID: {},
41
- unknownCountry: {
42
- id: '--',
43
- id3: '---',
44
- title: 'Unknown',
45
- geoRegions: [],
46
- emoji: '🏳️',
47
- },
48
- };
49
- }
50
- })();
51
- }
52
-
53
- return this.loadPromise;
54
- }
55
-
56
- /**
57
- * Gets all countries as an array.
58
- * Automatically loads country data if not yet loaded.
59
- */
60
- async getCountries(): Promise<readonly ICountry[]> {
61
- await this.loadCountries();
62
- return Object.values(this.countriesData!.countriesByID);
63
- }
64
-
65
- /**
66
- * Gets countries by ID lookup.
67
- * Automatically loads country data if not yet loaded.
68
- */
69
- async getCountriesByID(): Promise<Record<string, ICountry>> {
70
- await this.loadCountries();
71
- return this.countriesData!.countriesByID;
72
- }
73
-
74
- /**
75
- * Gets a specific country by ID.
76
- * Automatically loads country data if not yet loaded.
77
- */
78
- async getCountryByID(id: string): Promise<ICountry | undefined> {
79
- await this.loadCountries();
80
- return this.countriesData!.countriesByID[id];
81
- }
82
-
83
- /**
84
- * Gets the unknown country constant.
85
- * Automatically loads country data if not yet loaded.
86
- */
87
- async getUnknownCountry(): Promise<ICountry> {
88
- await this.loadCountries();
89
- return this.countriesData!.unknownCountry;
90
- }
91
-
92
- /**
93
- * Gets countries filtered by geo region.
94
- * Automatically loads country data if not yet loaded.
95
- */
96
- async getCountriesByRegion(region: GeoRegion): Promise<readonly ICountry[]> {
97
- const countries = await this.getCountries();
98
- return countries.filter((c) => c.geoRegions.includes(region));
99
- }
100
- }
@@ -1,42 +0,0 @@
1
- export type GeoRegion =
2
- | 'Europe'
3
- | 'Asia'
4
- | 'Americas'
5
- | 'South America'
6
- | 'North America'
7
- | 'Pacific Ocean'
8
- | 'Africa';
9
-
10
- export interface ICountry {
11
- id: string;
12
- id3: string;
13
- geoRegions: GeoRegion[];
14
- title: string;
15
- longTitle?: string;
16
- shortTitle?: string;
17
- emoji: string;
18
- }
19
-
20
- /**
21
- * @deprecated Use CountriesLoaderService.getCountriesByID() instead.
22
- * Country data is now lazy-loaded to reduce bundle size.
23
- */
24
- export const countriesByID: Record<string, ICountry> = {};
25
-
26
- /**
27
- * @deprecated Use CountriesLoaderService.getUnknownCountry() instead.
28
- * Country data is now lazy-loaded to reduce bundle size.
29
- */
30
- export const unknownCountry: ICountry = {
31
- id: '--',
32
- id3: '---',
33
- title: 'Unknown',
34
- geoRegions: [],
35
- emoji: '🏳️',
36
- };
37
-
38
- /**
39
- * @deprecated Use CountriesLoaderService.getCountries() instead.
40
- * Country data is now lazy-loaded to reduce bundle size.
41
- */
42
- export const countries: ICountry[] = [];
@@ -1,24 +0,0 @@
1
- <sneat-select-from-list
2
- [value]="countryID || ''"
3
- [isFilterable]="true"
4
- [filterLabel]="label"
5
- [label]="label"
6
- [items]="countries()"
7
- [(ngModel)]="countryID"
8
- (ngModelChange)="onChanged()"
9
- [isReadonly]="readonly"
10
- [filterItem]="filterCountryByCode"
11
- (filterChanged)="onFilterChanged($event)"
12
- labelPlacement="start"
13
- >
14
- @if (!countryID && !filter()) {
15
- <ion-segment [(ngModel)]="geoRegion" (ionChange)="onRegionChanged($event)">
16
- <ion-segment-button value="All">All</ion-segment-button>
17
- @for (region of geoRegions; track region.id) {
18
- <ion-segment-button [value]="region.id">
19
- <small>{{ region.id }}</small>
20
- </ion-segment-button>
21
- }
22
- </ion-segment>
23
- }
24
- </sneat-select-from-list>
@@ -1,5 +0,0 @@
1
- describe('CountrySelectorComponent', () => {
2
- it('should create', () => {
3
- expect(true).toBeTruthy();
4
- });
5
- });
@@ -1,125 +0,0 @@
1
- import {
2
- Component,
3
- computed,
4
- EventEmitter,
5
- inject,
6
- Input,
7
- OnChanges,
8
- OnInit,
9
- Output,
10
- signal,
11
- SimpleChanges,
12
- } from '@angular/core';
13
- import { FormsModule } from '@angular/forms';
14
- import { IonSegment, IonSegmentButton } from '@ionic/angular/standalone';
15
- import { ISelectItem, SelectFromListComponent } from '@sneat/ui';
16
- import { GeoRegion, ICountry } from './countries';
17
- import { CountriesLoaderService } from './countries-loader.service';
18
-
19
- @Component({
20
- selector: 'sneat-country-selector',
21
- templateUrl: './country-selector.component.html',
22
- imports: [FormsModule, SelectFromListComponent, IonSegment, IonSegmentButton],
23
- })
24
- export class CountrySelectorComponent implements OnInit, OnChanges {
25
- private readonly countriesLoader = inject(CountriesLoaderService);
26
-
27
- protected readonly geoRegions: readonly {
28
- readonly id: string;
29
- title?: string;
30
- }[] = [
31
- { id: 'Europe' },
32
- { id: 'Asia' },
33
- { id: 'Americas' },
34
- { id: 'Africa' },
35
- ];
36
-
37
- @Input({ required: true }) countryID?: string;
38
-
39
- @Input() defaultCountryID?: string;
40
-
41
- @Input() readonly = false;
42
- @Input() disabled = false;
43
- @Input() label = 'search';
44
- @Input() canBeUnknown = false;
45
-
46
- @Output() readonly countryIDChange = new EventEmitter<string>();
47
-
48
- protected readonly filter = signal<string>('');
49
- protected readonly geoRegion = signal<GeoRegion | 'All' | 'Americas'>('All');
50
- protected readonly allCountries = signal<readonly ICountry[]>([]);
51
- protected readonly unknownCountry = signal<ICountry | undefined>(undefined);
52
-
53
- protected readonly countries = computed<readonly ISelectItem[]>(() => {
54
- const allCountries = this.allCountries();
55
- if (allCountries.length === 0) {
56
- return []; // Data not loaded yet
57
- }
58
-
59
- const filter = this.filter();
60
- let countriesToShow: readonly ISelectItem[] = filter
61
- ? allCountries
62
- : allCountries.filter(
63
- (c) =>
64
- this.geoRegion() === 'All' ||
65
- (this.geoRegion() === 'Americas' &&
66
- (c.geoRegions.includes('North America') ||
67
- c.geoRegions.includes('South America'))) ||
68
- c.geoRegions.includes(this.geoRegion() as GeoRegion),
69
- );
70
- if (this.canBeUnknown && this.unknownCountry()) {
71
- countriesToShow = [...countriesToShow, this.unknownCountry()!];
72
- }
73
- return countriesToShow;
74
- });
75
-
76
- ngOnInit(): void {
77
- // Load countries data
78
- this.countriesLoader.getCountries().then((countries) => {
79
- this.allCountries.set(countries);
80
- });
81
- this.countriesLoader.getUnknownCountry().then((unknown) => {
82
- this.unknownCountry.set(unknown);
83
- });
84
- }
85
-
86
- // protected geoRegion: GeoRegion | 'All' | 'Americas' = 'All';
87
-
88
- protected onChanged(): void {
89
- this.countryIDChange.emit(this.countryID);
90
- }
91
-
92
- protected onFilterChanged(filter: string): void {
93
- this.filter.set(filter);
94
- }
95
-
96
- ngOnChanges(changes: SimpleChanges): void {
97
- if (changes['country'] && this.countryID === '--' && !this.canBeUnknown) {
98
- this.countryID = undefined;
99
- }
100
- if (
101
- changes['defaultCountryID'] &&
102
- !this.countryID &&
103
- this.defaultCountryID &&
104
- this.defaultCountryID !== '--'
105
- ) {
106
- this.countryID = this.defaultCountryID;
107
- this.countryIDChange.emit(this.countryID);
108
- }
109
- }
110
-
111
- protected onRegionChanged(event: Event): void {
112
- this.geoRegion.set(
113
- (event as CustomEvent).detail.value as GeoRegion | 'All' | 'Americas',
114
- );
115
- }
116
-
117
- protected readonly filterCountryByCode = (
118
- item: ISelectItem,
119
- filter: string,
120
- ) => {
121
- const f = filter.trim().toUpperCase();
122
- const c = item as ICountry;
123
- return c.id === f || c.id3.startsWith(f);
124
- };
125
- }
@@ -1,23 +0,0 @@
1
- import { TestBed } from '@angular/core/testing';
2
- import { ModalController } from '@ionic/angular/standalone';
3
- import { ErrorLogger } from '@sneat/core';
4
- import { CountrySelectorService } from './country-selector.service';
5
-
6
- describe('CountrySelectorService', () => {
7
- beforeEach(() => {
8
- TestBed.configureTestingModule({
9
- providers: [
10
- CountrySelectorService,
11
- {
12
- provide: ErrorLogger,
13
- useValue: { logError: vi.fn(), logErrorHandler: () => vi.fn() },
14
- },
15
- { provide: ModalController, useValue: { create: vi.fn() } },
16
- ],
17
- });
18
- });
19
-
20
- it('should be created', () => {
21
- expect(TestBed.inject(CountrySelectorService)).toBeTruthy();
22
- });
23
- });
@@ -1,11 +0,0 @@
1
- import { Injectable } from '@angular/core';
2
- import { SelectorBaseService } from '@sneat/ui';
3
- import { ICountry } from './countries';
4
- import { CountrySelectorComponent } from './country-selector.component';
5
-
6
- @Injectable()
7
- export class CountrySelectorService extends SelectorBaseService<ICountry> {
8
- constructor() {
9
- super(CountrySelectorComponent);
10
- }
11
- }
@@ -1,39 +0,0 @@
1
- <ion-item class="sneat-tiny-end-padding" lines="none">
2
- @let displayValue = $displayValue();
3
-
4
- <ion-input
5
- [label]="$label()"
6
- type="date"
7
- [readonly]="$value() === undefined || $updating()"
8
- [value]="displayValue"
9
- (ionChange)="onValueChanged($event)"
10
- />
11
-
12
- @if ($value() !== undefined || $newValue()) {
13
- <ion-buttons slot="end">
14
- @if ($updating()) {
15
- <ion-button disabled="true" (click)="openDatePicker($event)">
16
- <ion-spinner name="lines-small" slot="start" />
17
- <ion-label>Saving...</ion-label>
18
- </ion-button>
19
- } @else if ($newValue() !== undefined && $newValue() !== $value()) {
20
- <ion-button color="danger" fill="solid" (click)="save($event)">
21
- <ion-icon name="save-outline" slot="start" />
22
- <ion-label>Save</ion-label>
23
- </ion-button>
24
- } @else if ($value()) {
25
- <ion-button color="medium" (click)="openDatePicker($event)">
26
- <ion-icon name="chevron-down-outline" slot="start" />
27
- </ion-button>
28
- } @else {
29
- <ion-button
30
- fill="solid"
31
- [color]="$noValueButtonColor() || 'danger'"
32
- (click)="openDatePicker($event)"
33
- >
34
- <ion-label>Add</ion-label>
35
- </ion-button>
36
- }
37
- </ion-buttons>
38
- }
39
- </ion-item>