@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.
- package/esm2022/index.js +15 -0
- package/esm2022/index.js.map +1 -0
- package/esm2022/lib/app-version/app-version.component.js +16 -0
- package/esm2022/lib/app-version/app-version.component.js.map +1 -0
- package/esm2022/lib/app-version/build-info.js +5 -0
- package/esm2022/lib/app-version/build-info.js.map +1 -0
- package/esm2022/lib/app-version/index.js +3 -0
- package/esm2022/lib/app-version/index.js.map +1 -0
- package/esm2022/lib/card-list/index.js +2 -0
- package/esm2022/lib/card-list/index.js.map +1 -0
- package/esm2022/lib/card-list/sneat-card-list.component.js +106 -0
- package/esm2022/lib/card-list/sneat-card-list.component.js.map +1 -0
- package/esm2022/lib/copyright/copyright.component.js +11 -0
- package/esm2022/lib/copyright/copyright.component.js.map +1 -0
- package/esm2022/lib/copyright/index.js +2 -0
- package/esm2022/lib/copyright/index.js.map +1 -0
- package/esm2022/lib/country-input/country-input.component.js +59 -0
- package/esm2022/lib/country-input/country-input.component.js.map +1 -0
- package/esm2022/lib/country-input/index.js +2 -0
- package/esm2022/lib/country-input/index.js.map +1 -0
- package/esm2022/lib/country-selector/countries-loader.service.js +91 -0
- package/esm2022/lib/country-selector/countries-loader.service.js.map +1 -0
- package/esm2022/lib/country-selector/countries.js +22 -0
- package/esm2022/lib/country-selector/countries.js.map +1 -0
- package/esm2022/lib/country-selector/country-selector.component.js +103 -0
- package/esm2022/lib/country-selector/country-selector.component.js.map +1 -0
- package/esm2022/lib/country-selector/country-selector.service.js +15 -0
- package/esm2022/lib/country-selector/country-selector.service.js.map +1 -0
- package/esm2022/lib/country-selector/index.js +5 -0
- package/esm2022/lib/country-selector/index.js.map +1 -0
- package/esm2022/lib/date-input/date-input.component.js +70 -0
- package/esm2022/lib/date-input/date-input.component.js.map +1 -0
- package/esm2022/lib/date-input/date-modal.component.js +53 -0
- package/esm2022/lib/date-input/date-modal.component.js.map +1 -0
- package/esm2022/lib/dialog-header/dialog-header.component.js +23 -0
- package/esm2022/lib/dialog-header/dialog-header.component.js.map +1 -0
- package/esm2022/lib/dialog-header/index.js +2 -0
- package/esm2022/lib/dialog-header/index.js.map +1 -0
- package/esm2022/lib/error-card/index.js +2 -0
- package/esm2022/lib/error-card/index.js.map +1 -0
- package/esm2022/lib/error-card/sneat-error-card.component.js +23 -0
- package/esm2022/lib/error-card/sneat-error-card.component.js.map +1 -0
- package/esm2022/lib/filter-item/filter-item.component.js +37 -0
- package/esm2022/lib/filter-item/filter-item.component.js.map +1 -0
- package/esm2022/lib/filter-item/index.js +2 -0
- package/esm2022/lib/filter-item/index.js.map +1 -0
- package/esm2022/lib/pipes/country-emoji.pipe.js +69 -0
- package/esm2022/lib/pipes/country-emoji.pipe.js.map +1 -0
- package/esm2022/lib/pipes/decimal64p2.pipe.js +47 -0
- package/esm2022/lib/pipes/decimal64p2.pipe.js.map +1 -0
- package/esm2022/lib/pipes/gender.pipes.js +69 -0
- package/esm2022/lib/pipes/gender.pipes.js.map +1 -0
- package/esm2022/lib/pipes/index.js +7 -0
- package/esm2022/lib/pipes/index.js.map +1 -0
- package/esm2022/lib/pipes/long-month-name.pipe.js +41 -0
- package/esm2022/lib/pipes/long-month-name.pipe.js.map +1 -0
- package/esm2022/lib/pipes/short-month-name.pipe.js +31 -0
- package/esm2022/lib/pipes/short-month-name.pipe.js.map +1 -0
- package/esm2022/lib/pipes/team-emoji.pipe.js +31 -0
- package/esm2022/lib/pipes/team-emoji.pipe.js.map +1 -0
- package/esm2022/lib/save-event.js +2 -0
- package/esm2022/lib/save-event.js.map +1 -0
- package/esm2022/lib/user-country/user-country.component.js +136 -0
- package/esm2022/lib/user-country/user-country.component.js.map +1 -0
- package/esm2022/lib/virtual-slider/virtual-slider.js +83 -0
- package/esm2022/lib/virtual-slider/virtual-slider.js.map +1 -0
- package/esm2022/sneat-components.js +5 -0
- package/esm2022/sneat-components.js.map +1 -0
- package/lib/app-version/app-version.component.d.ts +9 -0
- package/lib/app-version/build-info.d.ts +4 -0
- package/lib/card-list/sneat-card-list.component.d.ts +43 -0
- package/lib/copyright/copyright.component.d.ts +5 -0
- package/lib/country-input/country-input.component.d.ts +16 -0
- package/lib/country-selector/countries-loader.service.d.ts +43 -0
- package/lib/country-selector/countries.d.ts +25 -0
- package/lib/country-selector/country-selector.component.d.ts +31 -0
- package/lib/country-selector/country-selector.service.d.ts +8 -0
- package/lib/date-input/date-input.component.d.ts +21 -0
- package/lib/date-input/date-modal.component.d.ts +13 -0
- package/lib/dialog-header/dialog-header.component.d.ts +8 -0
- package/lib/error-card/sneat-error-card.component.d.ts +8 -0
- package/lib/filter-item/filter-item.component.d.ts +13 -0
- package/lib/pipes/country-emoji.pipe.d.ts +29 -0
- package/lib/pipes/decimal64p2.pipe.d.ts +12 -0
- package/lib/pipes/gender.pipes.d.ts +20 -0
- package/lib/pipes/long-month-name.pipe.d.ts +7 -0
- package/lib/pipes/short-month-name.pipe.d.ts +8 -0
- package/lib/pipes/team-emoji.pipe.d.ts +8 -0
- package/lib/save-event.d.ts +5 -0
- package/lib/user-country/user-country.component.d.ts +24 -0
- package/lib/virtual-slider/virtual-slider.d.ts +13 -0
- package/package.json +14 -2
- package/sneat-components.d.ts +5 -0
- package/eslint.config.js +0 -7
- package/ng-package.json +0 -7
- package/project.json +0 -38
- package/src/assets/data/countries.json +0 -1730
- package/src/lib/app-version/app-version.component.html +0 -15
- package/src/lib/app-version/app-version.component.spec.ts +0 -25
- package/src/lib/app-version/app-version.component.ts +0 -17
- package/src/lib/app-version/build-info.ts +0 -8
- package/src/lib/card-list/sneat-card-list.component.html +0 -108
- package/src/lib/card-list/sneat-card-list.component.spec.ts +0 -131
- package/src/lib/card-list/sneat-card-list.component.ts +0 -125
- package/src/lib/copyright/copyright.component.html +0 -1
- package/src/lib/copyright/copyright.component.spec.ts +0 -24
- package/src/lib/copyright/copyright.component.ts +0 -7
- package/src/lib/country-input/country-input.component.html +0 -33
- package/src/lib/country-input/country-input.component.spec.ts +0 -84
- package/src/lib/country-input/country-input.component.ts +0 -76
- package/src/lib/country-selector/countries-loader.service.spec.ts +0 -149
- package/src/lib/country-selector/countries-loader.service.ts +0 -100
- package/src/lib/country-selector/countries.ts +0 -42
- package/src/lib/country-selector/country-selector.component.html +0 -24
- package/src/lib/country-selector/country-selector.component.spec.ts +0 -5
- package/src/lib/country-selector/country-selector.component.ts +0 -125
- package/src/lib/country-selector/country-selector.service.spec.ts +0 -23
- package/src/lib/country-selector/country-selector.service.ts +0 -11
- package/src/lib/date-input/date-input.component.html +0 -39
- package/src/lib/date-input/date-input.component.spec.ts +0 -179
- package/src/lib/date-input/date-input.component.ts +0 -97
- package/src/lib/date-input/date-modal.component.html +0 -23
- package/src/lib/date-input/date-modal.component.spec.ts +0 -105
- package/src/lib/date-input/date-modal.component.ts +0 -67
- package/src/lib/dialog-header/dialog-header.component.html +0 -8
- package/src/lib/dialog-header/dialog-header.component.spec.ts +0 -67
- package/src/lib/dialog-header/dialog-header.component.ts +0 -26
- package/src/lib/dismissable.ts +0 -6
- package/src/lib/error-card/sneat-error-card.component.html +0 -20
- package/src/lib/error-card/sneat-error-card.component.spec.ts +0 -49
- package/src/lib/error-card/sneat-error-card.component.ts +0 -28
- package/src/lib/filter-item/filter-item.component.html +0 -19
- package/src/lib/filter-item/filter-item.component.spec.ts +0 -83
- package/src/lib/filter-item/filter-item.component.ts +0 -47
- package/src/lib/pipes/country-emoji.pipe.spec.ts +0 -116
- package/src/lib/pipes/country-emoji.pipe.ts +0 -67
- package/src/lib/pipes/decimal64p2.pipe.spec.ts +0 -61
- package/src/lib/pipes/decimal64p2.pipe.ts +0 -35
- package/src/lib/pipes/gender.pipes.spec.ts +0 -84
- package/src/lib/pipes/gender.pipes.ts +0 -65
- package/src/lib/pipes/long-month-name.pipe.spec.ts +0 -36
- package/src/lib/pipes/long-month-name.pipe.ts +0 -35
- package/src/lib/pipes/short-month-name.pipe.spec.ts +0 -28
- package/src/lib/pipes/short-month-name.pipe.ts +0 -26
- package/src/lib/pipes/team-emoji.pipe.spec.ts +0 -27
- package/src/lib/pipes/team-emoji.pipe.ts +0 -26
- package/src/lib/save-event.ts +0 -5
- package/src/lib/test-ide-bug.ts +0 -24
- package/src/lib/user-country/user-country.component.html +0 -44
- package/src/lib/user-country/user-country.component.spec.ts +0 -136
- package/src/lib/user-country/user-country.component.ts +0 -172
- package/src/lib/virtual-slider/odd-even-virtual-slider.ts +0 -1
- package/src/lib/virtual-slider/virtual-slider.ts +0 -130
- package/src/lib/webstorm-type-err-demo.component.ts +0 -35
- package/src/test-setup.ts +0 -3
- package/tsconfig.json +0 -13
- package/tsconfig.lib.json +0 -19
- package/tsconfig.lib.prod.json +0 -7
- package/tsconfig.spec.json +0 -31
- package/vite.config.mts +0 -10
- /package/{src/index.ts → index.d.ts} +0 -0
- /package/{src/lib/app-version/index.ts → lib/app-version/index.d.ts} +0 -0
- /package/{src/lib/card-list/index.ts → lib/card-list/index.d.ts} +0 -0
- /package/{src/lib/copyright/index.ts → lib/copyright/index.d.ts} +0 -0
- /package/{src/lib/country-input/index.ts → lib/country-input/index.d.ts} +0 -0
- /package/{src/lib/country-selector/index.ts → lib/country-selector/index.d.ts} +0 -0
- /package/{src/lib/dialog-header/index.ts → lib/dialog-header/index.d.ts} +0 -0
- /package/{src/lib/error-card/index.ts → lib/error-card/index.d.ts} +0 -0
- /package/{src/lib/filter-item/index.ts → lib/filter-item/index.d.ts} +0 -0
- /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
|
-
}
|
package/src/lib/save-event.ts
DELETED
package/src/lib/test-ide-bug.ts
DELETED
|
@@ -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
package/tsconfig.json
DELETED
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
|
-
}
|