@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,26 +0,0 @@
1
- import { Pipe, PipeTransform } from '@angular/core';
2
-
3
- export const shortMonthNamesByNumber = [
4
- 'Jan',
5
- 'Feb',
6
- 'Mar',
7
- 'Apr',
8
- 'May',
9
- 'Jun',
10
- 'Jul',
11
- 'Aug',
12
- 'Sep',
13
- 'Oct',
14
- 'Nov',
15
- 'Dec',
16
- ];
17
-
18
- @Pipe({ name: 'shortMonthName' })
19
- export class ShortMonthNamePipe implements PipeTransform {
20
- transform(month?: number): string {
21
- if (month !== undefined && month >= 0 && month <= 11) {
22
- return shortMonthNamesByNumber[month];
23
- }
24
- return '' + month;
25
- }
26
- }
@@ -1,27 +0,0 @@
1
- import { SpaceType } from '@sneat/core';
2
- import { SpaceEmojiPipe } from './team-emoji.pipe';
3
-
4
- describe('SpaceEmojiPipe', () => {
5
- it('should create', () => {
6
- expect(new SpaceEmojiPipe()).toBeTruthy();
7
- });
8
-
9
- const testCases: { type: SpaceType | 'unknown'; emoji: string | undefined }[] =
10
- [
11
- { type: 'family', emoji: '👨‍👩‍👧‍👦' },
12
- { type: 'cohabit', emoji: '🤝' },
13
- { type: 'sport_club', emoji: '⚽' },
14
- { type: 'educator', emoji: '💃' },
15
- { type: 'realtor', emoji: '🏘️' },
16
- { type: 'parish', emoji: '⛪' },
17
- { type: 'private', emoji: '🕶️' },
18
- { type: 'unknown', emoji: undefined },
19
- ];
20
-
21
- testCases.forEach(({ type, emoji }) => {
22
- it(`should return ${emoji} for type ${type}`, () => {
23
- const pipe = new SpaceEmojiPipe();
24
- expect(pipe.transform(type as SpaceType)).toBe(emoji);
25
- });
26
- });
27
- });
@@ -1,26 +0,0 @@
1
- import { Pipe, PipeTransform } from '@angular/core';
2
- import { SpaceType } from '@sneat/core';
3
-
4
- @Pipe({ name: 'spaceEmoji' })
5
- export class SpaceEmojiPipe implements PipeTransform {
6
- transform(communeType: SpaceType): string | undefined {
7
- switch (communeType) {
8
- case 'family':
9
- return '👨‍👩‍👧‍👦';
10
- case 'cohabit':
11
- return '🤝';
12
- case 'sport_club':
13
- return '⚽';
14
- case 'educator':
15
- return '💃';
16
- case 'realtor':
17
- return '🏘️';
18
- case 'parish':
19
- return '⛪';
20
- case 'private':
21
- return '🕶️';
22
- default:
23
- return undefined;
24
- }
25
- }
26
- }
@@ -1,5 +0,0 @@
1
- export interface ISaveEvent<T> {
2
- readonly object?: T;
3
- readonly success: () => void;
4
- readonly error: (e: unknown) => void;
5
- }
@@ -1,24 +0,0 @@
1
- import { Component, Input } from '@angular/core';
2
-
3
- interface IItem {
4
- id: string;
5
- }
6
-
7
- @Component({
8
- selector: 'sneat-test',
9
- template: `Today's item: {{ item }}`,
10
- })
11
- export class TestComponent {
12
- @Input() item?: IItem | null;
13
- }
14
-
15
- @Component({
16
- selector: 'sneat-consumer',
17
- imports: [TestComponent],
18
- template: ` <sneat-test [item]="myItem('test')" />`,
19
- })
20
- export class ConsumerComponent {
21
- protected myItem(id: string): IItem | undefined {
22
- return id ? { id } : undefined;
23
- }
24
- }
@@ -1,44 +0,0 @@
1
- @let userCountryID = $userCountryID();
2
- @let userHasCountry = $userHasCountry();
3
-
4
- @if (doNotHide || ($ipCountry() && !userHasCountry)) {
5
- <ion-card>
6
- @if (userHasCountry || !$ipCountry()) {
7
- <ion-card-content>
8
- <sneat-country-input
9
- label="Country of residence"
10
- [canReset]="false"
11
- [countryID]="userCountryID || ''"
12
- (countryIDChange)="onCountryOfResidenceChanged($event)"
13
- />
14
- </ion-card-content>
15
- } @else {
16
- <ion-card-header>
17
- <ion-card-title>
18
- Is {{ $ipCountry()?.emoji }} {{ $ipCountry()?.title }} your country of
19
- residence?
20
- </ion-card-title>
21
- </ion-card-header>
22
- <ion-card-content>
23
- <ion-button
24
- [disabled]="$saving()"
25
- color="primary"
26
- class="ion-padding-end"
27
- (click)="setCountry($ipCountryID())"
28
- >
29
- <ion-icon name="checkmark-outline" slot="start" />
30
- <ion-label style="text-transform: none"
31
- ><b
32
- >Yes, my primary residence is in {{ $ipCountry()?.title }}
33
- {{ $ipCountry()?.emoji }}</b
34
- ></ion-label
35
- >
36
- </ion-button>
37
- <ion-button color="light" [disabled]="$saving()">
38
- <ion-icon name="list-outline" slot="start" />
39
- <ion-label style="text-transform: none">Choose county</ion-label>
40
- </ion-button>
41
- </ion-card-content>
42
- }
43
- </ion-card>
44
- }
@@ -1,136 +0,0 @@
1
- import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
2
- import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
3
- import { HttpClient } from '@angular/common/http';
4
- import { ErrorLogger } from '@sneat/core';
5
- import { SneatUserService } from '@sneat/auth-core';
6
- import { ClassName } from '@sneat/ui';
7
- import { of, throwError, BehaviorSubject } from 'rxjs';
8
- import { UserCountryComponent } from './user-country.component';
9
- import { CountriesLoaderService } from '../country-selector';
10
-
11
- describe('UserCountryComponent', () => {
12
- let component: UserCountryComponent;
13
- let fixture: ComponentFixture<UserCountryComponent>;
14
- let httpClient: { get: ReturnType<typeof vi.fn> };
15
- let userService: {
16
- userState: BehaviorSubject<{ record?: { countryID?: string } }>;
17
- user$: BehaviorSubject<unknown>;
18
- userChanged: BehaviorSubject<unknown>;
19
- setUserCountry: ReturnType<typeof vi.fn>;
20
- };
21
- let errorLogger: { logError: ReturnType<typeof vi.fn>; logErrorHandler: ReturnType<typeof vi.fn> };
22
- let countriesLoader: {
23
- getCountryByID: ReturnType<typeof vi.fn>;
24
- getCountries: ReturnType<typeof vi.fn>;
25
- };
26
-
27
- beforeEach(waitForAsync(async () => {
28
- httpClient = {
29
- get: vi.fn(() => of('US')),
30
- };
31
-
32
- userService = {
33
- userState: new BehaviorSubject<{ record?: { countryID?: string } }>({
34
- record: undefined
35
- }),
36
- user$: new BehaviorSubject({}),
37
- userChanged: new BehaviorSubject(undefined),
38
- setUserCountry: vi.fn(() => of({})),
39
- };
40
-
41
- errorLogger = {
42
- logError: vi.fn(),
43
- logErrorHandler: () => vi.fn(),
44
- };
45
-
46
- countriesLoader = {
47
- getCountryByID: vi.fn(() => Promise.resolve({
48
- id: 'US',
49
- emoji: '🇺🇸',
50
- title: 'United States'
51
- })),
52
- getCountries: vi.fn(() => Promise.resolve([])),
53
- };
54
-
55
- await TestBed.configureTestingModule({
56
- imports: [UserCountryComponent],
57
- providers: [
58
- { provide: ClassName, useValue: 'TestComponent' },
59
- { provide: ErrorLogger, useValue: errorLogger },
60
- { provide: HttpClient, useValue: httpClient },
61
- { provide: SneatUserService, useValue: userService },
62
- { provide: CountriesLoaderService, useValue: countriesLoader },
63
- ],
64
- schemas: [CUSTOM_ELEMENTS_SCHEMA],
65
- })
66
- .overrideComponent(UserCountryComponent, {
67
- set: { imports: [], schemas: [CUSTOM_ELEMENTS_SCHEMA], template: '' },
68
- })
69
- .compileComponents();
70
- fixture = TestBed.createComponent(UserCountryComponent);
71
- component = fixture.componentInstance;
72
- }));
73
-
74
- it('should create', () => {
75
- expect(component).toBeTruthy();
76
- });
77
-
78
- it('should track user record and update userCountryID', () => {
79
- userService.userState.next({ record: { countryID: 'UA' } });
80
- expect(component['$userCountryID']()).toBe('UA');
81
- });
82
-
83
- it('should set userCountryID to "--" when record has no countryID', () => {
84
- userService.userState.next({ record: {} });
85
- expect(component['$userCountryID']()).toBe('--');
86
- });
87
-
88
- it('should call setCountry when onCountryOfResidenceChanged is invoked', () => {
89
- const setCountrySpy = vi.spyOn(component as any, 'setCountry');
90
- component['onCountryOfResidenceChanged']('US');
91
- expect(setCountrySpy).toHaveBeenCalledWith('US');
92
- });
93
-
94
- it('should set user country when setCountry is called with valid countryID', () => {
95
- component['setCountry']('US');
96
- expect(component['$saving']()).toBe(true);
97
- expect(userService.setUserCountry).toHaveBeenCalledWith('US');
98
- });
99
-
100
- it('should not set country when countryID is undefined', () => {
101
- component['setCountry'](undefined);
102
- expect(userService.setUserCountry).not.toHaveBeenCalled();
103
- });
104
-
105
- it('should handle error when setting user country fails', () => {
106
- const error = new Error('Set country failed');
107
- userService.setUserCountry = vi.fn(() => throwError(() => error));
108
-
109
- component['setCountry']('US');
110
-
111
- expect(errorLogger.logError).toHaveBeenCalledWith(
112
- 'UserCountryComponent: Failed to set user country',
113
- error
114
- );
115
- });
116
-
117
- it('should compute userHasCountry correctly', () => {
118
- component['$userCountryID'].set('US');
119
- expect(component['$userHasCountry']()).toBe(true);
120
-
121
- component['$userCountryID'].set('--');
122
- expect(component['$userHasCountry']()).toBe(false);
123
-
124
- component['$userCountryID'].set('');
125
- expect(component['$userHasCountry']()).toBe(false);
126
- });
127
-
128
- it('should initialize with default values', () => {
129
- expect(component['$ipCountryID']()).toBe('');
130
- expect(component['$ipCountry']()).toBeUndefined();
131
- expect(component['isCountryDetectionStarted']).toBe(false);
132
- expect(component['$detectingCountry']()).toBe(false);
133
- expect(component['$saving']()).toBe(false);
134
- expect(component.doNotHide).toBe(false);
135
- });
136
- });
@@ -1,172 +0,0 @@
1
- import { HttpClient, HttpHeaders } from '@angular/common/http';
2
- import {
3
- ChangeDetectionStrategy,
4
- Component,
5
- computed,
6
- effect,
7
- Input,
8
- OnDestroy,
9
- signal,
10
- inject,
11
- } from '@angular/core';
12
- import {
13
- IonButton,
14
- IonCard,
15
- IonCardContent,
16
- IonCardHeader,
17
- IonCardTitle,
18
- IonIcon,
19
- IonLabel,
20
- } from '@ionic/angular/standalone';
21
- import { SneatUserService } from '@sneat/auth-core';
22
- import { ClassName, SneatBaseComponent } from '@sneat/ui';
23
- import { map, race, takeUntil } from 'rxjs';
24
- import { CountryInputComponent } from '../country-input';
25
- import { ICountry, CountriesLoaderService } from '../country-selector';
26
-
27
- let ipCountryCached: string | undefined; // TODO: Should have expiration?
28
-
29
- @Component({
30
- imports: [
31
- CountryInputComponent,
32
- IonCardContent,
33
- IonCardHeader,
34
- IonCardTitle,
35
- IonButton,
36
- IonIcon,
37
- IonLabel,
38
- IonCard,
39
- ],
40
- providers: [
41
- {
42
- provide: ClassName,
43
- useValue: 'UserCountryComponent',
44
- },
45
- ],
46
- changeDetection: ChangeDetectionStrategy.OnPush,
47
- selector: 'sneat-user-country',
48
- templateUrl: './user-country.component.html',
49
- })
50
- export class UserCountryComponent
51
- extends SneatBaseComponent
52
- implements OnDestroy
53
- {
54
- private readonly httpClient = inject(HttpClient);
55
- private readonly userService = inject(SneatUserService);
56
- private readonly countriesLoader = inject(CountriesLoaderService);
57
-
58
- protected readonly $ipCountryID = signal('');
59
- protected readonly $ipCountry = signal<ICountry | undefined>(undefined);
60
-
61
- protected readonly $userCountryID = signal<string>('');
62
- protected readonly $userHasCountry = computed(() => {
63
- const userCountryID = this.$userCountryID();
64
- return !!userCountryID && userCountryID !== '--';
65
- });
66
-
67
- protected isCountryDetectionStarted = false;
68
- protected readonly $detectingCountry = signal(false);
69
- protected readonly $saving = signal(false);
70
-
71
- @Input() public doNotHide = false;
72
-
73
- private trackUserRecord(): void {
74
- this.userService.userState
75
- .pipe(takeUntil(this.destroyed$))
76
- .subscribe((user) => {
77
- if (user.record) {
78
- this.$userCountryID.set(user?.record?.countryID || '--');
79
- }
80
- });
81
- }
82
-
83
- constructor() {
84
- super();
85
- this.trackUserRecord();
86
- // this.getIpCountry();
87
- effect(() => {
88
- const userCountryID = this.$userCountryID();
89
- if (
90
- userCountryID === '--' &&
91
- // It looks like using signal here can cause an infinite loop
92
- !this.isCountryDetectionStarted
93
- ) {
94
- this.getIpCountry();
95
- }
96
- });
97
- }
98
-
99
- protected onCountryOfResidenceChanged(countryID: string): void {
100
- this.setCountry(countryID);
101
- }
102
-
103
- private getIpCountry(): void {
104
- if (this.isCountryDetectionStarted) {
105
- return;
106
- }
107
- this.isCountryDetectionStarted = true;
108
- if (ipCountryCached) {
109
- this.$ipCountryID.set(ipCountryCached);
110
- this.countriesLoader.getCountryByID(ipCountryCached).then((country) => {
111
- this.$ipCountry.set(country);
112
- });
113
- return;
114
- }
115
- this.$detectingCountry.set(true);
116
- // console.log('UserCountryComponent: Detecting IP country...');
117
- race(
118
- this.httpClient
119
- .get<string>('https://ipapi.co/country', {
120
- headers: new HttpHeaders({
121
- Accept: 'text/plain',
122
- }),
123
- responseType: 'text' as 'json',
124
- })
125
- .pipe(
126
- map((country) => ({ source: 'https://ipapi.co/country', country })),
127
- ),
128
- ).subscribe({
129
- next: (response) => {
130
- // console.log('UserCountryComponent: Got IP country:', response);
131
- const ipCountryID = response.country;
132
- ipCountryCached = ipCountryID;
133
- this.$ipCountryID.set(ipCountryID);
134
- this.countriesLoader.getCountryByID(ipCountryID).then((country) => {
135
- this.$ipCountry.set(country);
136
- });
137
- this.$detectingCountry.set(false);
138
- },
139
- error: (err) => {
140
- this.$detectingCountry.set(false);
141
- // console.error('UserCountryComponent: Failed to get IP country', err);
142
- this.errorLogger.logError(
143
- err,
144
- 'UserCountryComponent: Failed to get IP country',
145
- {
146
- show: false,
147
- },
148
- );
149
- },
150
- });
151
- }
152
-
153
- protected setCountry(countryID?: string): void {
154
- if (!countryID) {
155
- return;
156
- }
157
- this.$saving.set(true);
158
- this.userService.setUserCountry(countryID).subscribe({
159
- next: () => {
160
- // console.log('UserCountryComponent: User country set to', countryID);
161
- this.$userCountryID.set(countryID);
162
- },
163
- error: (err) => {
164
- this.errorLogger.logError(
165
- 'UserCountryComponent: Failed to set user country',
166
- err,
167
- );
168
- this.$saving.set(false);
169
- },
170
- });
171
- }
172
- }
@@ -1 +0,0 @@
1
- export const empty = {};
@@ -1,130 +0,0 @@
1
- import {
2
- animate,
3
- animateChild,
4
- AnimationTriggerMetadata,
5
- group,
6
- query,
7
- state,
8
- style,
9
- transition,
10
- trigger,
11
- } from '@angular/animations';
12
-
13
- const defaultTiming = '250ms ease-out';
14
-
15
- const defaultActiveOpacity = '1';
16
- const defaultHiddenOpacity = '0.05';
17
-
18
- interface IVirtualSliderOptions {
19
- timing: string;
20
- activeOpacity?: string;
21
- hiddenOpacity?: string;
22
- }
23
-
24
- type VirtualSlideStateShow = 'show';
25
- type VirtualSlideStateHide = 'hide';
26
-
27
- export const showVirtualSlide: VirtualSlideStateShow = 'show';
28
- export const hideVirtualSlide: VirtualSlideStateHide = 'hide';
29
-
30
- export type VirtualSlideAnimationsStates =
31
- | VirtualSlideStateShow
32
- | VirtualSlideStateHide;
33
-
34
- export const VirtualSliderDirectPushedNext = 'direct-push-next';
35
- export const VirtualSliderDirectPushedPrev = 'direct-push-prev';
36
- export const VirtualSliderReversePushedNext = 'reverse-push-next';
37
- export const VirtualSliderReversePushedPrev = 'reverse-push-prev';
38
-
39
- export type VirtualSliderAnimationStates =
40
- | 'direct-push-next'
41
- | 'direct-push-prev'
42
- | 'reverse-push-next'
43
- | 'reverse-push-prev';
44
-
45
- const options: IVirtualSliderOptions = {
46
- timing: defaultTiming,
47
- activeOpacity: defaultActiveOpacity,
48
- hiddenOpacity: defaultHiddenOpacity,
49
- };
50
-
51
- const slideLeft = group([
52
- animate(options.timing, style({ transform: 'translateX(-50%)' })),
53
- query('@virtualSlide', animateChild()),
54
- ]);
55
- const slideRight = group([
56
- animate(options.timing, style({ transform: 'translateX(0)' })),
57
- query('@virtualSlide', animateChild()),
58
- ]);
59
-
60
- export const virtualSliderAnimations: AnimationTriggerMetadata[] = [
61
- trigger('virtualSlide', [
62
- transition('void => *', animate(0)),
63
- state(showVirtualSlide, style({ opacity: options.activeOpacity || 0 })),
64
- state(hideVirtualSlide, style({ opacity: options.hiddenOpacity || 0 })),
65
- transition('* => *', animate(options.timing)),
66
- ]),
67
- trigger('virtualSlider', [
68
- transition('void => *', animate(0)),
69
- // Slides: A→B
70
- state(
71
- VirtualSliderDirectPushedNext,
72
- style({
73
- flexDirection: 'row',
74
- transform: 'translateX(-50%)',
75
- }),
76
- ),
77
- transition(`* => ${VirtualSliderDirectPushedNext}`, [
78
- style({
79
- flexDirection: 'row',
80
- transform: 'translateX(0)',
81
- }),
82
- slideLeft,
83
- ]),
84
- // Slides: A←B
85
- state(
86
- VirtualSliderDirectPushedPrev,
87
- style({
88
- flexDirection: 'row',
89
- transform: 'translateX(0)',
90
- }),
91
- ),
92
- transition(`* => ${VirtualSliderDirectPushedPrev}`, [
93
- style({
94
- flexDirection: 'row',
95
- transform: 'translateX(-50%)',
96
- }),
97
- slideRight,
98
- ]),
99
- // Slides: B→A
100
- state(
101
- VirtualSliderReversePushedNext,
102
- style({
103
- flexDirection: 'row-reverse',
104
- transform: 'translateX(-50%)',
105
- }),
106
- ),
107
- transition(`* => ${VirtualSliderReversePushedNext}`, [
108
- style({
109
- flexDirection: 'row-reverse',
110
- transform: 'translateX(0)',
111
- }),
112
- slideLeft,
113
- ]),
114
- // Slides: B←A
115
- state(
116
- VirtualSliderReversePushedPrev,
117
- style({
118
- flexDirection: 'row-reverse',
119
- transform: 'translateX(0)',
120
- }),
121
- ),
122
- transition(`* => ${VirtualSliderReversePushedPrev}`, [
123
- style({
124
- flexDirection: 'row-reverse',
125
- transform: 'translateX(-50%)',
126
- }),
127
- slideRight,
128
- ]),
129
- ]),
130
- ];
@@ -1,35 +0,0 @@
1
- import { Component } from '@angular/core';
2
-
3
- export interface IIdAndBriefDemo<Brief> {
4
- readonly id: string;
5
- readonly brief: Brief;
6
- }
7
-
8
- interface Brief {
9
- readonly title: string;
10
- }
11
-
12
- function items<B>(o: Record<string, B>): readonly IIdAndBriefDemo<B>[] {
13
- return Object.keys(o).map((id) => ({ id, brief: o[id] }));
14
- }
15
-
16
- @Component({
17
- selector: 'sneat-webstorm-type-err-demo',
18
- template: `
19
- @for (item of items; track item.id) {
20
- {{ item.brief.title }}
21
- }
22
- `,
23
- })
24
- export class WebstormTypeErrDemoComponent {
25
- protected readonly _items: Record<string, Brief> = {
26
- first: { title: 'First' },
27
- };
28
-
29
- protected get items(): readonly IIdAndBriefDemo<Brief>[] {
30
- return items(this._items);
31
- }
32
-
33
- protected readonly idExpr = (i: number, record: { id: string }): string =>
34
- record.id;
35
- }
package/src/test-setup.ts DELETED
@@ -1,3 +0,0 @@
1
- import { setupTestEnvironment } from '@sneat/core/testing';
2
-
3
- setupTestEnvironment();
package/tsconfig.json DELETED
@@ -1,13 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.angular.json",
3
- "files": [],
4
- "include": [],
5
- "references": [
6
- {
7
- "path": "./tsconfig.lib.json"
8
- },
9
- {
10
- "path": "./tsconfig.spec.json"
11
- }
12
- ]
13
- }
package/tsconfig.lib.json DELETED
@@ -1,19 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.lib.base.json",
3
- "exclude": [
4
- "vite.config.ts",
5
- "vite.config.mts",
6
- "vitest.config.ts",
7
- "vitest.config.mts",
8
- "src/**/*.test.ts",
9
- "src/**/*.spec.ts",
10
- "src/**/*.test.tsx",
11
- "src/**/*.spec.tsx",
12
- "src/**/*.test.js",
13
- "src/**/*.spec.js",
14
- "src/**/*.test.jsx",
15
- "src/**/*.spec.jsx",
16
- "src/test-setup.ts",
17
- "src/lib/testing/**/*"
18
- ]
19
- }
@@ -1,7 +0,0 @@
1
- {
2
- "extends": "./tsconfig.lib.json",
3
- "compilerOptions": {
4
- "declarationMap": false
5
- },
6
- "angularCompilerOptions": {}
7
- }