@sneat/space-components 0.26.0 → 0.26.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/esm2022/lib/avatar/avatar.component.js +10 -10
  2. package/esm2022/lib/avatar/avatar.component.js.map +1 -1
  3. package/esm2022/lib/space-base-component.directive.js +20 -12
  4. package/esm2022/lib/space-base-component.directive.js.map +1 -1
  5. package/esm2022/lib/space-component-base-params.service.js +3 -3
  6. package/esm2022/lib/space-item-page-base.component.js +4 -2
  7. package/esm2022/lib/space-item-page-base.component.js.map +1 -1
  8. package/esm2022/lib/space-menu/space-menu.component.js +12 -9
  9. package/esm2022/lib/space-menu/space-menu.component.js.map +1 -1
  10. package/esm2022/lib/space-page-base-component.service.js +3 -3
  11. package/esm2022/lib/space-page-title/space-page-title.component.js +19 -16
  12. package/esm2022/lib/space-page-title/space-page-title.component.js.map +1 -1
  13. package/esm2022/lib/spaces-card/spaces-card.component.js +40 -16
  14. package/esm2022/lib/spaces-card/spaces-card.component.js.map +1 -1
  15. package/esm2022/lib/spaces-list/spaces-list.component.js +17 -20
  16. package/esm2022/lib/spaces-list/spaces-list.component.js.map +1 -1
  17. package/esm2022/lib/spaces-menu/spaces-menu.component.js +20 -18
  18. package/esm2022/lib/spaces-menu/spaces-menu.component.js.map +1 -1
  19. package/lib/avatar/avatar.component.d.ts +2 -2
  20. package/lib/space-base-component.directive.d.ts +1 -1
  21. package/lib/space-page-title/space-page-title.component.d.ts +4 -4
  22. package/lib/spaces-card/spaces-card.component.d.ts +17 -1
  23. package/lib/spaces-list/spaces-list.component.d.ts +5 -5
  24. package/lib/spaces-menu/spaces-menu.component.d.ts +3 -3
  25. package/package.json +6 -5
  26. 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/standalone';
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
- return this.space?.type && this.titlesBySpaceType
8
- ? this.titlesBySpaceType[this.space.type]
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: "21.2.0", ngImport: i0, type: SpacePageTitleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
12
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: SpacePageTitleComponent, isStandalone: true, selector: "sneat-space-page-title", inputs: { icon: "icon", generalTitle: "generalTitle", space: "space", titlesBySpaceType: "titlesBySpaceType" }, 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 }} &#64;\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 }); }
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() }} &#64;\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: "21.2.0", ngImport: i0, type: SpacePageTitleComponent, decorators: [{
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 }} &#64;\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() }} &#64;\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;AAC1E,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;;AASrD,MAAM,OAAO,uBAAuB;IAMlC,IAAW,SAAS;QAClB,OAAO,IAAI,CAAC,KAAK,EAAE,IAAI,IAAI,IAAI,CAAC,iBAAiB;YAC/C,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;YACzC,CAAC,CAAC,EAAE,CAAC;IACT,CAAC;8GAVU,uBAAuB;kGAAvB,uBAAuB,kMCXpC,sfAiBA,4CDX2B,QAAQ,4EAAvB,aAAa;;2FAKZ,uBAAuB;kBANnC,SAAS;8BACC,CAAC,aAAa,EAAE,QAAQ,CAAC,mBACjB,uBAAuB,CAAC,MAAM,YACrC,wBAAwB;;sBAIjC,KAAK;;sBACL,KAAK;;sBACL,KAAK;uBAAC,EAAE,QAAQ,EAAE,IAAI,EAAE;;sBACxB,KAAK","sourcesContent":["import { TitleCasePipe } from '@angular/common';\nimport { ChangeDetectionStrategy, Component, Input } from '@angular/core';\nimport { IonTitle } from '@ionic/angular/standalone';\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 @Input() icon?: string;\n @Input() generalTitle?: string;\n @Input({ required: true }) space?: ISpaceContext;\n @Input() titlesBySpaceType?: Record<string, string>;\n\n public get typeTitle(): string {\n return this.space?.type && this.titlesBySpaceType\n ? this.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 }} &#64;\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
+ {"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() }} &#64;\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
- import { ChangeDetectionStrategy, Component, computed, effect, inject, signal, viewChild, } from '@angular/core';
1
+ import { ChangeDetectionStrategy, Component, computed, effect, inject, Input, 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/standalone';
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';
@@ -20,13 +20,22 @@ export class SpacesCardComponent {
20
20
  return this.userService.currentUserID ?? '';
21
21
  }
22
22
  constructor() {
23
+ /** Card heading — a product names its spaces ("Clubs", "Schools"). */
24
+ this.title = 'Spaces';
25
+ /**
26
+ * Whether the card offers its quick-add form. A product whose spaces are
27
+ * created by a registration flow (decision 0006) passes false and offers
28
+ * its own "Register ..." call to action instead.
29
+ */
30
+ this.canAdd = true;
23
31
  this.errorLogger = inject(ErrorLogger);
24
32
  this.navService = inject(SpaceNavService);
25
33
  this.userService = inject(SneatUserService);
26
34
  this.spaceService = inject(SpaceService);
27
35
  this.analyticsService = inject(AnalyticsService);
28
36
  this.toastController = inject(ToastController);
29
- this.addSpaceInput = viewChild('addTeamInput', ...(ngDevMode ? [{ debugName: "addSpaceInput" }] : []));
37
+ this.addSpaceInput = viewChild('addTeamInput', /* @ts-ignore */
38
+ ...(ngDevMode ? [{ debugName: "addSpaceInput" }] : /* istanbul ignore next */ []));
30
39
  this.userState = toSignal(this.userService.userState);
31
40
  // undefined => user record not loaded yet (render the loading row).
32
41
  this.spaces = computed(() => {
@@ -36,8 +45,10 @@ export class SpacesCardComponent {
36
45
  }
37
46
  return Object.entries(record.spaces ?? {})
38
47
  .map(([id, brief]) => ({ id, brief }))
48
+ .filter(({ brief }) => !this.spaceType || brief.type === this.spaceType)
39
49
  .sort((a, b) => (a.brief.title > b.brief.title ? 1 : -1));
40
- }, ...(ngDevMode ? [{ debugName: "spaces" }] : []));
50
+ }, /* @ts-ignore */
51
+ ...(ngDevMode ? [{ debugName: "spaces" }] : /* istanbul ignore next */ []));
41
52
  // Adapts the user's spaces to the shape SpacesListComponent renders, so the
42
53
  // card reuses that component (icon + title decode + navigation) instead of
43
54
  // duplicating the row markup.
@@ -45,15 +56,22 @@ export class SpacesCardComponent {
45
56
  id,
46
57
  type: brief.type,
47
58
  brief: { title: brief.title, type: brief.type, roles: brief.roles },
48
- })), ...(ngDevMode ? [{ debugName: "spaceContexts" }] : []));
49
- this.loadingState = computed(() => this.userState()?.status === 'authenticated' ? 'Loading' : 'Authenticating', ...(ngDevMode ? [{ debugName: "loadingState" }] : []));
50
- this.showAdd = signal(false, ...(ngDevMode ? [{ debugName: "showAdd" }] : []));
51
- this.spaceName = signal('', ...(ngDevMode ? [{ debugName: "spaceName" }] : []));
52
- this.adding = signal(false, ...(ngDevMode ? [{ debugName: "adding" }] : []));
53
- // Auto-open the "add space" form once we know the user has no spaces.
59
+ })), /* @ts-ignore */
60
+ ...(ngDevMode ? [{ debugName: "spaceContexts" }] : /* istanbul ignore next */ []));
61
+ this.loadingState = computed(() => this.userState()?.status === 'authenticated' ? 'Loading' : 'Authenticating', /* @ts-ignore */
62
+ ...(ngDevMode ? [{ debugName: "loadingState" }] : /* istanbul ignore next */ []));
63
+ this.showAdd = signal(false, /* @ts-ignore */
64
+ ...(ngDevMode ? [{ debugName: "showAdd" }] : /* istanbul ignore next */ []));
65
+ this.spaceName = signal('', /* @ts-ignore */
66
+ ...(ngDevMode ? [{ debugName: "spaceName" }] : /* istanbul ignore next */ []));
67
+ this.adding = signal(false, /* @ts-ignore */
68
+ ...(ngDevMode ? [{ debugName: "adding" }] : /* istanbul ignore next */ []));
69
+ // Auto-open the "add space" form once we know the user has no spaces —
70
+ // unless adding is disabled (a registration-flow product supplies its own
71
+ // call to action).
54
72
  effect(() => {
55
73
  const spaces = this.spaces();
56
- if (spaces && spaces.length === 0) {
74
+ if (spaces && spaces.length === 0 && this.canAdd) {
57
75
  this.startAddingSpace();
58
76
  }
59
77
  });
@@ -123,10 +141,10 @@ export class SpacesCardComponent {
123
141
  cancelAdd() {
124
142
  this.showAdd.set(false);
125
143
  }
126
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: SpacesCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
127
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", 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"] }, { 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 }); }
144
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SpacesCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
145
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: SpacesCardComponent, isStandalone: true, selector: "sneat-spaces-card", inputs: { title: "title", spaceType: "spaceType", canAdd: "canAdd" }, 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\">{{ title }}</ion-card-title>\n </ion-label>\n <ion-buttons slot=\"end\" (click)=\"startAddingSpace()\">\n @if (canAdd && !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
146
  }
