@sneat/space-components 0.27.6 → 0.27.7
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/index.js +1 -0
- package/esm2022/lib/index.js.map +1 -1
- package/esm2022/lib/space-menu/space-menu.component.js +6 -38
- package/esm2022/lib/space-menu/space-menu.component.js.map +1 -1
- package/esm2022/lib/space-selector/index.js +2 -0
- package/esm2022/lib/space-selector/index.js.map +1 -0
- package/esm2022/lib/space-selector/space-selector.component.js +133 -0
- package/esm2022/lib/space-selector/space-selector.component.js.map +1 -0
- package/esm2022/lib/spaces-card/spaces-card.component.js +2 -2
- package/esm2022/lib/spaces-card/spaces-card.component.js.map +1 -1
- package/esm2022/lib/spaces-list/spaces-list.component.js +2 -2
- package/esm2022/lib/spaces-list/spaces-list.component.js.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/space-menu/space-menu.component.d.ts +0 -5
- package/lib/space-selector/index.d.ts +1 -0
- package/lib/space-selector/space-selector.component.d.ts +28 -0
- package/package.json +1 -1
- package/tsconfig.lib.prod.tsbuildinfo +1 -1
package/esm2022/lib/index.js
CHANGED
package/esm2022/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../libs/space/components/src/lib/index.ts"],"names":[],"mappings":"AAAA,cAAc,2BAA2B,CAAC;AAC1C,cAAc,cAAc,CAAC;AAC7B,cAAc,yBAAyB,CAAC;AACxC,cAAc,eAAe,CAAC;AAC9B,cAAc,+CAA+C,CAAC;AAC9D,cAAc,qCAAqC,CAAC;AACpD,cAAc,uCAAuC,CAAC;AACtD,cAAc,kCAAkC,CAAC;AACjD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,+BAA+B,CAAC;AAC9C,cAAc,kCAAkC,CAAC","sourcesContent":["export * from './avatar/avatar.component';\nexport * from './space-menu';\nexport * from './space-extension-links';\nexport * from './spaces-menu';\nexport * from './space-page-title/space-page-title.component';\nexport * from './space-page-base-component.service';\nexport * from './space-component-base-params.service';\nexport * from './space-item-page-base.component';\nexport * from './space-items-base.component';\nexport * from './spaces-list';\nexport * from './spaces-card';\nexport * from './space-module-base.component';\nexport * from './space-base-component.directive';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../libs/space/components/src/lib/index.ts"],"names":[],"mappings":"AAAA,cAAc,2BAA2B,CAAC;AAC1C,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,yBAAyB,CAAC;AACxC,cAAc,eAAe,CAAC;AAC9B,cAAc,+CAA+C,CAAC;AAC9D,cAAc,qCAAqC,CAAC;AACpD,cAAc,uCAAuC,CAAC;AACtD,cAAc,kCAAkC,CAAC;AACjD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,+BAA+B,CAAC;AAC9C,cAAc,kCAAkC,CAAC","sourcesContent":["export * from './avatar/avatar.component';\nexport * from './space-menu';\nexport * from './space-selector';\nexport * from './space-extension-links';\nexport * from './spaces-menu';\nexport * from './space-page-title/space-page-title.component';\nexport * from './space-page-base-component.service';\nexport * from './space-component-base-params.service';\nexport * from './space-item-page-base.component';\nexport * from './space-items-base.component';\nexport * from './spaces-list';\nexport * from './spaces-card';\nexport * from './space-module-base.component';\nexport * from './space-base-component.directive';\n"]}
|
|
@@ -1,40 +1,25 @@
|
|
|
1
|
-
import { TitleCasePipe } from '@angular/common';
|
|
2
1
|
import { ChangeDetectionStrategy, Component, computed, inject, signal, } from '@angular/core';
|
|
3
2
|
import { ActivatedRoute, NavigationEnd, Router, RouterLink, } from '@angular/router';
|
|
4
|
-
import { IonIcon, IonItem, IonItemDivider, IonLabel, IonList,
|
|
3
|
+
import { IonIcon, IonItem, IonItemDivider, IonLabel, IonList, MenuController, } from '@ionic/angular';
|
|
5
4
|
import { AuthMenuItemComponent } from '@sneat/auth-ui';
|
|
6
|
-
import { zipMapBriefsWithIDs } from '@sneat/space-models';
|
|
7
5
|
import { SpaceServiceModule } from '@sneat/space-services';
|
|
8
6
|
import { filter } from 'rxjs';
|
|
9
|
-
import { takeUntil } from 'rxjs/operators';
|
|
10
7
|
import { SpaceBaseComponent } from '../space-base-component.directive';
|
|
11
8
|
import { SpaceComponentBaseParams } from '../space-component-base-params.service';
|
|
12
9
|
import { ClassName } from '@sneat/ui';
|
|
13
10
|
import { SpaceExtensionLinksComponent } from '../space-extension-links';
|
|
11
|
+
import { SpaceSelectorComponent } from '../space-selector';
|
|
14
12
|
import * as i0 from "@angular/core";
|
|
15
13
|
export class SpaceMenuComponent extends SpaceBaseComponent {
|
|
16
14
|
constructor() {
|
|
17
15
|
const router = inject(Router);
|
|
18
16
|
super();
|
|
19
|
-
this.$spaces = signal(undefined, /* @ts-ignore */
|
|
20
|
-
...(ngDevMode ? [{ debugName: "$spaces" }] : /* istanbul ignore next */ []));
|
|
21
17
|
this.$disabled = computed(() => !this.$spaceID() || this.$spaceNotFound(), /* @ts-ignore */
|
|
22
18
|
...(ngDevMode ? [{ debugName: "$disabled" }] : /* istanbul ignore next */ []));
|
|
23
19
|
this.$currentPage = signal('', /* @ts-ignore */
|
|
24
20
|
...(ngDevMode ? [{ debugName: "$currentPage" }] : /* istanbul ignore next */ []));
|
|
25
21
|
this.activatedRoute = inject(ActivatedRoute);
|
|
26
22
|
this.menuCtrl = inject(MenuController);
|
|
27
|
-
this.onUserStateChanged = (userState) => {
|
|
28
|
-
this.$spaces.set(userState?.record
|
|
29
|
-
? zipMapBriefsWithIDs(userState.record.spaces) || []
|
|
30
|
-
: undefined);
|
|
31
|
-
};
|
|
32
|
-
this.spaceParams.userService.userState
|
|
33
|
-
.pipe(takeUntil(this.destroyed$))
|
|
34
|
-
.subscribe({
|
|
35
|
-
next: this.onUserStateChanged,
|
|
36
|
-
error: this.errorLogger.logErrorHandler('failed to get user stage'),
|
|
37
|
-
});
|
|
38
23
|
router.events
|
|
39
24
|
.pipe(this.takeUntilDestroyed(), filter((event) => event instanceof NavigationEnd))
|
|
40
25
|
.subscribe((event) => {
|
|
@@ -71,51 +56,34 @@ export class SpaceMenuComponent extends SpaceBaseComponent {
|
|
|
71
56
|
closeMenu() {
|
|
72
57
|
this.menuCtrl.close().catch(this.errorLogger.logError);
|
|
73
58
|
}
|
|
74
|
-
onSpaceSelected(event) {
|
|
75
|
-
const spaceID = event.detail.value;
|
|
76
|
-
if (spaceID === this.space?.id) {
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
const space = this.$spaces()?.find((t) => t.id === spaceID);
|
|
80
|
-
if (space) {
|
|
81
|
-
this.setSpaceRef(space);
|
|
82
|
-
this.spaceNav
|
|
83
|
-
.navigateToSpace(space)
|
|
84
|
-
.catch(this.errorLogger.logErrorHandler('Failed to navigate to teams page on current team changed from team menu dropdown'));
|
|
85
|
-
}
|
|
86
|
-
this.menuCtrl.close().catch(console.error);
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
59
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SpaceMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
90
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "
|
|
60
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.3", type: SpaceMenuComponent, isStandalone: true, selector: "sneat-space-menu", providers: [
|
|
91
61
|
{
|
|
92
62
|
provide: ClassName,
|
|
93
63
|
useValue: 'SpaceMenuComponent',
|
|
94
64
|
},
|
|
95
65
|
SpaceComponentBaseParams,
|
|
96
|
-
], usesInheritance: true, ngImport: i0, template: "<ion-list>\n <ion-item>\n <!--\t\t<ion-icon [name]=\"space.type === 'family' ? 'people-outline' : space.type === 'private' ? 'person-circle-outline' : 'people-outline' \" slot=\"end\" />-->\n <
|
|
66
|
+
], usesInheritance: true, ngImport: i0, template: "<ion-list>\n <ion-item class=\"sneat-pane-header\">\n <!--\t\t<ion-icon [name]=\"space.type === 'family' ? 'people-outline' : space.type === 'private' ? 'person-circle-outline' : 'people-outline' \" slot=\"end\" />-->\n <sneat-space-selector\n [closeMenuOnSelection]=\"true\"\n justify=\"space-between\"\n />\n <!--\t\t@if (!spaces) {-->\n <!--\t\t\t<ion-spinner name=\"lines\" slot=\"end\"></ion-spinner>-->\n <!--\t\t}-->\n </ion-item>\n <ion-item\n tappable\n [routerLink]=\"spacePageUrl('overview')\"\n [disabled]=\"$disabled()\"\n [class.currentPage]=\"$currentPage() === ''\"\n (click)=\"goOverview()\"\n >\n <ion-icon name=\"home-outline\" slot=\"start\" />\n <ion-label>Overview</ion-label>\n </ion-item>\n <ion-item\n tappable\n [routerLink]=\"spacePageUrl('members')\"\n routerDirection=\"root\"\n [disabled]=\"$disabled()\"\n [class.currentPage]=\"$currentPage() === 'members'\"\n (click)=\"closeMenu()\"\n >\n <ion-icon name=\"people-circle-outline\" slot=\"start\" />\n <ion-label>Members</ion-label>\n </ion-item>\n <ion-item\n tappable\n lines=\"none\"\n class=\"sneat-no-bottom-border\"\n [routerLink]=\"spacePageUrl('calendar')\"\n routerDirection=\"root\"\n [disabled]=\"$disabled()\"\n [class.currentPage]=\"$currentPage() === 'calendar'\"\n (click)=\"closeMenu()\"\n >\n <ion-icon name=\"calendar-outline\" slot=\"start\" />\n <ion-label>Calendar</ion-label>\n </ion-item>\n <ion-item-divider color=\"header\">\n <ion-label color=\"medium\">Extensions</ion-label>\n </ion-item-divider>\n <sneat-space-extension-links\n [$space]=\"$space()\"\n [$presentation]=\"'menu'\"\n [$currentPage]=\"$currentPage()\"\n [$disabled]=\"$disabled()\"\n (linkSelected)=\"closeMenu()\"\n />\n <sneat-auth-menu-item />\n</ion-list>\n", styles: [".currentPage ion-label{font-weight:700}\n"], dependencies: [{ kind: "component", type: AuthMenuItemComponent, selector: "sneat-auth-menu-item" }, { kind: "ngmodule", type: SpaceServiceModule }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "component", type: IonList, selector: "ion-list", inputs: ["inset", "lines", "mode"] }, { 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: IonItemDivider, selector: "ion-item-divider", inputs: ["color", "mode", "sticky"] }, { 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: SpaceExtensionLinksComponent, selector: "sneat-space-extension-links", inputs: ["$space", "$items", "$presentation", "$currentPage", "$disabled"], outputs: ["linkSelected"] }, { kind: "component", type: SpaceSelectorComponent, selector: "sneat-space-selector", inputs: ["closeMenuOnSelection", "justify", "navigateOnSelection"], outputs: ["spaceSelected"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
97
67
|
}
|
|
98
68
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SpaceMenuComponent, decorators: [{
|
|
99
69
|
type: Component,
|
|
100
70
|
args: [{ selector: 'sneat-space-menu', imports: [
|
|
101
71
|
AuthMenuItemComponent,
|
|
102
72
|
SpaceServiceModule,
|
|
103
|
-
TitleCasePipe,
|
|
104
73
|
RouterLink,
|
|
105
74
|
IonList,
|
|
106
75
|
IonItem,
|
|
107
76
|
IonItemDivider,
|
|
108
|
-
IonSelect,
|
|
109
|
-
IonSelectOption,
|
|
110
77
|
IonIcon,
|
|
111
78
|
IonLabel,
|
|
112
79
|
SpaceExtensionLinksComponent,
|
|
80
|
+
SpaceSelectorComponent,
|
|
113
81
|
], providers: [
|
|
114
82
|
{
|
|
115
83
|
provide: ClassName,
|
|
116
84
|
useValue: 'SpaceMenuComponent',
|
|
117
85
|
},
|
|
118
86
|
SpaceComponentBaseParams,
|
|
119
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ion-list>\n <ion-item>\n <!--\t\t<ion-icon [name]=\"space.type === 'family' ? 'people-outline' : space.type === 'private' ? 'person-circle-outline' : 'people-outline' \" slot=\"end\" />-->\n <
|
|
87
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ion-list>\n <ion-item class=\"sneat-pane-header\">\n <!--\t\t<ion-icon [name]=\"space.type === 'family' ? 'people-outline' : space.type === 'private' ? 'person-circle-outline' : 'people-outline' \" slot=\"end\" />-->\n <sneat-space-selector\n [closeMenuOnSelection]=\"true\"\n justify=\"space-between\"\n />\n <!--\t\t@if (!spaces) {-->\n <!--\t\t\t<ion-spinner name=\"lines\" slot=\"end\"></ion-spinner>-->\n <!--\t\t}-->\n </ion-item>\n <ion-item\n tappable\n [routerLink]=\"spacePageUrl('overview')\"\n [disabled]=\"$disabled()\"\n [class.currentPage]=\"$currentPage() === ''\"\n (click)=\"goOverview()\"\n >\n <ion-icon name=\"home-outline\" slot=\"start\" />\n <ion-label>Overview</ion-label>\n </ion-item>\n <ion-item\n tappable\n [routerLink]=\"spacePageUrl('members')\"\n routerDirection=\"root\"\n [disabled]=\"$disabled()\"\n [class.currentPage]=\"$currentPage() === 'members'\"\n (click)=\"closeMenu()\"\n >\n <ion-icon name=\"people-circle-outline\" slot=\"start\" />\n <ion-label>Members</ion-label>\n </ion-item>\n <ion-item\n tappable\n lines=\"none\"\n class=\"sneat-no-bottom-border\"\n [routerLink]=\"spacePageUrl('calendar')\"\n routerDirection=\"root\"\n [disabled]=\"$disabled()\"\n [class.currentPage]=\"$currentPage() === 'calendar'\"\n (click)=\"closeMenu()\"\n >\n <ion-icon name=\"calendar-outline\" slot=\"start\" />\n <ion-label>Calendar</ion-label>\n </ion-item>\n <ion-item-divider color=\"header\">\n <ion-label color=\"medium\">Extensions</ion-label>\n </ion-item-divider>\n <sneat-space-extension-links\n [$space]=\"$space()\"\n [$presentation]=\"'menu'\"\n [$currentPage]=\"$currentPage()\"\n [$disabled]=\"$disabled()\"\n (linkSelected)=\"closeMenu()\"\n />\n <sneat-auth-menu-item />\n</ion-list>\n", styles: [".currentPage ion-label{font-weight:700}\n"] }]
|
|
120
88
|
}], ctorParameters: () => [] });
|
|
121
89
|
//# sourceMappingURL=space-menu.component.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"space-menu.component.js","sourceRoot":"","sources":["../../../../../../../libs/space/components/src/lib/space-menu/space-menu.component.ts","../../../../../../../libs/space/components/src/lib/space-menu/space-menu.component.html"],"names":[],"mappings":"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","sourcesContent":["import { TitleCasePipe } from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n Component,\n computed,\n inject,\n signal,\n} from '@angular/core';\nimport {\n ActivatedRoute,\n NavigationEnd,\n Router,\n RouterLink,\n} from '@angular/router';\nimport {\n IonIcon,\n IonItem,\n IonItemDivider,\n IonLabel,\n IonList,\n IonSelect,\n IonSelectOption,\n MenuController,\n} from '@ionic/angular';\nimport { ISneatUserState } from '@sneat/auth-core';\nimport { IUserSpaceBrief } from '@sneat/auth-models';\nimport { AuthMenuItemComponent } from '@sneat/auth-ui';\nimport { IIdAndBrief } from '@sneat/core';\nimport { zipMapBriefsWithIDs } from '@sneat/space-models';\nimport { SpaceServiceModule } from '@sneat/space-services';\nimport { filter } from 'rxjs';\nimport { takeUntil } from 'rxjs/operators';\nimport { SpaceBaseComponent } from '../space-base-component.directive';\nimport { SpaceComponentBaseParams } from '../space-component-base-params.service';\nimport { ClassName } from '@sneat/ui';\nimport { SpaceExtensionLinksComponent } from '../space-extension-links';\n\n@Component({\n selector: 'sneat-space-menu',\n templateUrl: './space-menu.component.html',\n styles: '.currentPage ion-label {font-weight: bold}',\n imports: [\n AuthMenuItemComponent,\n SpaceServiceModule,\n TitleCasePipe,\n RouterLink,\n IonList,\n IonItem,\n IonItemDivider,\n IonSelect,\n IonSelectOption,\n IonIcon,\n IonLabel,\n SpaceExtensionLinksComponent,\n ],\n providers: [\n {\n provide: ClassName,\n useValue: 'SpaceMenuComponent',\n },\n SpaceComponentBaseParams,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class SpaceMenuComponent extends SpaceBaseComponent {\n protected readonly $spaces = signal<\n readonly IIdAndBrief<IUserSpaceBrief>[] | undefined\n >(undefined);\n\n protected readonly $disabled = computed(\n () => !this.$spaceID() || this.$spaceNotFound(),\n );\n\n protected readonly $currentPage = signal<string>('');\n\n private readonly activatedRoute = inject(ActivatedRoute);\n private readonly menuCtrl = inject(MenuController);\n\n constructor() {\n const router = inject(Router);\n\n super();\n this.spaceParams.userService.userState\n .pipe(takeUntil(this.destroyed$))\n .subscribe({\n next: this.onUserStateChanged,\n error: this.errorLogger.logErrorHandler('failed to get user stage'),\n });\n router.events\n .pipe(\n this.takeUntilDestroyed(),\n filter((event) => event instanceof NavigationEnd),\n )\n .subscribe((event: NavigationEnd) => {\n let route = this.activatedRoute.firstChild;\n while (route?.firstChild) {\n route = route.firstChild;\n }\n const url = event.urlAfterRedirects.split('/');\n this.$currentPage.set(url.length > 4 ? url[4] : '');\n });\n }\n\n // TODO: Should we use goSpacePage('') instead?\n protected goOverview(): boolean {\n const space = this.$space();\n if (!space) {\n this.errorLogger.logError('no space context');\n return false;\n }\n this.spaceParams.spaceNavService.navigateToSpace(space).then((v) => {\n if (v) {\n this.closeMenu();\n }\n });\n return false;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n protected goSpacePage(event: Event, _p: string): boolean {\n // At the moment we use routerLink for navigation\n event.stopPropagation();\n // event.preventDefault();\n this.closeMenu();\n return false;\n }\n\n protected closeMenu(): void {\n this.menuCtrl.close().catch(this.errorLogger.logError);\n }\n\n protected onSpaceSelected(event: Event): void {\n const spaceID = (event as CustomEvent).detail.value as string;\n if (spaceID === this.space?.id) {\n return;\n }\n const space = this.$spaces()?.find((t) => t.id === spaceID);\n if (space) {\n this.setSpaceRef(space);\n this.spaceNav\n .navigateToSpace(space)\n .catch(\n this.errorLogger.logErrorHandler(\n 'Failed to navigate to teams page on current team changed from team menu dropdown',\n ),\n );\n }\n this.menuCtrl.close().catch(console.error);\n return;\n }\n\n private readonly onUserStateChanged = (userState: ISneatUserState): void => {\n this.$spaces.set(\n userState?.record\n ? zipMapBriefsWithIDs(userState.record.spaces) || []\n : undefined,\n );\n };\n}\n","<ion-list>\n <ion-item>\n <!--\t\t<ion-icon [name]=\"space.type === 'family' ? 'people-outline' : space.type === 'private' ? 'person-circle-outline' : 'people-outline' \" slot=\"end\" />-->\n <ion-select\n label=\"Space\"\n [value]=\"$space().id\"\n interface=\"popover\"\n (ionChange)=\"onSpaceSelected($event)\"\n style=\"font-weight: bold\"\n >\n @for (userSpace of $spaces(); track userSpace.id) {\n <ion-select-option [value]=\"userSpace.id\"\n >{{\n userSpace.brief.title ||\n (userSpace.brief.type | titlecase) ||\n space.id\n }}\n </ion-select-option>\n } @empty {\n @let space = $space();\n <ion-select-option [value]=\"$safeNavigationMigration(space?.id)\">\n @if (!space || (!space?.id && !space?.type)) {\n Loading...\n } @else {\n {{ space.brief?.title || (space.type | titlecase) || space.id }}\n }\n </ion-select-option>\n }\n </ion-select>\n <!--\t\t@if (!spaces) {-->\n <!--\t\t\t<ion-spinner name=\"lines\" slot=\"end\"></ion-spinner>-->\n <!--\t\t}-->\n </ion-item>\n <ion-item\n tappable\n [routerLink]=\"spacePageUrl('overview')\"\n [disabled]=\"$disabled()\"\n [class.currentPage]=\"$currentPage() === ''\"\n (click)=\"goOverview()\"\n >\n <ion-icon name=\"home-outline\" slot=\"start\" />\n <ion-label>Overview</ion-label>\n </ion-item>\n <ion-item\n tappable\n [routerLink]=\"spacePageUrl('members')\"\n routerDirection=\"root\"\n [disabled]=\"$disabled()\"\n [class.currentPage]=\"$currentPage() === 'members'\"\n (click)=\"closeMenu()\"\n >\n <ion-icon name=\"people-circle-outline\" slot=\"start\" />\n <ion-label>Members</ion-label>\n </ion-item>\n <ion-item\n tappable\n lines=\"none\"\n class=\"sneat-no-bottom-border\"\n [routerLink]=\"spacePageUrl('calendar')\"\n routerDirection=\"root\"\n [disabled]=\"$disabled()\"\n [class.currentPage]=\"$currentPage() === 'calendar'\"\n (click)=\"closeMenu()\"\n >\n <ion-icon name=\"calendar-outline\" slot=\"start\" />\n <ion-label>Calendar</ion-label>\n </ion-item>\n <ion-item-divider color=\"header\">\n <ion-label color=\"medium\">Extensions</ion-label>\n </ion-item-divider>\n <sneat-space-extension-links\n [$space]=\"$space()\"\n [$presentation]=\"'menu'\"\n [$currentPage]=\"$currentPage()\"\n [$disabled]=\"$disabled()\"\n (linkSelected)=\"closeMenu()\"\n />\n <sneat-auth-menu-item />\n</ion-list>\n"]}
|
|
1
|
+
{"version":3,"file":"space-menu.component.js","sourceRoot":"","sources":["../../../../../../../libs/space/components/src/lib/space-menu/space-menu.component.ts","../../../../../../../libs/space/components/src/lib/space-menu/space-menu.component.html"],"names":[],"mappings":"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","sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n computed,\n inject,\n signal,\n} from '@angular/core';\nimport {\n ActivatedRoute,\n NavigationEnd,\n Router,\n RouterLink,\n} from '@angular/router';\nimport {\n IonIcon,\n IonItem,\n IonItemDivider,\n IonLabel,\n IonList,\n MenuController,\n} from '@ionic/angular';\nimport { AuthMenuItemComponent } from '@sneat/auth-ui';\nimport { SpaceServiceModule } from '@sneat/space-services';\nimport { filter } from 'rxjs';\nimport { SpaceBaseComponent } from '../space-base-component.directive';\nimport { SpaceComponentBaseParams } from '../space-component-base-params.service';\nimport { ClassName } from '@sneat/ui';\nimport { SpaceExtensionLinksComponent } from '../space-extension-links';\nimport { SpaceSelectorComponent } from '../space-selector';\n\n@Component({\n selector: 'sneat-space-menu',\n templateUrl: './space-menu.component.html',\n styles: '.currentPage ion-label {font-weight: bold}',\n imports: [\n AuthMenuItemComponent,\n SpaceServiceModule,\n RouterLink,\n IonList,\n IonItem,\n IonItemDivider,\n IonIcon,\n IonLabel,\n SpaceExtensionLinksComponent,\n SpaceSelectorComponent,\n ],\n providers: [\n {\n provide: ClassName,\n useValue: 'SpaceMenuComponent',\n },\n SpaceComponentBaseParams,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class SpaceMenuComponent extends SpaceBaseComponent {\n protected readonly $disabled = computed(\n () => !this.$spaceID() || this.$spaceNotFound(),\n );\n\n protected readonly $currentPage = signal<string>('');\n\n private readonly activatedRoute = inject(ActivatedRoute);\n private readonly menuCtrl = inject(MenuController);\n\n constructor() {\n const router = inject(Router);\n\n super();\n router.events\n .pipe(\n this.takeUntilDestroyed(),\n filter((event) => event instanceof NavigationEnd),\n )\n .subscribe((event: NavigationEnd) => {\n let route = this.activatedRoute.firstChild;\n while (route?.firstChild) {\n route = route.firstChild;\n }\n const url = event.urlAfterRedirects.split('/');\n this.$currentPage.set(url.length > 4 ? url[4] : '');\n });\n }\n\n // TODO: Should we use goSpacePage('') instead?\n protected goOverview(): boolean {\n const space = this.$space();\n if (!space) {\n this.errorLogger.logError('no space context');\n return false;\n }\n this.spaceParams.spaceNavService.navigateToSpace(space).then((v) => {\n if (v) {\n this.closeMenu();\n }\n });\n return false;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n protected goSpacePage(event: Event, _p: string): boolean {\n // At the moment we use routerLink for navigation\n event.stopPropagation();\n // event.preventDefault();\n this.closeMenu();\n return false;\n }\n\n protected closeMenu(): void {\n this.menuCtrl.close().catch(this.errorLogger.logError);\n }\n\n}\n","<ion-list>\n <ion-item class=\"sneat-pane-header\">\n <!--\t\t<ion-icon [name]=\"space.type === 'family' ? 'people-outline' : space.type === 'private' ? 'person-circle-outline' : 'people-outline' \" slot=\"end\" />-->\n <sneat-space-selector\n [closeMenuOnSelection]=\"true\"\n justify=\"space-between\"\n />\n <!--\t\t@if (!spaces) {-->\n <!--\t\t\t<ion-spinner name=\"lines\" slot=\"end\"></ion-spinner>-->\n <!--\t\t}-->\n </ion-item>\n <ion-item\n tappable\n [routerLink]=\"spacePageUrl('overview')\"\n [disabled]=\"$disabled()\"\n [class.currentPage]=\"$currentPage() === ''\"\n (click)=\"goOverview()\"\n >\n <ion-icon name=\"home-outline\" slot=\"start\" />\n <ion-label>Overview</ion-label>\n </ion-item>\n <ion-item\n tappable\n [routerLink]=\"spacePageUrl('members')\"\n routerDirection=\"root\"\n [disabled]=\"$disabled()\"\n [class.currentPage]=\"$currentPage() === 'members'\"\n (click)=\"closeMenu()\"\n >\n <ion-icon name=\"people-circle-outline\" slot=\"start\" />\n <ion-label>Members</ion-label>\n </ion-item>\n <ion-item\n tappable\n lines=\"none\"\n class=\"sneat-no-bottom-border\"\n [routerLink]=\"spacePageUrl('calendar')\"\n routerDirection=\"root\"\n [disabled]=\"$disabled()\"\n [class.currentPage]=\"$currentPage() === 'calendar'\"\n (click)=\"closeMenu()\"\n >\n <ion-icon name=\"calendar-outline\" slot=\"start\" />\n <ion-label>Calendar</ion-label>\n </ion-item>\n <ion-item-divider color=\"header\">\n <ion-label color=\"medium\">Extensions</ion-label>\n </ion-item-divider>\n <sneat-space-extension-links\n [$space]=\"$space()\"\n [$presentation]=\"'menu'\"\n [$currentPage]=\"$currentPage()\"\n [$disabled]=\"$disabled()\"\n (linkSelected)=\"closeMenu()\"\n />\n <sneat-auth-menu-item />\n</ion-list>\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../../libs/space/components/src/lib/space-selector/index.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAC","sourcesContent":["export * from './space-selector.component';\n"]}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { TitleCasePipe } from '@angular/common';
|
|
2
|
+
import { ChangeDetectionStrategy, Component, inject, input, output, signal, } from '@angular/core';
|
|
3
|
+
import { IonSelect, IonSelectOption, MenuController } from '@ionic/angular';
|
|
4
|
+
import { zipMapBriefsWithIDs } from '@sneat/space-models';
|
|
5
|
+
import { SpaceServiceModule } from '@sneat/space-services';
|
|
6
|
+
import { ClassName } from '@sneat/ui';
|
|
7
|
+
import { takeUntil } from 'rxjs/operators';
|
|
8
|
+
import { SpaceBaseComponent } from '../space-base-component.directive';
|
|
9
|
+
import { SpaceComponentBaseParams } from '../space-component-base-params.service';
|
|
10
|
+
import * as i0 from "@angular/core";
|
|
11
|
+
/**
|
|
12
|
+
* Switches the active space and, by default, navigates to that space's default
|
|
13
|
+
* page. Hosts that need to preserve their own sub-route can handle selection
|
|
14
|
+
* through `spaceSelected` and turn off the default navigation.
|
|
15
|
+
*
|
|
16
|
+
* It is deliberately presentation-only: callers place it in a menu section,
|
|
17
|
+
* a card header, or another Ionic item and opt into closing a surrounding menu.
|
|
18
|
+
*/
|
|
19
|
+
export class SpaceSelectorComponent extends SpaceBaseComponent {
|
|
20
|
+
constructor() {
|
|
21
|
+
super();
|
|
22
|
+
/** Whether a containing Ionic menu should close after a space is selected. */
|
|
23
|
+
this.closeMenuOnSelection = input(false, /* @ts-ignore */
|
|
24
|
+
...(ngDevMode ? [{ debugName: "closeMenuOnSelection" }] : /* istanbul ignore next */ []));
|
|
25
|
+
/** Align the label and selected space within the enclosing item. */
|
|
26
|
+
this.justify = input('start', /* @ts-ignore */
|
|
27
|
+
...(ngDevMode ? [{ debugName: "justify" }] : /* istanbul ignore next */ []));
|
|
28
|
+
/** Whether selection should navigate to the selected space's default page. */
|
|
29
|
+
this.navigateOnSelection = input(true, /* @ts-ignore */
|
|
30
|
+
...(ngDevMode ? [{ debugName: "navigateOnSelection" }] : /* istanbul ignore next */ []));
|
|
31
|
+
/** Emits the selected space so a host can preserve its contextual sub-route. */
|
|
32
|
+
this.spaceSelected = output();
|
|
33
|
+
this.$spaces = signal(undefined, /* @ts-ignore */
|
|
34
|
+
...(ngDevMode ? [{ debugName: "$spaces" }] : /* istanbul ignore next */ []));
|
|
35
|
+
this.menuCtrl = inject(MenuController);
|
|
36
|
+
this.spaceParams.userService.userState
|
|
37
|
+
.pipe(takeUntil(this.destroyed$))
|
|
38
|
+
.subscribe({
|
|
39
|
+
next: (userState) => this.$spaces.set(userState?.record
|
|
40
|
+
? zipMapBriefsWithIDs(userState.record.spaces) || []
|
|
41
|
+
: undefined),
|
|
42
|
+
error: this.errorLogger.logErrorHandler('failed to get user state'),
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
onSpaceSelected(event) {
|
|
46
|
+
const spaceID = event.detail.value;
|
|
47
|
+
if (spaceID === this.space?.id) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const space = this.$spaces()?.find((candidate) => candidate.id === spaceID);
|
|
51
|
+
if (space) {
|
|
52
|
+
this.setSpaceRef(space);
|
|
53
|
+
this.spaceSelected.emit(space);
|
|
54
|
+
if (this.navigateOnSelection()) {
|
|
55
|
+
this.spaceNav
|
|
56
|
+
.navigateToSpace(space)
|
|
57
|
+
.catch(this.errorLogger.logErrorHandler('Failed to navigate to selected space'));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (this.closeMenuOnSelection()) {
|
|
61
|
+
this.menuCtrl.close().catch(this.errorLogger.logError);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SpaceSelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
65
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: SpaceSelectorComponent, isStandalone: true, selector: "sneat-space-selector", inputs: { closeMenuOnSelection: { classPropertyName: "closeMenuOnSelection", publicName: "closeMenuOnSelection", isSignal: true, isRequired: false, transformFunction: null }, justify: { classPropertyName: "justify", publicName: "justify", isSignal: true, isRequired: false, transformFunction: null }, navigateOnSelection: { classPropertyName: "navigateOnSelection", publicName: "navigateOnSelection", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { spaceSelected: "spaceSelected" }, providers: [
|
|
66
|
+
{ provide: ClassName, useValue: 'SpaceSelectorComponent' },
|
|
67
|
+
SpaceComponentBaseParams,
|
|
68
|
+
], usesInheritance: true, ngImport: i0, template: `
|
|
69
|
+
<ion-select
|
|
70
|
+
label="Space"
|
|
71
|
+
[value]="$space().id"
|
|
72
|
+
[justify]="justify()"
|
|
73
|
+
interface="popover"
|
|
74
|
+
(ionChange)="onSpaceSelected($event)"
|
|
75
|
+
style="font-weight: bold"
|
|
76
|
+
>
|
|
77
|
+
@for (userSpace of $spaces(); track userSpace.id) {
|
|
78
|
+
<ion-select-option [value]="userSpace.id">
|
|
79
|
+
{{
|
|
80
|
+
userSpace.brief.title ||
|
|
81
|
+
(userSpace.brief.type | titlecase) ||
|
|
82
|
+
userSpace.id
|
|
83
|
+
}}
|
|
84
|
+
</ion-select-option>
|
|
85
|
+
} @empty {
|
|
86
|
+
@let space = $space();
|
|
87
|
+
<ion-select-option [value]="$safeNavigationMigration(space?.id)">
|
|
88
|
+
@if (!space || (!space?.id && !space?.type)) {
|
|
89
|
+
Loading...
|
|
90
|
+
} @else {
|
|
91
|
+
{{ space.brief?.title || (space.type | titlecase) || space.id }}
|
|
92
|
+
}
|
|
93
|
+
</ion-select-option>
|
|
94
|
+
}
|
|
95
|
+
</ion-select>
|
|
96
|
+
`, isInline: true, styles: ["ion-select::part(label){color:var(--ion-color-medium)}\n"], dependencies: [{ kind: "ngmodule", type: SpaceServiceModule }, { kind: "component", type: IonSelect, selector: "ion-select", inputs: ["cancelText", "color", "compareWith", "disabled", "errorText", "expandedIcon", "fill", "helperText", "interface", "interfaceOptions", "justify", "label", "labelPlacement", "mode", "multiple", "name", "okText", "placeholder", "selectedText", "shape", "toggleIcon", "value"] }, { kind: "component", type: IonSelectOption, selector: "ion-select-option", inputs: ["description", "disabled", "justify", "labelPlacement", "mode", "value"] }, { kind: "pipe", type: TitleCasePipe, name: "titlecase" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
97
|
+
}
|
|
98
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SpaceSelectorComponent, decorators: [{
|
|
99
|
+
type: Component,
|
|
100
|
+
args: [{ selector: 'sneat-space-selector', template: `
|
|
101
|
+
<ion-select
|
|
102
|
+
label="Space"
|
|
103
|
+
[value]="$space().id"
|
|
104
|
+
[justify]="justify()"
|
|
105
|
+
interface="popover"
|
|
106
|
+
(ionChange)="onSpaceSelected($event)"
|
|
107
|
+
style="font-weight: bold"
|
|
108
|
+
>
|
|
109
|
+
@for (userSpace of $spaces(); track userSpace.id) {
|
|
110
|
+
<ion-select-option [value]="userSpace.id">
|
|
111
|
+
{{
|
|
112
|
+
userSpace.brief.title ||
|
|
113
|
+
(userSpace.brief.type | titlecase) ||
|
|
114
|
+
userSpace.id
|
|
115
|
+
}}
|
|
116
|
+
</ion-select-option>
|
|
117
|
+
} @empty {
|
|
118
|
+
@let space = $space();
|
|
119
|
+
<ion-select-option [value]="$safeNavigationMigration(space?.id)">
|
|
120
|
+
@if (!space || (!space?.id && !space?.type)) {
|
|
121
|
+
Loading...
|
|
122
|
+
} @else {
|
|
123
|
+
{{ space.brief?.title || (space.type | titlecase) || space.id }}
|
|
124
|
+
}
|
|
125
|
+
</ion-select-option>
|
|
126
|
+
}
|
|
127
|
+
</ion-select>
|
|
128
|
+
`, imports: [TitleCasePipe, SpaceServiceModule, IonSelect, IonSelectOption], providers: [
|
|
129
|
+
{ provide: ClassName, useValue: 'SpaceSelectorComponent' },
|
|
130
|
+
SpaceComponentBaseParams,
|
|
131
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, styles: ["ion-select::part(label){color:var(--ion-color-medium)}\n"] }]
|
|
132
|
+
}], ctorParameters: () => [], propDecorators: { closeMenuOnSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeMenuOnSelection", required: false }] }], justify: [{ type: i0.Input, args: [{ isSignal: true, alias: "justify", required: false }] }], navigateOnSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "navigateOnSelection", required: false }] }], spaceSelected: [{ type: i0.Output, args: ["spaceSelected"] }] } });
|
|
133
|
+
//# sourceMappingURL=space-selector.component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"space-selector.component.js","sourceRoot":"","sources":["../../../../../../../libs/space/components/src/lib/space-selector/space-selector.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { TitleCasePipe } from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n Component,\n inject,\n input,\n output,\n signal,\n} from '@angular/core';\nimport { IonSelect, IonSelectOption, MenuController } from '@ionic/angular';\nimport { ISneatUserState } from '@sneat/auth-core';\nimport { IUserSpaceBrief } from '@sneat/auth-models';\nimport { IIdAndBrief } from '@sneat/core';\nimport { zipMapBriefsWithIDs } from '@sneat/space-models';\nimport { SpaceServiceModule } from '@sneat/space-services';\nimport { ClassName } from '@sneat/ui';\nimport { takeUntil } from 'rxjs/operators';\nimport { SpaceBaseComponent } from '../space-base-component.directive';\nimport { SpaceComponentBaseParams } from '../space-component-base-params.service';\n\n/**\n * Switches the active space and, by default, navigates to that space's default\n * page. Hosts that need to preserve their own sub-route can handle selection\n * through `spaceSelected` and turn off the default navigation.\n *\n * It is deliberately presentation-only: callers place it in a menu section,\n * a card header, or another Ionic item and opt into closing a surrounding menu.\n */\n@Component({\n selector: 'sneat-space-selector',\n styles: `\n ion-select::part(label) {\n color: var(--ion-color-medium);\n }\n `,\n template: `\n <ion-select\n label=\"Space\"\n [value]=\"$space().id\"\n [justify]=\"justify()\"\n interface=\"popover\"\n (ionChange)=\"onSpaceSelected($event)\"\n style=\"font-weight: bold\"\n >\n @for (userSpace of $spaces(); track userSpace.id) {\n <ion-select-option [value]=\"userSpace.id\">\n {{\n userSpace.brief.title ||\n (userSpace.brief.type | titlecase) ||\n userSpace.id\n }}\n </ion-select-option>\n } @empty {\n @let space = $space();\n <ion-select-option [value]=\"$safeNavigationMigration(space?.id)\">\n @if (!space || (!space?.id && !space?.type)) {\n Loading...\n } @else {\n {{ space.brief?.title || (space.type | titlecase) || space.id }}\n }\n </ion-select-option>\n }\n </ion-select>\n `,\n imports: [TitleCasePipe, SpaceServiceModule, IonSelect, IonSelectOption],\n providers: [\n { provide: ClassName, useValue: 'SpaceSelectorComponent' },\n SpaceComponentBaseParams,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class SpaceSelectorComponent extends SpaceBaseComponent {\n /** Whether a containing Ionic menu should close after a space is selected. */\n readonly closeMenuOnSelection = input(false);\n /** Align the label and selected space within the enclosing item. */\n readonly justify = input<'start' | 'end' | 'space-between'>('start');\n /** Whether selection should navigate to the selected space's default page. */\n readonly navigateOnSelection = input(true);\n /** Emits the selected space so a host can preserve its contextual sub-route. */\n readonly spaceSelected = output<IIdAndBrief<IUserSpaceBrief>>();\n\n protected readonly $spaces = signal<\n readonly IIdAndBrief<IUserSpaceBrief>[] | undefined\n >(undefined);\n\n private readonly menuCtrl = inject(MenuController);\n\n constructor() {\n super();\n this.spaceParams.userService.userState\n .pipe(takeUntil(this.destroyed$))\n .subscribe({\n next: (userState: ISneatUserState) =>\n this.$spaces.set(\n userState?.record\n ? zipMapBriefsWithIDs(userState.record.spaces) || []\n : undefined,\n ),\n error: this.errorLogger.logErrorHandler('failed to get user state'),\n });\n }\n\n protected onSpaceSelected(event: Event): void {\n const spaceID = (event as CustomEvent).detail.value as string;\n if (spaceID === this.space?.id) {\n return;\n }\n const space = this.$spaces()?.find((candidate) => candidate.id === spaceID);\n if (space) {\n this.setSpaceRef(space);\n this.spaceSelected.emit(space);\n if (this.navigateOnSelection()) {\n this.spaceNav\n .navigateToSpace(space)\n .catch(\n this.errorLogger.logErrorHandler(\n 'Failed to navigate to selected space',\n ),\n );\n }\n }\n if (this.closeMenuOnSelection()) {\n this.menuCtrl.close().catch(this.errorLogger.logError);\n }\n }\n}\n"]}
|
|
@@ -142,7 +142,7 @@ export class SpacesCardComponent {
|
|
|
142
142
|
this.showAdd.set(false);
|
|
143
143
|
}
|
|
144
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 }); }
|
|
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 class=\"sneat-card-header\" lines=\"full\">\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 }); }
|
|
146
146
|
}
|
|
147
147
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SpacesCardComponent, decorators: [{
|
|
148
148
|
type: Component,
|
|
@@ -161,7 +161,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
|
|
|
161
161
|
IonSkeletonText,
|
|
162
162
|
IonCardContent,
|
|
163
163
|
SpacesListComponent,
|
|
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" }]
|
|
164
|
+
], template: "<ion-card>\n <ion-item class=\"sneat-card-header\" lines=\"full\">\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
165
|
}], ctorParameters: () => [], propDecorators: { title: [{
|
|
166
166
|
type: Input
|
|
167
167
|
}], spaceType: [{
|
|
@@ -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 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
|
+
{"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 class=\"sneat-card-header\" lines=\"full\">\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"]}
|
|
@@ -112,7 +112,7 @@ export class SpacesListComponent extends SneatBaseComponent {
|
|
|
112
112
|
provide: ClassName,
|
|
113
113
|
useValue: 'SpacesListComponent',
|
|
114
114
|
},
|
|
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 ? '
|
|
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 ? 'none' : '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 }); }
|
|
116
116
|
}
|
|
117
117
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: SpacesListComponent, decorators: [{
|
|
118
118
|
type: Component,
|
|
@@ -131,7 +131,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
|
|
|
131
131
|
provide: ClassName,
|
|
132
132
|
useValue: 'SpacesListComponent',
|
|
133
133
|
},
|
|
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 ? '
|
|
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 ? 'none' : '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
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: [{
|
|
136
136
|
type: Output
|
|
137
137
|
}], leftSpace: [{
|