@sneat/space-components 0.25.0 → 0.26.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.
- package/esm2022/lib/avatar/avatar.component.js +10 -10
- package/esm2022/lib/avatar/avatar.component.js.map +1 -1
- package/esm2022/lib/space-base-component.directive.js +20 -12
- package/esm2022/lib/space-base-component.directive.js.map +1 -1
- package/esm2022/lib/space-component-base-params.service.js +3 -3
- package/esm2022/lib/space-item-page-base.component.js +4 -2
- package/esm2022/lib/space-item-page-base.component.js.map +1 -1
- package/esm2022/lib/space-menu/space-menu.component.js +12 -9
- package/esm2022/lib/space-menu/space-menu.component.js.map +1 -1
- package/esm2022/lib/space-page-base-component.service.js +3 -3
- package/esm2022/lib/space-page-title/space-page-title.component.js +19 -16
- package/esm2022/lib/space-page-title/space-page-title.component.js.map +1 -1
- package/esm2022/lib/spaces-card/spaces-card.component.js +18 -11
- package/esm2022/lib/spaces-card/spaces-card.component.js.map +1 -1
- package/esm2022/lib/spaces-list/spaces-list.component.js +17 -20
- package/esm2022/lib/spaces-list/spaces-list.component.js.map +1 -1
- package/esm2022/lib/spaces-menu/spaces-menu.component.js +20 -18
- package/esm2022/lib/spaces-menu/spaces-menu.component.js.map +1 -1
- package/lib/avatar/avatar.component.d.ts +2 -2
- package/lib/space-base-component.directive.d.ts +1 -1
- package/lib/space-page-title/space-page-title.component.d.ts +4 -4
- package/lib/spaces-list/spaces-list.component.d.ts +5 -5
- package/lib/spaces-menu/spaces-menu.component.d.ts +3 -3
- package/package.json +6 -5
- package/tsconfig.lib.prod.tsbuildinfo +1 -1
|
@@ -1,27 +1,30 @@
|
|
|
1
1
|
import { TitleCasePipe } from '@angular/common';
|
|
2
|
-
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
|
3
|
-
import { IonTitle } from '@ionic/angular
|
|
2
|
+
import { ChangeDetectionStrategy, Component, Input, input } from '@angular/core';
|
|
3
|
+
import { IonTitle } from '@ionic/angular';
|
|
4
4
|
import * as i0 from "@angular/core";
|
|
5
5
|
export class SpacePageTitleComponent {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.icon = input(/* @ts-ignore */
|
|
8
|
+
...(ngDevMode ? [undefined, { debugName: "icon" }] : /* istanbul ignore next */ []));
|
|
9
|
+
this.generalTitle = input(/* @ts-ignore */
|
|
10
|
+
...(ngDevMode ? [undefined, { debugName: "generalTitle" }] : /* istanbul ignore next */ []));
|
|
11
|
+
this.titlesBySpaceType = input(/* @ts-ignore */
|
|
12
|
+
...(ngDevMode ? [undefined, { debugName: "titlesBySpaceType" }] : /* istanbul ignore next */ []));
|
|
13
|
+
}
|
|
6
14
|
get typeTitle() {
|
|
7
|
-
|
|
8
|
-
|
|
15
|
+
const titlesBySpaceType = this.titlesBySpaceType();
|
|
16
|
+
return this.space?.type && titlesBySpaceType
|
|
17
|
+
? titlesBySpaceType[this.space.type]
|
|
9
18
|
: '';
|
|
10
19
|
}
|
|
11
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
12
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "
|
|
20
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SpacePageTitleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
21
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: SpacePageTitleComponent, isStandalone: true, selector: "sneat-space-page-title", inputs: { icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, generalTitle: { classPropertyName: "generalTitle", publicName: "generalTitle", isSignal: true, isRequired: false, transformFunction: null }, space: { classPropertyName: "space", publicName: "space", isSignal: false, isRequired: true, transformFunction: null }, titlesBySpaceType: { classPropertyName: "titlesBySpaceType", publicName: "titlesBySpaceType", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<ion-title>\n @if (space) {\n @if (!!typeTitle) {\n <span style=\"margin-right: 0.5em\">{{ icon() }}</span>\n <span>{{ typeTitle }}</span>\n } @else {\n <span style=\"margin-right: 0.5em\">{{ icon() }}</span>\n <span\n >{{ generalTitle() }} @\n {{ space.brief?.title || (space.type | titlecase) || space.id }}</span\n >\n }\n } @else {\n <span style=\"margin-right: 0.5em\">{{ icon() }}</span>\n <span>{{ generalTitle() }}</span>\n }\n</ion-title>\n", dependencies: [{ kind: "component", type: IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "pipe", type: TitleCasePipe, name: "titlecase" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
13
22
|
}
|
|
14
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
23
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SpacePageTitleComponent, decorators: [{
|
|
15
24
|
type: Component,
|
|
16
|
-
args: [{ imports: [TitleCasePipe, IonTitle], changeDetection: ChangeDetectionStrategy.OnPush, selector: 'sneat-space-page-title', template: "<ion-title>\n @if (space) {\n @if (!!typeTitle) {\n <span style=\"margin-right: 0.5em\">{{ icon }}</span>\n <span>{{ typeTitle }}</span>\n } @else {\n <span style=\"margin-right: 0.5em\">{{ icon }}</span>\n <span\n >{{ generalTitle }} @\n {{ space.brief?.title || (space.type | titlecase) || space.id }}</span\n >\n }\n } @else {\n <span style=\"margin-right: 0.5em\">{{ icon }}</span>\n <span>{{ generalTitle }}</span>\n }\n</ion-title>\n" }]
|
|
17
|
-
}], propDecorators: { icon: [{
|
|
18
|
-
type: Input
|
|
19
|
-
}], generalTitle: [{
|
|
20
|
-
type: Input
|
|
21
|
-
}], space: [{
|
|
25
|
+
args: [{ imports: [TitleCasePipe, IonTitle], changeDetection: ChangeDetectionStrategy.OnPush, selector: 'sneat-space-page-title', template: "<ion-title>\n @if (space) {\n @if (!!typeTitle) {\n <span style=\"margin-right: 0.5em\">{{ icon() }}</span>\n <span>{{ typeTitle }}</span>\n } @else {\n <span style=\"margin-right: 0.5em\">{{ icon() }}</span>\n <span\n >{{ generalTitle() }} @\n {{ space.brief?.title || (space.type | titlecase) || space.id }}</span\n >\n }\n } @else {\n <span style=\"margin-right: 0.5em\">{{ icon() }}</span>\n <span>{{ generalTitle() }}</span>\n }\n</ion-title>\n" }]
|
|
26
|
+
}], propDecorators: { icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], generalTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "generalTitle", required: false }] }], space: [{
|
|
22
27
|
type: Input,
|
|
23
28
|
args: [{ required: true }]
|
|
24
|
-
}], titlesBySpaceType: [{
|
|
25
|
-
type: Input
|
|
26
|
-
}] } });
|
|
29
|
+
}], titlesBySpaceType: [{ type: i0.Input, args: [{ isSignal: true, alias: "titlesBySpaceType", required: false }] }] } });
|
|
27
30
|
//# sourceMappingURL=space-page-title.component.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"space-page-title.component.js","sourceRoot":"","sources":["../../../../../../../libs/space/components/src/lib/space-page-title/space-page-title.component.ts","../../../../../../../libs/space/components/src/lib/space-page-title/space-page-title.component.html"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,uBAAuB,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"space-page-title.component.js","sourceRoot":"","sources":["../../../../../../../libs/space/components/src/lib/space-page-title/space-page-title.component.ts","../../../../../../../libs/space/components/src/lib/space-page-title/space-page-title.component.html"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,uBAAuB,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACjF,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;;AAS1C,MAAM,OAAO,uBAAuB;IANpC;QAOW,SAAI,GAAG,KAAK;4FAAU,CAAC;QACvB,iBAAY,GAAG,KAAK;oGAAU,CAAC;QAK/B,sBAAiB,GAAG,KAAK;yGAA0B,CAAC;KAQ9D;IANC,IAAW,SAAS;QAClB,MAAM,iBAAiB,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACnD,OAAO,IAAI,CAAC,KAAK,EAAE,IAAI,IAAI,iBAAiB;YAC1C,CAAC,CAAC,iBAAiB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;YACpC,CAAC,CAAC,EAAE,CAAC;IACT,CAAC;8GAdU,uBAAuB;kGAAvB,uBAAuB,onBCXpC,ggBAiBA,4CDX2B,QAAQ,4EAAvB,aAAa;;2FAKZ,uBAAuB;kBANnC,SAAS;8BACC,CAAC,aAAa,EAAE,QAAQ,CAAC,mBACjB,uBAAuB,CAAC,MAAM,YACrC,wBAAwB;;sBASjC,KAAK;uBAAC,EAAE,QAAQ,EAAE,IAAI,EAAE","sourcesContent":["import { TitleCasePipe } from '@angular/common';\nimport { ChangeDetectionStrategy, Component, Input, input } from '@angular/core';\nimport { IonTitle } from '@ionic/angular';\nimport { ISpaceContext } from '@sneat/space-models';\n\n@Component({\n imports: [TitleCasePipe, IonTitle],\n changeDetection: ChangeDetectionStrategy.OnPush,\n selector: 'sneat-space-page-title',\n templateUrl: './space-page-title.component.html',\n})\nexport class SpacePageTitleComponent {\n readonly icon = input<string>();\n readonly generalTitle = input<string>();\n // TODO: Skipped for migration because:\n // This input is used in a control flow expression (e.g. `@if` or `*ngIf`)\n // and migrating would break narrowing currently.\n @Input({ required: true }) space?: ISpaceContext;\n readonly titlesBySpaceType = input<Record<string, string>>();\n\n public get typeTitle(): string {\n const titlesBySpaceType = this.titlesBySpaceType();\n return this.space?.type && titlesBySpaceType\n ? titlesBySpaceType[this.space.type]\n : '';\n }\n}\n","<ion-title>\n @if (space) {\n @if (!!typeTitle) {\n <span style=\"margin-right: 0.5em\">{{ icon() }}</span>\n <span>{{ typeTitle }}</span>\n } @else {\n <span style=\"margin-right: 0.5em\">{{ icon() }}</span>\n <span\n >{{ generalTitle() }} @\n {{ space.brief?.title || (space.type | titlecase) || space.id }}</span\n >\n }\n } @else {\n <span style=\"margin-right: 0.5em\">{{ icon() }}</span>\n <span>{{ generalTitle() }}</span>\n }\n</ion-title>\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ChangeDetectionStrategy, Component, computed, effect, inject, signal, viewChild, } from '@angular/core';
|
|
2
2
|
import { toSignal } from '@angular/core/rxjs-interop';
|
|
3
3
|
import { FormsModule } from '@angular/forms';
|
|
4
|
-
import { IonButton, IonButtons, IonCard, IonCardContent, IonCardTitle, IonIcon, IonInput, IonItem, IonLabel, IonList, IonSkeletonText, IonSpinner, ToastController, } from '@ionic/angular
|
|
4
|
+
import { IonButton, IonButtons, IonCard, IonCardContent, IonCardTitle, IonIcon, IonInput, IonItem, IonLabel, IonList, IonSkeletonText, IonSpinner, ToastController, } from '@ionic/angular';
|
|
5
5
|
import { AnalyticsService } from '@sneat/core';
|
|
6
6
|
import { ErrorLogger } from '@sneat/core';
|
|
7
7
|
import { SpaceNavService, SpaceService } from '@sneat/space-services';
|
|
@@ -26,7 +26,8 @@ export class SpacesCardComponent {
|
|
|
26
26
|
this.spaceService = inject(SpaceService);
|
|
27
27
|
this.analyticsService = inject(AnalyticsService);
|
|
28
28
|
this.toastController = inject(ToastController);
|
|
29
|
-
this.addSpaceInput = viewChild('addTeamInput',
|
|
29
|
+
this.addSpaceInput = viewChild('addTeamInput', /* @ts-ignore */
|
|
30
|
+
...(ngDevMode ? [{ debugName: "addSpaceInput" }] : /* istanbul ignore next */ []));
|
|
30
31
|
this.userState = toSignal(this.userService.userState);
|
|
31
32
|
// undefined => user record not loaded yet (render the loading row).
|
|
32
33
|
this.spaces = computed(() => {
|
|
@@ -37,7 +38,8 @@ export class SpacesCardComponent {
|
|
|
37
38
|
return Object.entries(record.spaces ?? {})
|
|
38
39
|
.map(([id, brief]) => ({ id, brief }))
|
|
39
40
|
.sort((a, b) => (a.brief.title > b.brief.title ? 1 : -1));
|
|
40
|
-
},
|
|
41
|
+
}, /* @ts-ignore */
|
|
42
|
+
...(ngDevMode ? [{ debugName: "spaces" }] : /* istanbul ignore next */ []));
|
|
41
43
|
// Adapts the user's spaces to the shape SpacesListComponent renders, so the
|
|
42
44
|
// card reuses that component (icon + title decode + navigation) instead of
|
|
43
45
|
// duplicating the row markup.
|
|
@@ -45,11 +47,16 @@ export class SpacesCardComponent {
|
|
|
45
47
|
id,
|
|
46
48
|
type: brief.type,
|
|
47
49
|
brief: { title: brief.title, type: brief.type, roles: brief.roles },
|
|
48
|
-
})),
|
|
49
|
-
|
|
50
|
-
this.
|
|
51
|
-
|
|
52
|
-
this.
|
|
50
|
+
})), /* @ts-ignore */
|
|
51
|
+
...(ngDevMode ? [{ debugName: "spaceContexts" }] : /* istanbul ignore next */ []));
|
|
52
|
+
this.loadingState = computed(() => this.userState()?.status === 'authenticated' ? 'Loading' : 'Authenticating', /* @ts-ignore */
|
|
53
|
+
...(ngDevMode ? [{ debugName: "loadingState" }] : /* istanbul ignore next */ []));
|
|
54
|
+
this.showAdd = signal(false, /* @ts-ignore */
|
|
55
|
+
...(ngDevMode ? [{ debugName: "showAdd" }] : /* istanbul ignore next */ []));
|
|
56
|
+
this.spaceName = signal('', /* @ts-ignore */
|
|
57
|
+
...(ngDevMode ? [{ debugName: "spaceName" }] : /* istanbul ignore next */ []));
|
|
58
|
+
this.adding = signal(false, /* @ts-ignore */
|
|
59
|
+
...(ngDevMode ? [{ debugName: "adding" }] : /* istanbul ignore next */ []));
|
|
53
60
|
// Auto-open the "add space" form once we know the user has no spaces.
|
|
54
61
|
effect(() => {
|
|
55
62
|
const spaces = this.spaces();
|
|
@@ -123,10 +130,10 @@ export class SpacesCardComponent {
|
|
|
123
130
|
cancelAdd() {
|
|
124
131
|
this.showAdd.set(false);
|
|
125
132
|
}
|
|
126
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
127
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "
|
|
133
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SpacesCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
134
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: SpacesCardComponent, isStandalone: true, selector: "sneat-spaces-card", viewQueries: [{ propertyName: "addSpaceInput", first: true, predicate: ["addTeamInput"], descendants: true, isSignal: true }], ngImport: i0, template: "<ion-card>\n <ion-item>\n <ion-label>\n <ion-card-title color=\"medium\">Spaces</ion-card-title>\n </ion-label>\n <ion-buttons slot=\"end\" (click)=\"startAddingSpace()\">\n @if (!showAdd()) {\n <ion-button color=\"primary\">\n <ion-icon name=\"add\" slot=\"start\" />\n <ion-label>Add</ion-label>\n </ion-button>\n }\n </ion-buttons>\n </ion-item>\n @if (spaceContexts(); as spaceContexts) {\n <ion-list>\n <sneat-spaces-list\n [userID]=\"currentUserID\"\n [spaces]=\"spaceContexts\"\n [allowLeave]=\"true\"\n />\n </ion-list>\n } @else {\n <ion-list>\n <ion-item>\n <ion-spinner name=\"\" slot=\"start\" color=\"medium\" />\n <ion-buttons slot=\"start\">\n <ion-button disabled=\"disabled\" style=\"text-transform: none\"\n >{{ loadingState() }}...\n </ion-button>\n </ion-buttons>\n <ion-skeleton-text animated />\n </ion-item>\n </ion-list>\n }\n\n @if (showAdd()) {\n <ion-item [disabled]=\"adding()\">\n <ion-input\n (keyup.enter)=\"addSpace()\"\n #addTeamInput\n [ngModel]=\"spaceName()\"\n (ngModelChange)=\"spaceName.set($event)\"\n (keyup.escape)=\"cancelAdd()\"\n placeholder=\"New team name\"\n />\n <ion-buttons slot=\"end\">\n <ion-button color=\"primary\" fill=\"solid\" (click)=\"addSpace()\">\n <ion-label>Create</ion-label>\n </ion-button>\n @if (!!spaces()?.length) {\n <ion-button (click)=\"cancelAdd()\" color=\"medium\" title=\"Cancel\">\n @if (adding()) {\n <ion-spinner />\n } @else {\n <ion-icon name=\"close-outline\" />\n }\n </ion-button>\n }\n </ion-buttons>\n </ion-item>\n }\n @if (showAdd()) {\n <ion-card-content>\n <p>Enter team name and click \"Create\" button to add a new team.</p>\n </ion-card-content>\n }\n</ion-card>\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: IonInput, selector: "ion-input", inputs: ["accept", "autocapitalize", "autocomplete", "autocorrect", "autofocus", "clearInput", "clearOnEdit", "color", "counter", "counterFormatter", "debounce", "disabled", "enterkeyhint", "errorText", "fill", "helperText", "inputmode", "label", "labelPlacement", "max", "maxlength", "min", "minlength", "mode", "multiple", "name", "pattern", "placeholder", "readonly", "required", "shape", "size", "spellcheck", "step", "type", "value"] }, { kind: "component", type: IonCard, selector: "ion-card", inputs: ["button", "color", "disabled", "download", "href", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: IonCardTitle, selector: "ion-card-title", inputs: ["color", "mode"] }, { kind: "component", type: IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"], outputs: ["ionFocus", "ionBlur"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonList, selector: "ion-list", inputs: ["inset", "lines", "mode"] }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: IonSkeletonText, selector: "ion-skeleton-text", inputs: ["animated"] }, { kind: "component", type: IonCardContent, selector: "ion-card-content", inputs: ["mode"] }, { kind: "component", type: SpacesListComponent, selector: "sneat-spaces-list", inputs: ["userID", "spaces", "pathPrefix", "allowLeave"], outputs: ["beforeNavigateToSpace", "leftSpace"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
128
135
|
}
|
|
129
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
136
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SpacesCardComponent, decorators: [{
|
|
130
137
|
type: Component,
|
|
131
138
|
args: [{ selector: 'sneat-spaces-card', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
|
|
132
139
|
FormsModule,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spaces-card.component.js","sourceRoot":"","sources":["../../../../../../../libs/space/components/src/lib/spaces-card/spaces-card.component.ts","../../../../../../../libs/space/components/src/lib/spaces-card/spaces-card.component.html"],"names":[],"mappings":"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","sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n computed,\n effect,\n inject,\n signal,\n viewChild,\n} from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { FormsModule } from '@angular/forms';\nimport {\n IonButton,\n IonButtons,\n IonCard,\n IonCardContent,\n IonCardTitle,\n IonIcon,\n IonInput,\n IonItem,\n IonLabel,\n IonList,\n IonSkeletonText,\n IonSpinner,\n ToastController,\n} from '@ionic/angular/standalone';\nimport { AnalyticsService, IAnalyticsService } from '@sneat/core';\nimport { IUserSpaceBrief } from '@sneat/auth-models';\nimport { IIdAndBrief } from '@sneat/core';\nimport { ErrorLogger, IErrorLogger } from '@sneat/core';\nimport { ICreateSpaceRequest, ISpaceContext } from '@sneat/space-models';\nimport { SpaceNavService, SpaceService } from '@sneat/space-services';\nimport { SneatUserService } from '@sneat/auth-core';\nimport { SpacesListComponent } from '../spaces-list';\n\n// Signal-based + OnPush so the card repaints reactively when the user record\n// loads, instead of mutating fields inside an rxjs subscription and relying on\n// Zone change detection. The previous version stayed stuck on \"Authenticating...\"\n// when the Firestore onSnapshot update landed outside the Angular zone (the\n// record loaded but the view never repainted). toSignal()/computed() repaint\n// correctly under zone.js too — this is not a zoneless change.\n@Component({\n selector: 'sneat-spaces-card',\n templateUrl: './spaces-card.component.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [\n FormsModule,\n IonInput,\n IonCard,\n IonItem,\n IonLabel,\n IonCardTitle,\n IonButtons,\n IonButton,\n IonIcon,\n IonList,\n IonSpinner,\n IonSkeletonText,\n IonCardContent,\n SpacesListComponent,\n ],\n})\nexport class SpacesCardComponent {\n private readonly errorLogger = inject<IErrorLogger>(ErrorLogger);\n private readonly navService = inject(SpaceNavService);\n private readonly userService = inject(SneatUserService);\n private readonly spaceService = inject(SpaceService);\n private readonly analyticsService =\n inject<IAnalyticsService>(AnalyticsService);\n private readonly toastController = inject(ToastController);\n\n private readonly addSpaceInput = viewChild<IonInput>('addTeamInput');\n\n private readonly userState = toSignal(this.userService.userState);\n\n // undefined => user record not loaded yet (render the loading row).\n protected readonly spaces = computed<\n IIdAndBrief<IUserSpaceBrief>[] | undefined\n >(() => {\n const record = this.userState()?.record;\n if (!record) {\n return undefined;\n }\n return Object.entries(record.spaces ?? {})\n .map(([id, brief]) => ({ id, brief }))\n .sort((a, b) => (a.brief.title > b.brief.title ? 1 : -1));\n });\n\n // Adapts the user's spaces to the shape SpacesListComponent renders, so the\n // card reuses that component (icon + title decode + navigation) instead of\n // duplicating the row markup.\n protected readonly spaceContexts = computed<ISpaceContext[] | undefined>(\n () =>\n this.spaces()?.map(({ id, brief }) => ({\n id,\n type: brief.type,\n brief: { title: brief.title, type: brief.type, roles: brief.roles },\n })),\n );\n\n protected get currentUserID(): string {\n return this.userService.currentUserID ?? '';\n }\n\n protected readonly loadingState = computed(() =>\n this.userState()?.status === 'authenticated' ? 'Loading' : 'Authenticating',\n );\n\n protected readonly showAdd = signal(false);\n protected readonly spaceName = signal('');\n protected readonly adding = signal(false);\n\n public constructor() {\n // Auto-open the \"add space\" form once we know the user has no spaces.\n effect(() => {\n const spaces = this.spaces();\n if (spaces && spaces.length === 0) {\n this.startAddingSpace();\n }\n });\n }\n\n private navigateToSpace(space: ISpaceContext): void {\n this.navService\n .navigateToSpace(space, 'forward')\n .catch(this.errorLogger.logError);\n }\n\n protected addSpace(): void {\n this.analyticsService.logEvent('addSpace');\n const title = this.spaceName().trim();\n if (!title) {\n this.presentToast('Space name is required', 'tertiary');\n return;\n }\n if (this.spaces()?.find((t) => t.brief.title === title)) {\n this.presentToast('You already have a team with the same name', 'danger');\n return;\n }\n const request: ICreateSpaceRequest = {\n type: 'team',\n title,\n };\n this.adding.set(true);\n this.spaceService.createSpace(request).subscribe({\n next: (space) => {\n this.analyticsService.logEvent('spaceCreated', { space: space.id });\n this.adding.set(false);\n this.spaceName.set('');\n // The user record updates via Firestore, which recomputes `spaces`.\n this.navigateToSpace(space);\n },\n error: (err) => {\n this.errorLogger.logError(err, 'Failed to create new team record');\n this.adding.set(false);\n },\n });\n }\n\n private presentToast(message: string, color: string): void {\n this.toastController\n .create({\n position: 'middle',\n message,\n color,\n duration: 5000,\n keyboardClose: true,\n buttons: [{ role: 'cancel', text: 'OK' }],\n })\n .then((toast) =>\n toast\n .present()\n .catch((err) =>\n this.errorLogger.logError(err, 'Failed to present toast'),\n ),\n )\n .catch((err) => this.errorLogger.logError(err, 'Failed to create toast'));\n }\n\n public startAddingSpace(): void {\n this.showAdd.set(true);\n setTimeout(() => {\n const input = this.addSpaceInput();\n if (!input) {\n return;\n }\n input\n .setFocus()\n .catch((err) =>\n this.errorLogger.logError(err, 'Failed to set focus to addTeamInput'),\n );\n }, 200);\n }\n\n protected cancelAdd(): void {\n this.showAdd.set(false);\n }\n}\n","<ion-card>\n <ion-item>\n <ion-label>\n <ion-card-title color=\"medium\">Spaces</ion-card-title>\n </ion-label>\n <ion-buttons slot=\"end\" (click)=\"startAddingSpace()\">\n @if (!showAdd()) {\n <ion-button color=\"primary\">\n <ion-icon name=\"add\" slot=\"start\" />\n <ion-label>Add</ion-label>\n </ion-button>\n }\n </ion-buttons>\n </ion-item>\n @if (spaceContexts(); as spaceContexts) {\n <ion-list>\n <sneat-spaces-list\n [userID]=\"currentUserID\"\n [spaces]=\"spaceContexts\"\n [allowLeave]=\"true\"\n />\n </ion-list>\n } @else {\n <ion-list>\n <ion-item>\n <ion-spinner name=\"\" slot=\"start\" color=\"medium\" />\n <ion-buttons slot=\"start\">\n <ion-button disabled=\"disabled\" style=\"text-transform: none\"\n >{{ loadingState() }}...\n </ion-button>\n </ion-buttons>\n <ion-skeleton-text animated />\n </ion-item>\n </ion-list>\n }\n\n @if (showAdd()) {\n <ion-item [disabled]=\"adding()\">\n <ion-input\n (keyup.enter)=\"addSpace()\"\n #addTeamInput\n [ngModel]=\"spaceName()\"\n (ngModelChange)=\"spaceName.set($event)\"\n (keyup.escape)=\"cancelAdd()\"\n placeholder=\"New team name\"\n />\n <ion-buttons slot=\"end\">\n <ion-button color=\"primary\" fill=\"solid\" (click)=\"addSpace()\">\n <ion-label>Create</ion-label>\n </ion-button>\n @if (!!spaces()?.length) {\n <ion-button (click)=\"cancelAdd()\" color=\"medium\" title=\"Cancel\">\n @if (adding()) {\n <ion-spinner />\n } @else {\n <ion-icon name=\"close-outline\" />\n }\n </ion-button>\n }\n </ion-buttons>\n </ion-item>\n }\n @if (showAdd()) {\n <ion-card-content>\n <p>Enter team name and click \"Create\" button to add a new team.</p>\n </ion-card-content>\n }\n</ion-card>\n"]}
|
|
1
|
+
{"version":3,"file":"spaces-card.component.js","sourceRoot":"","sources":["../../../../../../../libs/space/components/src/lib/spaces-card/spaces-card.component.ts","../../../../../../../libs/space/components/src/lib/spaces-card/spaces-card.component.html"],"names":[],"mappings":"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","sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n computed,\n effect,\n inject,\n signal,\n viewChild,\n} from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { FormsModule } from '@angular/forms';\nimport {\n IonButton,\n IonButtons,\n IonCard,\n IonCardContent,\n IonCardTitle,\n IonIcon,\n IonInput,\n IonItem,\n IonLabel,\n IonList,\n IonSkeletonText,\n IonSpinner,\n ToastController,\n} from '@ionic/angular';\nimport { AnalyticsService, IAnalyticsService } from '@sneat/core';\nimport { IUserSpaceBrief } from '@sneat/auth-models';\nimport { IIdAndBrief } from '@sneat/core';\nimport { ErrorLogger, IErrorLogger } from '@sneat/core';\nimport { ICreateSpaceRequest, ISpaceContext } from '@sneat/space-models';\nimport { SpaceNavService, SpaceService } from '@sneat/space-services';\nimport { SneatUserService } from '@sneat/auth-core';\nimport { SpacesListComponent } from '../spaces-list';\n\n// Signal-based + OnPush so the card repaints reactively when the user record\n// loads, instead of mutating fields inside an rxjs subscription and relying on\n// Zone change detection. The previous version stayed stuck on \"Authenticating...\"\n// when the Firestore onSnapshot update landed outside the Angular zone (the\n// record loaded but the view never repainted). toSignal()/computed() repaint\n// correctly under zone.js too — this is not a zoneless change.\n@Component({\n selector: 'sneat-spaces-card',\n templateUrl: './spaces-card.component.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [\n FormsModule,\n IonInput,\n IonCard,\n IonItem,\n IonLabel,\n IonCardTitle,\n IonButtons,\n IonButton,\n IonIcon,\n IonList,\n IonSpinner,\n IonSkeletonText,\n IonCardContent,\n SpacesListComponent,\n ],\n})\nexport class SpacesCardComponent {\n private readonly errorLogger = inject<IErrorLogger>(ErrorLogger);\n private readonly navService = inject(SpaceNavService);\n private readonly userService = inject(SneatUserService);\n private readonly spaceService = inject(SpaceService);\n private readonly analyticsService =\n inject<IAnalyticsService>(AnalyticsService);\n private readonly toastController = inject(ToastController);\n\n private readonly addSpaceInput = viewChild<IonInput>('addTeamInput');\n\n private readonly userState = toSignal(this.userService.userState);\n\n // undefined => user record not loaded yet (render the loading row).\n protected readonly spaces = computed<\n IIdAndBrief<IUserSpaceBrief>[] | undefined\n >(() => {\n const record = this.userState()?.record;\n if (!record) {\n return undefined;\n }\n return Object.entries(record.spaces ?? {})\n .map(([id, brief]) => ({ id, brief }))\n .sort((a, b) => (a.brief.title > b.brief.title ? 1 : -1));\n });\n\n // Adapts the user's spaces to the shape SpacesListComponent renders, so the\n // card reuses that component (icon + title decode + navigation) instead of\n // duplicating the row markup.\n protected readonly spaceContexts = computed<ISpaceContext[] | undefined>(\n () =>\n this.spaces()?.map(({ id, brief }) => ({\n id,\n type: brief.type,\n brief: { title: brief.title, type: brief.type, roles: brief.roles },\n })),\n );\n\n protected get currentUserID(): string {\n return this.userService.currentUserID ?? '';\n }\n\n protected readonly loadingState = computed(() =>\n this.userState()?.status === 'authenticated' ? 'Loading' : 'Authenticating',\n );\n\n protected readonly showAdd = signal(false);\n protected readonly spaceName = signal('');\n protected readonly adding = signal(false);\n\n public constructor() {\n // Auto-open the \"add space\" form once we know the user has no spaces.\n effect(() => {\n const spaces = this.spaces();\n if (spaces && spaces.length === 0) {\n this.startAddingSpace();\n }\n });\n }\n\n private navigateToSpace(space: ISpaceContext): void {\n this.navService\n .navigateToSpace(space, 'forward')\n .catch(this.errorLogger.logError);\n }\n\n protected addSpace(): void {\n this.analyticsService.logEvent('addSpace');\n const title = this.spaceName().trim();\n if (!title) {\n this.presentToast('Space name is required', 'tertiary');\n return;\n }\n if (this.spaces()?.find((t) => t.brief.title === title)) {\n this.presentToast('You already have a team with the same name', 'danger');\n return;\n }\n const request: ICreateSpaceRequest = {\n type: 'team',\n title,\n };\n this.adding.set(true);\n this.spaceService.createSpace(request).subscribe({\n next: (space) => {\n this.analyticsService.logEvent('spaceCreated', { space: space.id });\n this.adding.set(false);\n this.spaceName.set('');\n // The user record updates via Firestore, which recomputes `spaces`.\n this.navigateToSpace(space);\n },\n error: (err) => {\n this.errorLogger.logError(err, 'Failed to create new team record');\n this.adding.set(false);\n },\n });\n }\n\n private presentToast(message: string, color: string): void {\n this.toastController\n .create({\n position: 'middle',\n message,\n color,\n duration: 5000,\n keyboardClose: true,\n buttons: [{ role: 'cancel', text: 'OK' }],\n })\n .then((toast) =>\n toast\n .present()\n .catch((err) =>\n this.errorLogger.logError(err, 'Failed to present toast'),\n ),\n )\n .catch((err) => this.errorLogger.logError(err, 'Failed to create toast'));\n }\n\n public startAddingSpace(): void {\n this.showAdd.set(true);\n setTimeout(() => {\n const input = this.addSpaceInput();\n if (!input) {\n return;\n }\n input\n .setFocus()\n .catch((err) =>\n this.errorLogger.logError(err, 'Failed to set focus to addTeamInput'),\n );\n }, 200);\n }\n\n protected cancelAdd(): void {\n this.showAdd.set(false);\n }\n}\n","<ion-card>\n <ion-item>\n <ion-label>\n <ion-card-title color=\"medium\">Spaces</ion-card-title>\n </ion-label>\n <ion-buttons slot=\"end\" (click)=\"startAddingSpace()\">\n @if (!showAdd()) {\n <ion-button color=\"primary\">\n <ion-icon name=\"add\" slot=\"start\" />\n <ion-label>Add</ion-label>\n </ion-button>\n }\n </ion-buttons>\n </ion-item>\n @if (spaceContexts(); as spaceContexts) {\n <ion-list>\n <sneat-spaces-list\n [userID]=\"currentUserID\"\n [spaces]=\"spaceContexts\"\n [allowLeave]=\"true\"\n />\n </ion-list>\n } @else {\n <ion-list>\n <ion-item>\n <ion-spinner name=\"\" slot=\"start\" color=\"medium\" />\n <ion-buttons slot=\"start\">\n <ion-button disabled=\"disabled\" style=\"text-transform: none\"\n >{{ loadingState() }}...\n </ion-button>\n </ion-buttons>\n <ion-skeleton-text animated />\n </ion-item>\n </ion-list>\n }\n\n @if (showAdd()) {\n <ion-item [disabled]=\"adding()\">\n <ion-input\n (keyup.enter)=\"addSpace()\"\n #addTeamInput\n [ngModel]=\"spaceName()\"\n (ngModelChange)=\"spaceName.set($event)\"\n (keyup.escape)=\"cancelAdd()\"\n placeholder=\"New team name\"\n />\n <ion-buttons slot=\"end\">\n <ion-button color=\"primary\" fill=\"solid\" (click)=\"addSpace()\">\n <ion-label>Create</ion-label>\n </ion-button>\n @if (!!spaces()?.length) {\n <ion-button (click)=\"cancelAdd()\" color=\"medium\" title=\"Cancel\">\n @if (adding()) {\n <ion-spinner />\n } @else {\n <ion-icon name=\"close-outline\" />\n }\n </ion-button>\n }\n </ion-buttons>\n </ion-item>\n }\n @if (showAdd()) {\n <ion-card-content>\n <p>Enter team name and click \"Create\" button to add a new team.</p>\n </ion-card-content>\n }\n</ion-card>\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { TitleCasePipe } from '@angular/common';
|
|
2
|
-
import { ChangeDetectionStrategy, Component, EventEmitter,
|
|
2
|
+
import { ChangeDetectionStrategy, Component, EventEmitter, Output, inject, input } from '@angular/core';
|
|
3
3
|
import { RouterLink } from '@angular/router';
|
|
4
|
-
import { IonBadge, IonButton, IonButtons, IonIcon, IonItem, IonLabel, IonSpinner, } from '@ionic/angular
|
|
4
|
+
import { IonBadge, IonButton, IonButtons, IonIcon, IonItem, IonLabel, IonSpinner, } from '@ionic/angular';
|
|
5
5
|
import { UserRequiredFieldsService } from '@sneat/auth-ui';
|
|
6
6
|
import { SpaceNavService, SpaceService } from '@sneat/space-services';
|
|
7
7
|
import { SneatUserService } from '@sneat/auth-core';
|
|
@@ -15,10 +15,17 @@ export class SpacesListComponent extends SneatBaseComponent {
|
|
|
15
15
|
this.spaceNavService = inject(SpaceNavService);
|
|
16
16
|
this.spaceService = inject(SpaceService);
|
|
17
17
|
this.userRequiredFieldsService = inject(UserRequiredFieldsService);
|
|
18
|
-
|
|
18
|
+
// Inputs
|
|
19
|
+
this.userID = input.required(/* @ts-ignore */
|
|
20
|
+
...(ngDevMode ? [{ debugName: "userID" }] : /* istanbul ignore next */ []));
|
|
21
|
+
this.spaces = input.required(/* @ts-ignore */
|
|
22
|
+
...(ngDevMode ? [{ debugName: "spaces" }] : /* istanbul ignore next */ []));
|
|
23
|
+
this.pathPrefix = input('/space', /* @ts-ignore */
|
|
24
|
+
...(ngDevMode ? [{ debugName: "pathPrefix" }] : /* istanbul ignore next */ []));
|
|
19
25
|
// Opt-in: render a per-row "leave" button. Off by default so existing
|
|
20
26
|
// consumers (spaces menu, for-space-type-card) are unchanged.
|
|
21
|
-
this.allowLeave = false
|
|
27
|
+
this.allowLeave = input(false, /* @ts-ignore */
|
|
28
|
+
...(ngDevMode ? [{ debugName: "allowLeave" }] : /* istanbul ignore next */ []));
|
|
22
29
|
// Outputs
|
|
23
30
|
this.beforeNavigateToSpace = new EventEmitter();
|
|
24
31
|
this.leftSpace = new EventEmitter();
|
|
@@ -99,15 +106,15 @@ export class SpacesListComponent extends SneatBaseComponent {
|
|
|
99
106
|
error: this.errorLogger.logErrorHandler(`Failed to leave space: ${title}`),
|
|
100
107
|
});
|
|
101
108
|
}
|
|
102
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
103
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "
|
|
109
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SpacesListComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
110
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: SpacesListComponent, isStandalone: true, selector: "sneat-spaces-list", inputs: { userID: { classPropertyName: "userID", publicName: "userID", isSignal: true, isRequired: true, transformFunction: null }, spaces: { classPropertyName: "spaces", publicName: "spaces", isSignal: true, isRequired: true, transformFunction: null }, pathPrefix: { classPropertyName: "pathPrefix", publicName: "pathPrefix", isSignal: true, isRequired: false, transformFunction: null }, allowLeave: { classPropertyName: "allowLeave", publicName: "allowLeave", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { beforeNavigateToSpace: "beforeNavigateToSpace", leftSpace: "leftSpace" }, providers: [
|
|
104
111
|
{
|
|
105
112
|
provide: ClassName,
|
|
106
113
|
useValue: 'SpacesListComponent',
|
|
107
114
|
},
|
|
108
|
-
], usesInheritance: true, ngImport: i0, template: "@for (space of spaces; track space.id; let last = $last) {\n <ion-item\n tappable=\"true\"\n [disabled]=\"!space.brief\"\n [lines]=\"last ? 'full' : 'inset'\"\n routerLink=\"{{ pathPrefix }}/{{ space.type }}/{{ space.id }}\"\n (click)=\"goSpace($event, space)\"\n >\n <ion-icon\n [name]=\"\n space.type === 'family'\n ? 'people-outline'\n : space.type === 'personal'\n ? 'person-circle-outline'\n : 'people-outline'\n \"\n slot=\"start\"\n />\n <ion-label class=\"ion-text-wrap\">\n {{ space.brief?.title || (space.type | titlecase) || space.id }}\n @if (!space.id && space.brief) {\n <ion-badge color=\"light\">(new)</ion-badge>\n }\n </ion-label>\n @if (!space.id && !space.brief) {\n <ion-spinner name=\"lines-small\" />\n }\n @if (allowLeave && space.id) {\n <ion-buttons slot=\"end\">\n <ion-button\n color=\"medium\"\n title=\"Leave\"\n (click)=\"leaveSpace(space, $event)\"\n >\n <ion-icon name=\"close-outline\" slot=\"icon-only\" />\n </ion-button>\n </ion-buttons>\n }\n </ion-item>\n}\n", dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: IonBadge, selector: "ion-badge", inputs: ["color", "mode"] }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "pipe", type: TitleCasePipe, name: "titlecase" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
115
|
+
], usesInheritance: true, ngImport: i0, template: "@for (space of spaces(); track space.id; let last = $last) {\n <ion-item\n tappable=\"true\"\n [disabled]=\"!space.brief\"\n [lines]=\"last ? 'full' : 'inset'\"\n routerLink=\"{{ pathPrefix() }}/{{ space.type }}/{{ space.id }}\"\n (click)=\"goSpace($event, space)\"\n >\n <ion-icon\n [name]=\"\n space.type === 'family'\n ? 'people-outline'\n : space.type === 'personal'\n ? 'person-circle-outline'\n : 'people-outline'\n \"\n slot=\"start\"\n />\n <ion-label class=\"ion-text-wrap\">\n {{ space.brief?.title || (space.type | titlecase) || space.id }}\n @if (!space.id && space.brief) {\n <ion-badge color=\"light\">(new)</ion-badge>\n }\n </ion-label>\n @if (!space.id && !space.brief) {\n <ion-spinner name=\"lines-small\" />\n }\n @if (allowLeave() && space.id) {\n <ion-buttons slot=\"end\">\n <ion-button\n color=\"medium\"\n title=\"Leave\"\n (click)=\"leaveSpace(space, $event)\"\n >\n <ion-icon name=\"close-outline\" slot=\"icon-only\" />\n </ion-button>\n </ion-buttons>\n }\n </ion-item>\n}\n", dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "component", type: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: IonBadge, selector: "ion-badge", inputs: ["color", "mode"] }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"], outputs: ["ionFocus", "ionBlur"] }, { kind: "pipe", type: TitleCasePipe, name: "titlecase" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
109
116
|
}
|
|
110
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
117
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SpacesListComponent, decorators: [{
|
|
111
118
|
type: Component,
|
|
112
119
|
args: [{ selector: 'sneat-spaces-list', imports: [
|
|
113
120
|
RouterLink,
|
|
@@ -124,18 +131,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImpor
|
|
|
124
131
|
provide: ClassName,
|
|
125
132
|
useValue: 'SpacesListComponent',
|
|
126
133
|
},
|
|
127
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "@for (space of spaces; track space.id; let last = $last) {\n <ion-item\n tappable=\"true\"\n [disabled]=\"!space.brief\"\n [lines]=\"last ? 'full' : 'inset'\"\n routerLink=\"{{ pathPrefix }}/{{ space.type }}/{{ space.id }}\"\n (click)=\"goSpace($event, space)\"\n >\n <ion-icon\n [name]=\"\n space.type === 'family'\n ? 'people-outline'\n : space.type === 'personal'\n ? 'person-circle-outline'\n : 'people-outline'\n \"\n slot=\"start\"\n />\n <ion-label class=\"ion-text-wrap\">\n {{ space.brief?.title || (space.type | titlecase) || space.id }}\n @if (!space.id && space.brief) {\n <ion-badge color=\"light\">(new)</ion-badge>\n }\n </ion-label>\n @if (!space.id && !space.brief) {\n <ion-spinner name=\"lines-small\" />\n }\n @if (allowLeave && space.id) {\n <ion-buttons slot=\"end\">\n <ion-button\n color=\"medium\"\n title=\"Leave\"\n (click)=\"leaveSpace(space, $event)\"\n >\n <ion-icon name=\"close-outline\" slot=\"icon-only\" />\n </ion-button>\n </ion-buttons>\n }\n </ion-item>\n}\n" }]
|
|
128
|
-
}], propDecorators: { userID: [{
|
|
129
|
-
type: Input,
|
|
130
|
-
args: [{ required: true }]
|
|
131
|
-
}], spaces: [{
|
|
132
|
-
type: Input,
|
|
133
|
-
args: [{ required: true }]
|
|
134
|
-
}], pathPrefix: [{
|
|
135
|
-
type: Input
|
|
136
|
-
}], allowLeave: [{
|
|
137
|
-
type: Input
|
|
138
|
-
}], beforeNavigateToSpace: [{
|
|
134
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "@for (space of spaces(); track space.id; let last = $last) {\n <ion-item\n tappable=\"true\"\n [disabled]=\"!space.brief\"\n [lines]=\"last ? 'full' : 'inset'\"\n routerLink=\"{{ pathPrefix() }}/{{ space.type }}/{{ space.id }}\"\n (click)=\"goSpace($event, space)\"\n >\n <ion-icon\n [name]=\"\n space.type === 'family'\n ? 'people-outline'\n : space.type === 'personal'\n ? 'person-circle-outline'\n : 'people-outline'\n \"\n slot=\"start\"\n />\n <ion-label class=\"ion-text-wrap\">\n {{ space.brief?.title || (space.type | titlecase) || space.id }}\n @if (!space.id && space.brief) {\n <ion-badge color=\"light\">(new)</ion-badge>\n }\n </ion-label>\n @if (!space.id && !space.brief) {\n <ion-spinner name=\"lines-small\" />\n }\n @if (allowLeave() && space.id) {\n <ion-buttons slot=\"end\">\n <ion-button\n color=\"medium\"\n title=\"Leave\"\n (click)=\"leaveSpace(space, $event)\"\n >\n <ion-icon name=\"close-outline\" slot=\"icon-only\" />\n </ion-button>\n </ion-buttons>\n }\n </ion-item>\n}\n" }]
|
|
135
|
+
}], propDecorators: { userID: [{ type: i0.Input, args: [{ isSignal: true, alias: "userID", required: true }] }], spaces: [{ type: i0.Input, args: [{ isSignal: true, alias: "spaces", required: true }] }], pathPrefix: [{ type: i0.Input, args: [{ isSignal: true, alias: "pathPrefix", required: false }] }], allowLeave: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowLeave", required: false }] }], beforeNavigateToSpace: [{
|
|
139
136
|
type: Output
|
|
140
137
|
}], leftSpace: [{
|
|
141
138
|
type: Output
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spaces-list.component.js","sourceRoot":"","sources":["../../../../../../../libs/space/components/src/lib/spaces-list/spaces-list.component.ts","../../../../../../../libs/space/components/src/lib/spaces-list/spaces-list.component.html"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EACL,uBAAuB,EACvB,SAAS,EACT,YAAY,EACZ,
|
|
1
|
+
{"version":3,"file":"spaces-list.component.js","sourceRoot":"","sources":["../../../../../../../libs/space/components/src/lib/spaces-list/spaces-list.component.ts","../../../../../../../libs/space/components/src/lib/spaces-list/spaces-list.component.html"],"names":[],"mappings":"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","sourcesContent":["import { TitleCasePipe } from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n Component,\n EventEmitter,\n Output,\n inject,\n input\n} from '@angular/core';\nimport { RouterLink } from '@angular/router';\nimport {\n IonBadge,\n IonButton,\n IonButtons,\n IonIcon,\n IonItem,\n IonLabel,\n IonSpinner,\n} from '@ionic/angular';\nimport { UserRequiredFieldsService } from '@sneat/auth-ui';\nimport { SpaceType } from '@sneat/core';\nimport { ICreateSpaceRequest, ISpaceContext } from '@sneat/space-models';\nimport { SpaceNavService, SpaceService } from '@sneat/space-services';\nimport { SneatUserService } from '@sneat/auth-core';\nimport { ClassName, SneatBaseComponent } from '@sneat/ui';\nimport { first } from 'rxjs';\n\n@Component({\n selector: 'sneat-spaces-list',\n templateUrl: 'spaces-list.component.html',\n imports: [\n RouterLink,\n TitleCasePipe,\n IonItem,\n IonIcon,\n IonLabel,\n IonBadge,\n IonSpinner,\n IonButtons,\n IonButton,\n ],\n providers: [\n {\n provide: ClassName,\n useValue: 'SpacesListComponent',\n },\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class SpacesListComponent extends SneatBaseComponent {\n public readonly userService = inject(SneatUserService);\n private readonly spaceNavService = inject(SpaceNavService);\n private readonly spaceService = inject(SpaceService);\n private readonly userRequiredFieldsService = inject(\n UserRequiredFieldsService,\n );\n\n // Inputs\n readonly userID = input.required<string | undefined>();\n readonly spaces = input.required<ISpaceContext[] | undefined>();\n readonly pathPrefix = input('/space');\n // Opt-in: render a per-row \"leave\" button. Off by default so existing\n // consumers (spaces menu, for-space-type-card) are unchanged.\n readonly allowLeave = input(false);\n\n // Outputs\n @Output() readonly beforeNavigateToSpace = new EventEmitter<ISpaceContext>();\n @Output() readonly leftSpace = new EventEmitter<ISpaceContext>();\n\n protected goSpace(event: Event, space: ISpaceContext): boolean {\n event.stopPropagation();\n event.preventDefault();\n if (space.id) {\n this.navigateToSpace(space);\n } else if (space.type) {\n this.createNewSpace(space.type);\n }\n return false;\n }\n\n private navigateToSpace(space: ISpaceContext): void {\n this.beforeNavigateToSpace.emit(space);\n this.spaceNavService\n .navigateToSpace(space)\n .catch(\n this.errorLogger.logErrorHandler(\n 'Failed to navigate to teams overview page from teams menu',\n ),\n );\n }\n\n protected createNewSpace(type: SpaceType): boolean {\n const request: ICreateSpaceRequest = {\n type,\n };\n\n this.userService.userState\n .pipe(first(), this.takeUntilDestroyed())\n .subscribe({\n next: (userState) => {\n if (userState.record) {\n this.createSpace(request);\n } else {\n this.userRequiredFieldsService\n .open()\n .then((modalResult) => {\n if (modalResult) {\n this.createSpace(request);\n }\n })\n .catch(\n this.errorLogger.logErrorHandler(\n 'Failed to open user required fields modal',\n ),\n );\n }\n },\n });\n // this.closeMenu();\n return false;\n }\n\n private createSpace(request: ICreateSpaceRequest): void {\n this.spaceService\n .createSpace(request)\n .pipe(this.takeUntilDestroyed())\n .subscribe({\n next: (value) => {\n this.navigateToSpace(value);\n },\n error: this.errorLogger.logErrorHandler(\n 'failed to create a new family team',\n ),\n });\n }\n\n // Only reachable when [allowLeave]=\"true\". Stops the row's navigate handler,\n // confirms, then leaves the space; the user record update removes the row.\n protected leaveSpace(space: ISpaceContext, event?: Event): void {\n if (event) {\n event.stopPropagation();\n event.preventDefault();\n }\n if (!space.id) {\n return;\n }\n const title = space.brief?.title || space.id;\n if (!confirm(`Are you sure you want to leave \"${title}\"?`)) {\n return;\n }\n this.spaceService\n .leaveSpace({ spaceID: space.id })\n .pipe(this.takeUntilDestroyed())\n .subscribe({\n next: () => this.leftSpace.emit(space),\n error: this.errorLogger.logErrorHandler(\n `Failed to leave space: ${title}`,\n ),\n });\n }\n}\n","@for (space of spaces(); track space.id; let last = $last) {\n <ion-item\n tappable=\"true\"\n [disabled]=\"!space.brief\"\n [lines]=\"last ? 'full' : 'inset'\"\n routerLink=\"{{ pathPrefix() }}/{{ space.type }}/{{ space.id }}\"\n (click)=\"goSpace($event, space)\"\n >\n <ion-icon\n [name]=\"\n space.type === 'family'\n ? 'people-outline'\n : space.type === 'personal'\n ? 'person-circle-outline'\n : 'people-outline'\n \"\n slot=\"start\"\n />\n <ion-label class=\"ion-text-wrap\">\n {{ space.brief?.title || (space.type | titlecase) || space.id }}\n @if (!space.id && space.brief) {\n <ion-badge color=\"light\">(new)</ion-badge>\n }\n </ion-label>\n @if (!space.id && !space.brief) {\n <ion-spinner name=\"lines-small\" />\n }\n @if (allowLeave() && space.id) {\n <ion-buttons slot=\"end\">\n <ion-button\n color=\"medium\"\n title=\"Leave\"\n (click)=\"leaveSpace(space, $event)\"\n >\n <ion-icon name=\"close-outline\" slot=\"icon-only\" />\n </ion-button>\n </ion-buttons>\n }\n </ion-item>\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ChangeDetectionStrategy, Component, computed, Input, signal, inject, } from '@angular/core';
|
|
2
|
-
import { IonIcon, IonItem, IonLabel, MenuController, NavController, } from '@ionic/angular
|
|
1
|
+
import { ChangeDetectionStrategy, Component, computed, Input, signal, inject, input } from '@angular/core';
|
|
2
|
+
import { IonIcon, IonItem, IonLabel, MenuController, NavController, } from '@ionic/angular';
|
|
3
3
|
import { SneatUserService } from '@sneat/auth-core';
|
|
4
4
|
import { UserRequiredFieldsService } from '@sneat/auth-ui';
|
|
5
5
|
import { spaceContextFromBrief, zipMapBriefsWithIDs, } from '@sneat/space-models';
|
|
@@ -12,10 +12,14 @@ export class SpacesMenuComponent extends SneatBaseComponent {
|
|
|
12
12
|
this.userService = inject(SneatUserService);
|
|
13
13
|
this.navController = inject(NavController);
|
|
14
14
|
this.menuController = inject(MenuController);
|
|
15
|
-
this.spacesLabel = 'Spaces'
|
|
16
|
-
|
|
17
|
-
this
|
|
18
|
-
|
|
15
|
+
this.spacesLabel = input('Spaces', /* @ts-ignore */
|
|
16
|
+
...(ngDevMode ? [{ debugName: "spacesLabel" }] : /* istanbul ignore next */ []));
|
|
17
|
+
this.pathPrefix = input('/space', /* @ts-ignore */
|
|
18
|
+
...(ngDevMode ? [{ debugName: "pathPrefix" }] : /* istanbul ignore next */ []));
|
|
19
|
+
this.$userSpaces = signal(undefined, /* @ts-ignore */
|
|
20
|
+
...(ngDevMode ? [{ debugName: "$userSpaces" }] : /* istanbul ignore next */ []));
|
|
21
|
+
this.$userID = signal('', /* @ts-ignore */
|
|
22
|
+
...(ngDevMode ? [{ debugName: "$userID" }] : /* istanbul ignore next */ []));
|
|
19
23
|
this.$spacesToShow = computed(() => {
|
|
20
24
|
const userSpaces = this.$userSpaces();
|
|
21
25
|
const spaces = (this.spaceType
|
|
@@ -55,7 +59,8 @@ export class SpacesMenuComponent extends SneatBaseComponent {
|
|
|
55
59
|
return a.brief?.title.localeCompare(b.brief?.title || '') || 0;
|
|
56
60
|
});
|
|
57
61
|
return spaces;
|
|
58
|
-
},
|
|
62
|
+
}, /* @ts-ignore */
|
|
63
|
+
...(ngDevMode ? [{ debugName: "$spacesToShow" }] : /* istanbul ignore next */ []));
|
|
59
64
|
this.onUserStateChanged = (user) => {
|
|
60
65
|
this.$userID.set(user.user?.uid || '');
|
|
61
66
|
if (!user?.record) {
|
|
@@ -66,7 +71,8 @@ export class SpacesMenuComponent extends SneatBaseComponent {
|
|
|
66
71
|
? zipMapBriefsWithIDs(user?.record?.spaces).map((t) => spaceContextFromBrief(t.id, t.brief))
|
|
67
72
|
: []);
|
|
68
73
|
};
|
|
69
|
-
this.$familySpace = signal(undefined,
|
|
74
|
+
this.$familySpace = signal(undefined, /* @ts-ignore */
|
|
75
|
+
...(ngDevMode ? [{ debugName: "$familySpace" }] : /* istanbul ignore next */ []));
|
|
70
76
|
const userService = this.userService;
|
|
71
77
|
userService.userState.pipe(this.takeUntilDestroyed()).subscribe({
|
|
72
78
|
next: this.onUserStateChanged,
|
|
@@ -77,16 +83,16 @@ export class SpacesMenuComponent extends SneatBaseComponent {
|
|
|
77
83
|
.close()
|
|
78
84
|
.catch(this.errorLogger.logErrorHandler('Failed to close teams menu'));
|
|
79
85
|
}
|
|
80
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
81
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "
|
|
86
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SpacesMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
87
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: SpacesMenuComponent, isStandalone: true, selector: "sneat-spaces-menu", inputs: { spacesLabel: { classPropertyName: "spacesLabel", publicName: "spacesLabel", isSignal: true, isRequired: false, transformFunction: null }, pathPrefix: { classPropertyName: "pathPrefix", publicName: "pathPrefix", isSignal: true, isRequired: false, transformFunction: null }, spaceType: { classPropertyName: "spaceType", publicName: "spaceType", isSignal: false, isRequired: false, transformFunction: null } }, providers: [
|
|
82
88
|
{
|
|
83
89
|
provide: ClassName,
|
|
84
90
|
useValue: 'SpacesMenuComponent',
|
|
85
91
|
},
|
|
86
92
|
UserRequiredFieldsService,
|
|
87
|
-
], usesInheritance: true, ngImport: i0, template: "<ion-item>\n <ion-label color=\"medium\" style=\"font-weight: bold\">{{\n spacesLabel || \"Spaces\"\n }}</ion-label>\n</ion-item>\n\n@if ($spacesToShow().length) {\n <sneat-spaces-list\n [spaces]=\"$spacesToShow()\"\n [userID]=\"$userID()\"\n (beforeNavigateToSpace)=\"closeMenu()\"\n />\n} @else if (spaceType) {\n <ion-item>\n <ion-icon name=\"people-outline\" slot=\"start\" />\n <ion-label color=\"medium\">\n @if (spaceType === \"company\") {\n No companies\n } @else {\n No {{ spaceType }}\n }\n </ion-label>\n </ion-item>\n}\n", dependencies: [{ kind: "component", type: SpacesListComponent, selector: "sneat-spaces-list", inputs: ["userID", "spaces", "pathPrefix", "allowLeave"], outputs: ["beforeNavigateToSpace", "leftSpace"] }, { kind: "component", type: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
93
|
+
], usesInheritance: true, ngImport: i0, template: "<ion-item>\n <ion-label color=\"medium\" style=\"font-weight: bold\">{{\n spacesLabel() || \"Spaces\"\n }}</ion-label>\n</ion-item>\n\n@if ($spacesToShow().length) {\n <sneat-spaces-list\n [spaces]=\"$spacesToShow()\"\n [userID]=\"$userID()\"\n (beforeNavigateToSpace)=\"closeMenu()\"\n />\n} @else if (spaceType) {\n <ion-item>\n <ion-icon name=\"people-outline\" slot=\"start\" />\n <ion-label color=\"medium\">\n @if (spaceType === \"company\") {\n No companies\n } @else {\n No {{ spaceType }}\n }\n </ion-label>\n </ion-item>\n}\n", dependencies: [{ kind: "component", type: SpacesListComponent, selector: "sneat-spaces-list", inputs: ["userID", "spaces", "pathPrefix", "allowLeave"], outputs: ["beforeNavigateToSpace", "leftSpace"] }, { kind: "component", type: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
88
94
|
}
|
|
89
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
95
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SpacesMenuComponent, decorators: [{
|
|
90
96
|
type: Component,
|
|
91
97
|
args: [{ selector: 'sneat-spaces-menu', imports: [SpacesListComponent, IonItem, IonLabel, IonIcon], changeDetection: ChangeDetectionStrategy.OnPush, providers: [
|
|
92
98
|
{
|
|
@@ -94,12 +100,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImpor
|
|
|
94
100
|
useValue: 'SpacesMenuComponent',
|
|
95
101
|
},
|
|
96
102
|
UserRequiredFieldsService,
|
|
97
|
-
], template: "<ion-item>\n <ion-label color=\"medium\" style=\"font-weight: bold\">{{\n spacesLabel || \"Spaces\"\n }}</ion-label>\n</ion-item>\n\n@if ($spacesToShow().length) {\n <sneat-spaces-list\n [spaces]=\"$spacesToShow()\"\n [userID]=\"$userID()\"\n (beforeNavigateToSpace)=\"closeMenu()\"\n />\n} @else if (spaceType) {\n <ion-item>\n <ion-icon name=\"people-outline\" slot=\"start\" />\n <ion-label color=\"medium\">\n @if (spaceType === \"company\") {\n No companies\n } @else {\n No {{ spaceType }}\n }\n </ion-label>\n </ion-item>\n}\n" }]
|
|
98
|
-
}], ctorParameters: () => [], propDecorators: { spacesLabel: [{
|
|
99
|
-
type: Input
|
|
100
|
-
}], pathPrefix: [{
|
|
101
|
-
type: Input
|
|
102
|
-
}], spaceType: [{
|
|
103
|
+
], template: "<ion-item>\n <ion-label color=\"medium\" style=\"font-weight: bold\">{{\n spacesLabel() || \"Spaces\"\n }}</ion-label>\n</ion-item>\n\n@if ($spacesToShow().length) {\n <sneat-spaces-list\n [spaces]=\"$spacesToShow()\"\n [userID]=\"$userID()\"\n (beforeNavigateToSpace)=\"closeMenu()\"\n />\n} @else if (spaceType) {\n <ion-item>\n <ion-icon name=\"people-outline\" slot=\"start\" />\n <ion-label color=\"medium\">\n @if (spaceType === \"company\") {\n No companies\n } @else {\n No {{ spaceType }}\n }\n </ion-label>\n </ion-item>\n}\n" }]
|
|
104
|
+
}], ctorParameters: () => [], propDecorators: { spacesLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "spacesLabel", required: false }] }], pathPrefix: [{ type: i0.Input, args: [{ isSignal: true, alias: "pathPrefix", required: false }] }], spaceType: [{
|
|
103
105
|
type: Input
|
|
104
106
|
}] } });
|
|
105
107
|
//# sourceMappingURL=spaces-menu.component.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spaces-menu.component.js","sourceRoot":"","sources":["../../../../../../../libs/space/components/src/lib/spaces-menu/spaces-menu.component.ts","../../../../../../../libs/space/components/src/lib/spaces-menu/spaces-menu.component.html"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EACvB,SAAS,EACT,QAAQ,EACR,KAAK,EACL,MAAM,EACN,MAAM,
|
|
1
|
+
{"version":3,"file":"spaces-menu.component.js","sourceRoot":"","sources":["../../../../../../../libs/space/components/src/lib/spaces-menu/spaces-menu.component.ts","../../../../../../../libs/space/components/src/lib/spaces-menu/spaces-menu.component.html"],"names":[],"mappings":"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","sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n computed,\n Input,\n signal,\n inject,\n input\n} from '@angular/core';\nimport {\n IonIcon,\n IonItem,\n IonLabel,\n MenuController,\n NavController,\n} from '@ionic/angular';\nimport { ISneatUserState, SneatUserService } from '@sneat/auth-core';\nimport { UserRequiredFieldsService } from '@sneat/auth-ui';\nimport { SpaceType } from '@sneat/core';\nimport {\n ISpaceContext,\n spaceContextFromBrief,\n zipMapBriefsWithIDs,\n} from '@sneat/space-models';\nimport { ClassName, SneatBaseComponent } from '@sneat/ui';\nimport { SpacesListComponent } from '../spaces-list';\n\n@Component({\n selector: 'sneat-spaces-menu',\n templateUrl: './spaces-menu.component.html',\n imports: [SpacesListComponent, IonItem, IonLabel, IonIcon],\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [\n {\n provide: ClassName,\n useValue: 'SpacesMenuComponent',\n },\n UserRequiredFieldsService,\n ],\n})\nexport class SpacesMenuComponent extends SneatBaseComponent {\n readonly userService = inject(SneatUserService);\n private readonly navController = inject(NavController);\n private readonly menuController = inject(MenuController);\n\n readonly spacesLabel = input('Spaces');\n readonly pathPrefix = input('/space');\n\n // TODO: Skipped for migration because:\n // This input is used in a control flow expression (e.g. `@if` or `*ngIf`)\n // and migrating would break narrowing currently.\n @Input() spaceType?: SpaceType;\n\n protected readonly $userSpaces = signal<ISpaceContext[] | undefined>(\n undefined,\n );\n\n protected readonly $userID = signal<string>('');\n\n protected readonly $spacesToShow = computed<ISpaceContext[]>(() => {\n const userSpaces = this.$userSpaces();\n const spaces =\n (this.spaceType\n ? userSpaces?.filter((t) => t.type === this.spaceType)\n : userSpaces) || [];\n if (!this.spaceType) {\n const addPseudoSpace = (type: 'family' | 'personal'): void => {\n if (!spaces.some((t) => t.type === type)) {\n spaces.push({\n id: '',\n type,\n brief: userSpaces\n ? {\n type,\n title: '',\n }\n : undefined, // define brief indicates we have user record loaded\n });\n }\n };\n addPseudoSpace('family');\n addPseudoSpace('personal');\n }\n const sortOrder: Record<string, number> = {\n family: 1,\n personal: 2,\n };\n spaces.sort((a: ISpaceContext, b: ISpaceContext) => {\n // Determine the sorting priority (lower values mean higher priority)\n const priorityA = (a.type && sortOrder[a.type]) ?? 3; // Default to 3 for all other types\n const priorityB = (b.type && sortOrder[b.type]) ?? 3;\n\n // Compare priority first\n if (priorityA !== priorityB) {\n return priorityA - priorityB;\n }\n\n // If same priority, sort by title alphabetically\n return a.brief?.title.localeCompare(b.brief?.title || '') || 0;\n });\n return spaces;\n });\n\n private onUserStateChanged = (user: ISneatUserState): void => {\n this.$userID.set(user.user?.uid || '');\n if (!user?.record) {\n this.$userSpaces.set(undefined);\n return;\n }\n\n this.$userSpaces.set(\n user?.record?.spaces\n ? zipMapBriefsWithIDs(user?.record?.spaces).map((t) =>\n spaceContextFromBrief(t.id, t.brief),\n )\n : [],\n );\n };\n\n protected readonly $familySpace = signal<ISpaceContext | undefined>(\n undefined,\n );\n\n constructor() {\n super();\n const userService = this.userService;\n\n userService.userState.pipe(this.takeUntilDestroyed()).subscribe({\n next: this.onUserStateChanged,\n });\n }\n\n protected closeMenu(): void {\n this.menuController\n .close()\n .catch(this.errorLogger.logErrorHandler('Failed to close teams menu'));\n }\n}\n","<ion-item>\n <ion-label color=\"medium\" style=\"font-weight: bold\">{{\n spacesLabel() || \"Spaces\"\n }}</ion-label>\n</ion-item>\n\n@if ($spacesToShow().length) {\n <sneat-spaces-list\n [spaces]=\"$spacesToShow()\"\n [userID]=\"$userID()\"\n (beforeNavigateToSpace)=\"closeMenu()\"\n />\n} @else if (spaceType) {\n <ion-item>\n <ion-icon name=\"people-outline\" slot=\"start\" />\n <ion-label color=\"medium\">\n @if (spaceType === \"company\") {\n No companies\n } @else {\n No {{ spaceType }}\n }\n </ion-label>\n </ion-item>\n}\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { IAvatar } from '@sneat/auth-models';
|
|
2
2
|
import * as i0 from "@angular/core";
|
|
3
3
|
export declare class AvatarComponent {
|
|
4
|
-
avatar
|
|
4
|
+
readonly avatar: import("@angular/core").InputSignal<IAvatar | undefined>;
|
|
5
5
|
static ɵfac: i0.ɵɵFactoryDeclaration<AvatarComponent, never>;
|
|
6
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<AvatarComponent, "sneat-avatar", never, { "avatar": { "alias": "avatar"; "required": true; }; }, {}, never, never, true, never>;
|
|
6
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<AvatarComponent, "sneat-avatar", never, { "avatar": { "alias": "avatar"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
7
7
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { OnInit } from '@angular/core';
|
|
2
2
|
import { ActivatedRoute, ParamMap } from '@angular/router';
|
|
3
|
-
import { NavController } from '@ionic/angular
|
|
3
|
+
import { NavController } from '@ionic/angular';
|
|
4
4
|
import { ILogger, ISpaceRef, SpaceType } from '@sneat/core';
|
|
5
5
|
import { ISpaceBrief, ISpaceDbo } from '@sneat/dto';
|
|
6
6
|
import { ISpaceContext } from '@sneat/space-models';
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { ISpaceContext } from '@sneat/space-models';
|
|
2
2
|
import * as i0 from "@angular/core";
|
|
3
3
|
export declare class SpacePageTitleComponent {
|
|
4
|
-
icon
|
|
5
|
-
generalTitle
|
|
4
|
+
readonly icon: import("@angular/core").InputSignal<string | undefined>;
|
|
5
|
+
readonly generalTitle: import("@angular/core").InputSignal<string | undefined>;
|
|
6
6
|
space?: ISpaceContext;
|
|
7
|
-
titlesBySpaceType
|
|
7
|
+
readonly titlesBySpaceType: import("@angular/core").InputSignal<Record<string, string> | undefined>;
|
|
8
8
|
get typeTitle(): string;
|
|
9
9
|
static ɵfac: i0.ɵɵFactoryDeclaration<SpacePageTitleComponent, never>;
|
|
10
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<SpacePageTitleComponent, "sneat-space-page-title", never, { "icon": { "alias": "icon"; "required": false; }; "generalTitle": { "alias": "generalTitle"; "required": false; }; "space": { "alias": "space"; "required": true; }; "titlesBySpaceType": { "alias": "titlesBySpaceType"; "required": false; }; }, {}, never, never, true, never>;
|
|
10
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<SpacePageTitleComponent, "sneat-space-page-title", never, { "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "generalTitle": { "alias": "generalTitle"; "required": false; "isSignal": true; }; "space": { "alias": "space"; "required": true; }; "titlesBySpaceType": { "alias": "titlesBySpaceType"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
11
11
|
}
|