129
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: SpacesCardComponent, decorators: [{
147
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SpacesCardComponent, decorators: [{
130
148
  type: Component,
131
149
  args: [{ selector: 'sneat-spaces-card', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
132
150
  FormsModule,
@@ -143,6 +161,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImpor
143
161
  IonSkeletonText,
144
162
  IonCardContent,
145
163
  SpacesListComponent,
146
- ], 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" }]
147
- }], ctorParameters: () => [], propDecorators: { addSpaceInput: [{ type: i0.ViewChild, args: ['addTeamInput', { isSignal: true }] }] } });
164
+ ], template: "<ion-card>\n <ion-item>\n <ion-label>\n <ion-card-title color=\"medium\">{{ title }}</ion-card-title>\n </ion-label>\n <ion-buttons slot=\"end\" (click)=\"startAddingSpace()\">\n @if (canAdd && !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" }]
165
+ }], ctorParameters: () => [], propDecorators: { title: [{
166
+ type: Input
167
+ }], spaceType: [{
168
+ type: Input
169
+ }], canAdd: [{
170
+ type: Input
171
+ }], addSpaceInput: [{ type: i0.ViewChild, args: ['addTeamInput', { isSignal: true }] }] } });
148
172
  //# sourceMappingURL=spaces-card.component.js.map
@@ -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 Input,\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 { SpaceType } from '@sneat/core';\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 /** Card heading — a product names its spaces (\"Clubs\", \"Schools\"). */\n @Input() title = 'Spaces';\n\n /**\n * When set, only spaces of this type are listed. A Sneat account holds\n * spaces from every product (family, work, clubs...), and a product home\n * that lists them all reads as a generic space browser rather than the\n * product — sneat.club wants exactly the `club` spaces.\n */\n @Input() spaceType?: SpaceType;\n\n /**\n * Whether the card offers its quick-add form. A product whose spaces are\n * created by a registration flow (decision 0006) passes false and offers\n * its own \"Register ...\" call to action instead.\n */\n @Input() canAdd = true;\n\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 .filter(({ brief }) => !this.spaceType || brief.type === this.spaceType)\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 // unless adding is disabled (a registration-flow product supplies its own\n // call to action).\n effect(() => {\n const spaces = this.spaces();\n if (spaces && spaces.length === 0 && this.canAdd) {\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\">{{ title }}</ion-card-title>\n </ion-label>\n <ion-buttons slot=\"end\" (click)=\"startAddingSpace()\">\n @if (canAdd && !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, Input, Output, inject, } from '@angular/core';
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/standalone';
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
- this.pathPrefix = '/space';
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: "21.2.0", ngImport: i0, type: SpacesListComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
103
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: SpacesListComponent, isStandalone: true, selector: "sneat-spaces-list", inputs: { userID: "userID", spaces: "spaces", pathPrefix: "pathPrefix", allowLeave: "allowLeave" }, outputs: { beforeNavigateToSpace: "beforeNavigateToSpace", leftSpace: "leftSpace" }, providers: [
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: "21.2.0", ngImport: i0, type: SpacesListComponent, decorators: [{
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":"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","sourcesContent":["import { TitleCasePipe } from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n Component,\n EventEmitter,\n Input,\n Output,\n inject,\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/standalone';\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 @Input({ required: true }) userID?: string;\n @Input({ required: true }) spaces?: ISpaceContext[];\n @Input() pathPrefix = '/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 @Input() allowLeave = 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
+ {"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/standalone';
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
- this.pathPrefix = '/space';
17
- this.$userSpaces = signal(undefined, ...(ngDevMode ? [{ debugName: "$userSpaces" }] : []));
18
- this.$userID = signal('', ...(ngDevMode ? [{ debugName: "$userID" }] : []));
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
- }, ...(ngDevMode ? [{ debugName: "$spacesToShow" }] : []));
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, ...(ngDevMode ? [{ debugName: "$familySpace" }] : []));
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: "21.2.0", ngImport: i0, type: SpacesMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
81
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: SpacesMenuComponent, isStandalone: true, selector: "sneat-spaces-menu", inputs: { spacesLabel: "spacesLabel", pathPrefix: "pathPrefix", spaceType: "spaceType" }, providers: [
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: "21.2.0", ngImport: i0, type: SpacesMenuComponent, decorators: [{
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":"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","sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n computed,\n Input,\n signal,\n inject,\n} from '@angular/core';\nimport {\n IonIcon,\n IonItem,\n IonLabel,\n MenuController,\n NavController,\n} from '@ionic/angular/standalone';\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 @Input() spacesLabel = 'Spaces';\n @Input() pathPrefix = '/space';\n\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
+ {"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"]}