@sinequa/atomic-angular 1.15.0 → 1.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/document-workspaces/i18n/de.json +3 -0
- package/document-workspaces/i18n/en.json +3 -0
- package/document-workspaces/i18n/fr.json +3 -0
- package/document-workspaces/i18n/ja.json +3 -0
- package/document-workspaces/index.d.ts +6 -1
- package/fesm2022/sinequa-atomic-angular-document-workspaces.mjs +117 -23
- package/fesm2022/sinequa-atomic-angular-document-workspaces.mjs.map +1 -1
- package/fesm2022/sinequa-atomic-angular.mjs +16 -19
- package/fesm2022/sinequa-atomic-angular.mjs.map +1 -1
- package/index.d.ts +160 -160
- package/package.json +1 -1
|
@@ -33,6 +33,9 @@
|
|
|
33
33
|
"deleteWorkspacesTitle": "{count, plural, one {{count} Arbeitsbereich löschen?} other {{count} Arbeitsbereiche löschen?}}",
|
|
34
34
|
"deleteTitle": "Arbeitsbereich löschen?",
|
|
35
35
|
"loadError": "Arbeitsbereiche konnten nicht geladen werden.",
|
|
36
|
+
"loadMore": "Mehr laden",
|
|
37
|
+
"loadMoreError": "Weitere Arbeitsbereiche konnten nicht geladen werden: {error}",
|
|
38
|
+
"partial": "{shown} von {total} Arbeitsbereichen angezeigt",
|
|
36
39
|
"loading": "Arbeitsbereiche werden geladen…",
|
|
37
40
|
"refreshError": "Arbeitsbereiche konnten nicht aktualisiert werden: {error}",
|
|
38
41
|
"roleOwner": "Eigentümer",
|
|
@@ -33,6 +33,9 @@
|
|
|
33
33
|
"deleteWorkspacesTitle": "{count, plural, one {Delete {count} workspace?} other {Delete {count} workspaces?}}",
|
|
34
34
|
"deleteTitle": "Delete workspace?",
|
|
35
35
|
"loadError": "Couldn’t load workspaces.",
|
|
36
|
+
"loadMore": "Load more",
|
|
37
|
+
"loadMoreError": "Couldn’t load more workspaces: {error}",
|
|
38
|
+
"partial": "{shown} of {total} workspaces shown",
|
|
36
39
|
"loading": "Loading workspaces…",
|
|
37
40
|
"refreshError": "Couldn’t refresh workspaces: {error}",
|
|
38
41
|
"roleOwner": "Owner",
|
|
@@ -33,6 +33,9 @@
|
|
|
33
33
|
"deleteWorkspacesTitle": "{count, plural, one {Supprimer {count} espace de travail ?} many {Supprimer {count} espaces de travail ?} other {Supprimer {count} espaces de travail ?}}",
|
|
34
34
|
"deleteTitle": "Supprimer l’espace de travail ?",
|
|
35
35
|
"loadError": "Impossible de charger les espaces de travail.",
|
|
36
|
+
"loadMore": "Charger plus",
|
|
37
|
+
"loadMoreError": "Impossible de charger d’autres espaces de travail : {error}",
|
|
38
|
+
"partial": "{shown} espaces de travail affichés sur {total}",
|
|
36
39
|
"loading": "Chargement des espaces de travail…",
|
|
37
40
|
"refreshError": "Impossible d’actualiser les espaces de travail : {error}",
|
|
38
41
|
"roleOwner": "Propriétaire",
|
|
@@ -33,6 +33,9 @@
|
|
|
33
33
|
"deleteWorkspacesTitle": "{count, plural, other {{count} 件のワークスペースを削除しますか?}}",
|
|
34
34
|
"deleteTitle": "ワークスペースを削除しますか?",
|
|
35
35
|
"loadError": "ワークスペースを読み込めませんでした。",
|
|
36
|
+
"loadMore": "さらに読み込む",
|
|
37
|
+
"loadMoreError": "ワークスペースをさらに読み込めませんでした: {error}",
|
|
38
|
+
"partial": "{total} 件中 {shown} 件のワークスペースを表示",
|
|
36
39
|
"loading": "ワークスペースを読み込んでいます…",
|
|
37
40
|
"refreshError": "ワークスペースを更新できませんでした: {error}",
|
|
38
41
|
"roleOwner": "所有者",
|
|
@@ -198,6 +198,7 @@ declare const WorkspacesStore: _angular_core.Type<{
|
|
|
198
198
|
documentPagination: _angular_core.Signal<Record<string, Pagination>>;
|
|
199
199
|
selectedIds: _angular_core.Signal<string[]>;
|
|
200
200
|
hasMore: _angular_core.Signal<boolean>;
|
|
201
|
+
truncated: _angular_core.Signal<boolean>;
|
|
201
202
|
selectedCount: _angular_core.Signal<number>;
|
|
202
203
|
selectedIdSet: _angular_core.Signal<Set<string>>;
|
|
203
204
|
workspace: (id: string) => Workspace | undefined;
|
|
@@ -883,6 +884,7 @@ declare class WorkspaceList {
|
|
|
883
884
|
documentPagination: _angular_core.Signal<Record<string, _sinequa_atomic_angular_document_workspaces.Pagination>>;
|
|
884
885
|
selectedIds: _angular_core.Signal<string[]>;
|
|
885
886
|
hasMore: _angular_core.Signal<boolean>;
|
|
887
|
+
truncated: _angular_core.Signal<boolean>;
|
|
886
888
|
selectedCount: _angular_core.Signal<number>;
|
|
887
889
|
selectedIdSet: _angular_core.Signal<Set<string>>;
|
|
888
890
|
workspace: (id: string) => Workspace | undefined;
|
|
@@ -924,6 +926,8 @@ declare class WorkspaceList {
|
|
|
924
926
|
protected readonly status: _angular_core.WritableSignal<"error" | "loading" | "ready">;
|
|
925
927
|
protected readonly loadError: _angular_core.WritableSignal<Message | null>;
|
|
926
928
|
protected readonly refreshError: _angular_core.WritableSignal<string | null>;
|
|
929
|
+
protected readonly loadingMore: _angular_core.WritableSignal<boolean>;
|
|
930
|
+
protected readonly loadMoreError: _angular_core.WritableSignal<string | null>;
|
|
927
931
|
protected readonly filter: {
|
|
928
932
|
tab: _angular_core.WritableSignal<WorkspaceTab>;
|
|
929
933
|
query: _angular_core.WritableSignal<string>;
|
|
@@ -936,6 +940,7 @@ declare class WorkspaceList {
|
|
|
936
940
|
/** Shows what is cached at once and refreshes behind it, or loads for the first time. */
|
|
937
941
|
protected load(): Promise<void>;
|
|
938
942
|
private refresh;
|
|
943
|
+
protected loadMore(): Promise<void>;
|
|
939
944
|
protected onSearch(event: Event): void;
|
|
940
945
|
protected deleteSelected(): void;
|
|
941
946
|
/**
|
|
@@ -1894,7 +1899,7 @@ declare class UploadTracker {
|
|
|
1894
1899
|
/** What the strip of a minimized panel shows, which is not always what it should: a change swaps it. */
|
|
1895
1900
|
protected readonly peekShown: _angular_core.WritableSignal<Message>;
|
|
1896
1901
|
protected readonly peekTone: _angular_core.WritableSignal<"error" | "success" | null>;
|
|
1897
|
-
protected readonly peekSwap: _angular_core.WritableSignal<"
|
|
1902
|
+
protected readonly peekSwap: _angular_core.WritableSignal<"hidden" | "shown" | "below">;
|
|
1898
1903
|
private peekKey;
|
|
1899
1904
|
private hoverTimer;
|
|
1900
1905
|
private readonly timers;
|
|
@@ -2,7 +2,7 @@ import * as i0 from '@angular/core';
|
|
|
2
2
|
import { computed, signal, Injectable, InjectionToken, inject, DestroyRef, assertInInjectionContext, effect, untracked, output, input, booleanAttribute, Directive, model, Pipe, viewChild, ChangeDetectionStrategy, Component, ElementRef, ViewEncapsulation } from '@angular/core';
|
|
3
3
|
import { withDevtools } from '@angular-architects/ngrx-toolkit';
|
|
4
4
|
import { signalStore, withState, withComputed, withMethods, patchState } from '@ngrx/signals';
|
|
5
|
-
import { listContainers, addContainerDocuments, deleteContainerDocuments,
|
|
5
|
+
import { listContainerDocuments, listContainers, addContainerDocuments, deleteContainerDocuments, deleteContainers, updateContainer, createContainer, getContainer, fetchPrincipal, globalConfig, tryAutoAuthentication, createHeaders, onAuthEvent, ApiError, fetchUploadStatus, searchPrincipals, fetchPrincipalsByIds, fetchPreview } from '@sinequa/atomic';
|
|
6
6
|
import { Upload } from 'tus-js-client';
|
|
7
7
|
import { toSignal } from '@angular/core/rxjs-interop';
|
|
8
8
|
import { TranslocoService, TranslocoPipe, provideTranslocoScope } from '@jsverse/transloco';
|
|
@@ -102,6 +102,11 @@ function mapContainerDocumentToWorkspaceDocument(dto) {
|
|
|
102
102
|
|
|
103
103
|
/** API maximum page size. */
|
|
104
104
|
const PAGE_SIZE = 100;
|
|
105
|
+
/**
|
|
106
|
+
* Most workspaces the list keeps loaded: "load more" stops there, so the list never puts an
|
|
107
|
+
* unbounded number of cards in the DOM. Past it, {@link WorkspacesStore.truncated} is true.
|
|
108
|
+
*/
|
|
109
|
+
const MAX_LOADED_WORKSPACES = 3 * PAGE_SIZE;
|
|
105
110
|
const initialState$1 = {
|
|
106
111
|
workspaces: [],
|
|
107
112
|
loaded: false,
|
|
@@ -131,19 +136,56 @@ function withDocumentCountAdjusted(workspaces, id, delta) {
|
|
|
131
136
|
* pre-transfer cache update.
|
|
132
137
|
*/
|
|
133
138
|
const WorkspacesStore = signalStore({ providedIn: "root" }, withDevtools("Workspaces"), withState(initialState$1), withComputed(({ workspaces, pagination, selectedIds }) => ({
|
|
134
|
-
hasMore: computed(() => workspaces().length < pagination().total),
|
|
139
|
+
hasMore: computed(() => workspaces().length < pagination().total && workspaces().length < MAX_LOADED_WORKSPACES),
|
|
140
|
+
/** The server holds more workspaces than the list is allowed to load. */
|
|
141
|
+
truncated: computed(() => workspaces().length < pagination().total && workspaces().length >= MAX_LOADED_WORKSPACES),
|
|
135
142
|
selectedCount: computed(() => selectedIds().length),
|
|
136
143
|
selectedIdSet: computed(() => new Set(selectedIds()))
|
|
137
144
|
})), withMethods(store => {
|
|
138
145
|
let loadingPromise = null;
|
|
146
|
+
let loadingMorePromise = null;
|
|
139
147
|
const setDocuments = (workspaceId, documents) => {
|
|
140
148
|
patchState(store, state => ({ documents: { ...state.documents, [workspaceId]: documents } }));
|
|
141
149
|
};
|
|
142
150
|
// Counts are moved by a delta rather than reset to the cached list's length:
|
|
143
151
|
// the cache holds at most the pages loaded so far, while `documentCount` is
|
|
144
152
|
// the server's total.
|
|
153
|
+
// The same goes for the documents' pagination total, which `hasMoreDocuments` compares
|
|
154
|
+
// to the cached list: left as it was loaded, a workspace emptied of its documents would
|
|
155
|
+
// still offer to load more.
|
|
145
156
|
const adjustDocumentCount = (workspaceId, delta) => {
|
|
146
|
-
patchState(store, state =>
|
|
157
|
+
patchState(store, state => {
|
|
158
|
+
const pagination = state.documentPagination[workspaceId];
|
|
159
|
+
return {
|
|
160
|
+
workspaces: withDocumentCountAdjusted(state.workspaces, workspaceId, delta),
|
|
161
|
+
documentPagination: pagination
|
|
162
|
+
? { ...state.documentPagination, [workspaceId]: { ...pagination, total: Math.max(0, pagination.total + delta) } }
|
|
163
|
+
: state.documentPagination
|
|
164
|
+
};
|
|
165
|
+
});
|
|
166
|
+
};
|
|
167
|
+
const loadDocuments = async (workspaceId) => {
|
|
168
|
+
const res = await listContainerDocuments(workspaceId, { page: 1, pageSize: PAGE_SIZE });
|
|
169
|
+
const documents = res.documents.map(mapContainerDocumentToWorkspaceDocument);
|
|
170
|
+
patchState(store, state => ({
|
|
171
|
+
documentPagination: { ...state.documentPagination, [workspaceId]: { page: res.page, total: res.total } }
|
|
172
|
+
}));
|
|
173
|
+
setDocuments(workspaceId, documents);
|
|
174
|
+
return documents;
|
|
175
|
+
};
|
|
176
|
+
// Deleting everything that was loaded does not mean the workspace is empty: the server may hold
|
|
177
|
+
// pages that were never loaded. Show them rather than an empty state next to a "load more".
|
|
178
|
+
// The deletion already succeeded, so a failed refill is not an error here: the cache stays empty
|
|
179
|
+
// and "load more" (which then asks for the first page) lets the user retry.
|
|
180
|
+
const refillIfEmptied = async (workspaceId) => {
|
|
181
|
+
if ((store.documents()[workspaceId] ?? []).length || !(store.documentPagination()[workspaceId]?.total ?? 0))
|
|
182
|
+
return;
|
|
183
|
+
try {
|
|
184
|
+
await loadDocuments(workspaceId);
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
// see above
|
|
188
|
+
}
|
|
147
189
|
};
|
|
148
190
|
const load = async () => {
|
|
149
191
|
patchState(store, { loading: true });
|
|
@@ -194,15 +236,30 @@ const WorkspacesStore = signalStore({ providedIn: "root" }, withDevtools("Worksp
|
|
|
194
236
|
loadingPromise ??= load();
|
|
195
237
|
return loadingPromise;
|
|
196
238
|
},
|
|
197
|
-
|
|
239
|
+
/** Shares an in-flight call, so a double click does not fetch (and append) the same page twice. */
|
|
240
|
+
loadMore() {
|
|
198
241
|
if (!store.hasMore())
|
|
199
|
-
return store.workspaces();
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
242
|
+
return Promise.resolve(store.workspaces());
|
|
243
|
+
loadingMorePromise ??= (async () => {
|
|
244
|
+
try {
|
|
245
|
+
const current = store.workspaces();
|
|
246
|
+
// Deleting workspaces shifts the server's pages: `page + 1` would skip as many as were
|
|
247
|
+
// deleted since. Ask for the page holding the first one not loaded yet (it may overlap
|
|
248
|
+
// the loaded tail, hence the dedup).
|
|
249
|
+
const page = Math.min(store.pagination().page + 1, Math.floor(current.length / PAGE_SIZE) + 1);
|
|
250
|
+
const res = await listContainers({ page, pageSize: PAGE_SIZE });
|
|
251
|
+
patchState(store, state => {
|
|
252
|
+
const loaded = new Set(state.workspaces.map(w => w.id));
|
|
253
|
+
const fresh = res.containers.map(mapContainerToWorkspace).filter(w => !loaded.has(w.id));
|
|
254
|
+
return { workspaces: [...state.workspaces, ...fresh], pagination: { page: res.page, total: res.total } };
|
|
255
|
+
});
|
|
256
|
+
return store.workspaces();
|
|
257
|
+
}
|
|
258
|
+
finally {
|
|
259
|
+
loadingMorePromise = null;
|
|
260
|
+
}
|
|
261
|
+
})();
|
|
262
|
+
return loadingMorePromise;
|
|
206
263
|
},
|
|
207
264
|
/**
|
|
208
265
|
* Fallback for a deep link opened before the list was loaded, or for an id
|
|
@@ -220,6 +277,8 @@ const WorkspacesStore = signalStore({ providedIn: "root" }, withDevtools("Worksp
|
|
|
220
277
|
const workspace = mapContainerToWorkspace(await createContainer({ name, description, tags, owners, readers }));
|
|
221
278
|
patchState(store, state => ({
|
|
222
279
|
workspaces: [...state.workspaces, workspace],
|
|
280
|
+
// The server's total grew by one too: left alone, a full list would offer to load a phantom workspace.
|
|
281
|
+
pagination: { ...state.pagination, total: state.pagination.total + 1 },
|
|
223
282
|
documents: { ...state.documents, [workspace.id]: [] }
|
|
224
283
|
}));
|
|
225
284
|
return workspace;
|
|
@@ -248,28 +307,28 @@ const WorkspacesStore = signalStore({ providedIn: "root" }, withDevtools("Worksp
|
|
|
248
307
|
const succeeded = new Set(result.succeeded);
|
|
249
308
|
patchState(store, state => ({
|
|
250
309
|
workspaces: state.workspaces.filter(w => !succeeded.has(w.id)),
|
|
310
|
+
// Followed like the documents' total: an emptied list must not offer to load more.
|
|
311
|
+
pagination: { ...state.pagination, total: Math.max(0, state.pagination.total - result.succeeded.length) },
|
|
251
312
|
documents: Object.fromEntries(Object.entries(state.documents).filter(([id]) => !succeeded.has(id))),
|
|
252
313
|
selectedIds: state.selectedIds.filter(id => !succeeded.has(id))
|
|
253
314
|
}));
|
|
254
315
|
return { succeeded: result.succeeded, failed: result.failed.map(f => ({ id: f.containerId, error: f.error })) };
|
|
255
316
|
},
|
|
256
|
-
|
|
257
|
-
const res = await listContainerDocuments(workspaceId, { page: 1, pageSize: PAGE_SIZE });
|
|
258
|
-
const documents = res.documents.map(mapContainerDocumentToWorkspaceDocument);
|
|
259
|
-
patchState(store, state => ({
|
|
260
|
-
documentPagination: { ...state.documentPagination, [workspaceId]: { page: res.page, total: res.total } }
|
|
261
|
-
}));
|
|
262
|
-
setDocuments(workspaceId, documents);
|
|
263
|
-
return documents;
|
|
264
|
-
},
|
|
317
|
+
loadDocuments,
|
|
265
318
|
/** Without this, a workspace with more than one page of documents is silently truncated. */
|
|
266
319
|
async loadMoreDocuments(workspaceId) {
|
|
267
320
|
const current = store.documents()[workspaceId] ?? [];
|
|
268
321
|
const pagination = store.documentPagination()[workspaceId];
|
|
269
322
|
if (!pagination || current.length >= pagination.total)
|
|
270
323
|
return current;
|
|
271
|
-
|
|
272
|
-
|
|
324
|
+
// Deleting documents shifts the server's pages: `pagination.page + 1` would skip as many
|
|
325
|
+
// documents as were deleted since. Ask for the page holding the first one not loaded yet
|
|
326
|
+
// (it may overlap the loaded tail, hence the dedup).
|
|
327
|
+
const page = Math.min(pagination.page + 1, Math.floor(current.length / PAGE_SIZE) + 1);
|
|
328
|
+
const res = await listContainerDocuments(workspaceId, { page, pageSize: PAGE_SIZE });
|
|
329
|
+
const loaded = new Set(current.map(d => d.id));
|
|
330
|
+
const fresh = res.documents.map(mapContainerDocumentToWorkspaceDocument).filter(d => !loaded.has(d.id));
|
|
331
|
+
const merged = [...current, ...fresh];
|
|
273
332
|
patchState(store, state => ({
|
|
274
333
|
documentPagination: { ...state.documentPagination, [workspaceId]: { page: res.page, total: res.total } }
|
|
275
334
|
}));
|
|
@@ -322,6 +381,7 @@ const WorkspacesStore = signalStore({ providedIn: "root" }, withDevtools("Worksp
|
|
|
322
381
|
const after = before.filter(d => !documentIds.includes(d.id));
|
|
323
382
|
setDocuments(workspaceId, after);
|
|
324
383
|
adjustDocumentCount(workspaceId, after.length - before.length);
|
|
384
|
+
await refillIfEmptied(workspaceId);
|
|
325
385
|
},
|
|
326
386
|
/**
|
|
327
387
|
* "Move to workspace". There is no dedicated re-parent endpoint, so this
|
|
@@ -341,6 +401,7 @@ const WorkspacesStore = signalStore({ providedIn: "root" }, withDevtools("Worksp
|
|
|
341
401
|
const remaining = from.filter(d => !documentIds.includes(d.id));
|
|
342
402
|
setDocuments(fromWorkspaceId, remaining);
|
|
343
403
|
adjustDocumentCount(fromWorkspaceId, remaining.length - from.length);
|
|
404
|
+
await refillIfEmptied(fromWorkspaceId);
|
|
344
405
|
},
|
|
345
406
|
toggleSelected(id, checked) {
|
|
346
407
|
patchState(store, state => {
|
|
@@ -3017,6 +3078,8 @@ class WorkspaceList {
|
|
|
3017
3078
|
status = signal("loading", ...(ngDevMode ? [{ debugName: "status" }] : []));
|
|
3018
3079
|
loadError = signal(null, ...(ngDevMode ? [{ debugName: "loadError" }] : []));
|
|
3019
3080
|
refreshError = signal(null, ...(ngDevMode ? [{ debugName: "refreshError" }] : []));
|
|
3081
|
+
loadingMore = signal(false, ...(ngDevMode ? [{ debugName: "loadingMore" }] : []));
|
|
3082
|
+
loadMoreError = signal(null, ...(ngDevMode ? [{ debugName: "loadMoreError" }] : []));
|
|
3020
3083
|
filter = injectWorkspaceFilter(this.store.workspaces);
|
|
3021
3084
|
// A workspace shared read-only cannot be deleted: it is not offered for selection either, so the
|
|
3022
3085
|
// count, "select all" and the Delete button never include one.
|
|
@@ -3057,6 +3120,21 @@ class WorkspaceList {
|
|
|
3057
3120
|
this.refreshError.set(this.translate(workspaceErrorMessage(error)));
|
|
3058
3121
|
}
|
|
3059
3122
|
}
|
|
3123
|
+
async loadMore() {
|
|
3124
|
+
if (this.loadingMore())
|
|
3125
|
+
return;
|
|
3126
|
+
this.loadMoreError.set(null);
|
|
3127
|
+
this.loadingMore.set(true);
|
|
3128
|
+
try {
|
|
3129
|
+
await this.store.loadMore();
|
|
3130
|
+
}
|
|
3131
|
+
catch (error) {
|
|
3132
|
+
this.loadMoreError.set(this.translate(workspaceErrorMessage(error)));
|
|
3133
|
+
}
|
|
3134
|
+
finally {
|
|
3135
|
+
this.loadingMore.set(false);
|
|
3136
|
+
}
|
|
3137
|
+
}
|
|
3060
3138
|
onSearch(event) {
|
|
3061
3139
|
this.filter.query.set(event.target.value);
|
|
3062
3140
|
}
|
|
@@ -3101,7 +3179,7 @@ class WorkspaceList {
|
|
|
3101
3179
|
return "key" in message ? this.transloco.translate(message.key, message.params) : message.text;
|
|
3102
3180
|
}
|
|
3103
3181
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.29", ngImport: i0, type: WorkspaceList, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3104
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.29", type: WorkspaceList, isStandalone: true, selector: "workspace-list", outputs: { opened: "opened", createRequested: "createRequested" }, host: { classAttribute: "@container flex h-full min-h-0 flex-col p-3 pb-2 md:p-8 md:pb-4" }, providers: [provideTranslocoScope("document-workspaces")], ngImport: i0, template: "\n <div class=\"mb-1 flex items-center justify-between gap-4\">\n <h1 class=\"text-2xl leading-[31px] font-bold text-(--font-neutral-black)\">{{ \"documentWorkspaces.list.title\" | transloco }}</h1>\n <button size=\"sm\" variant=\"primary\" class=\"gap-1.5 rounded-full\" (click)=\"createRequested.emit()\">\n <plus-icon />\n {{ \"documentWorkspaces.list.addWorkspace\" | transloco }}\n </button>\n </div>\n <p class=\"mb-6 text-base text-(--font-primary-base)\">{{ \"documentWorkspaces.list.subtitle\" | transloco }}</p>\n\n <div role=\"tablist\" class=\"mb-3 flex w-fit max-w-full overflow-x-auto border-b border-(--stroke-primary-lighter)\">\n @for (item of tabs; track item.tab) {\n <button\n variant=\"none\"\n role=\"tab\"\n type=\"button\"\n class=\"flex h-11 items-center gap-1 rounded-t-lg border-b-2 border-transparent px-3 text-sm font-semibold whitespace-nowrap text-(--font-primary-base) hover:bg-(--bg-primary-lightest) aria-selected:border-(--stroke-primary-base) aria-selected:bg-(--bg-neutral-white)\"\n [attr.aria-selected]=\"filter.tab() === item.tab\"\n (click)=\"filter.tab.set(item.tab)\">\n @switch (item.tab) {\n @case (\"editor\") {\n <user-icon class=\"size-4\" />\n }\n @case (\"reader\") {\n <user-round-icon class=\"size-4\" />\n }\n }\n {{ item.labelKey | transloco }}\n <span\n class=\"inline-flex h-4 min-w-4 items-center justify-center rounded-full bg-(--bg-primary-light) px-1 text-[10px] leading-[10px] font-semibold\"\n [attr.data-testid]=\"'badge-' + item.tab\"\n >{{ filter.counts()[item.tab] }}</span\n >\n </button>\n }\n </div>\n\n <div class=\"mb-6 flex flex-wrap items-center gap-2\">\n <div role=\"group\" class=\"inline-flex gap-1 rounded-full bg-(--bg-primary-lighter) p-0.5\">\n <button\n variant=\"none\"\n class=\"inline-flex size-6 items-center justify-center rounded-full text-(--font-primary-base) aria-pressed:bg-(--bg-neutral-white) aria-pressed:shadow-md\"\n [attr.aria-pressed]=\"viewMode() === 'grid'\"\n [attr.aria-label]=\"'documentWorkspaces.common.gridView' | transloco\"\n [tooltip]=\"'documentWorkspaces.common.gridView' | transloco\"\n (click)=\"viewMode.set('grid')\">\n <grid-icon class=\"size-3.5\" />\n </button>\n <button\n variant=\"none\"\n class=\"inline-flex size-6 items-center justify-center rounded-full text-(--font-primary-base) aria-pressed:bg-(--bg-neutral-white) aria-pressed:shadow-md\"\n [attr.aria-pressed]=\"viewMode() === 'list'\"\n [attr.aria-label]=\"'documentWorkspaces.common.listView' | transloco\"\n [tooltip]=\"'documentWorkspaces.common.listView' | transloco\"\n (click)=\"viewMode.set('list')\">\n <list-icon class=\"size-3.5\" />\n </button>\n </div>\n\n <div\n role=\"search\"\n class=\"flex h-9 min-w-32 flex-[0_1_15rem] items-center gap-2 rounded-full border border-(--stroke-primary-light) bg-(--bg-neutral-white) px-3 focus-within:border-(--stroke-primary-base)\">\n <magnifying-glass-icon class=\"size-4 shrink-0 text-(--font-primary-muted)\" />\n <input\n type=\"text\"\n class=\"min-w-0 flex-1 bg-transparent text-xs text-(--font-neutral-black) outline-none placeholder:text-(--font-primary-muted)\"\n [value]=\"filter.query()\"\n [attr.placeholder]=\"'documentWorkspaces.list.searchPlaceholder' | transloco\"\n [attr.aria-label]=\"'documentWorkspaces.list.searchPlaceholder' | transloco\"\n (input)=\"onSearch($event)\" />\n @if (filter.query()) {\n <button variant=\"none\" type=\"button\" class=\"inline-flex\" [attr.aria-label]=\"'documentWorkspaces.common.clearSearch' | transloco\" (click)=\"filter.clearQuery()\">\n <x-mark-icon class=\"size-4\" />\n </button>\n }\n </div>\n\n @if (selection.selectableItems().length) {\n @if (selection.active()) {\n <div class=\"ml-auto flex items-center gap-2 @max-xl:order-last @max-xl:ml-0 @max-xl:basis-full\" data-testid=\"selection-bar\">\n <span class=\"text-sm text-(--font-primary-base)\" aria-live=\"polite\">{{ \"documentWorkspaces.list.selectedCount\" | transloco: { count: selection.count() } }}</span>\n <button variant=\"ghost\" size=\"xs\" (click)=\"selection.toggleAll()\">\n {{ (selection.allSelected() ? \"documentWorkspaces.common.unselectAll\" : \"documentWorkspaces.common.selectAll\") | transloco }}\n </button>\n <button class=\"gap-1.5 text-(--font-error-base) hover:bg-(--bg-error-lightest)\" variant=\"ghost\" size=\"xs\" [disabled]=\"!selection.count()\" (click)=\"deleteSelected()\">\n <trash-icon />\n {{ \"documentWorkspaces.common.delete\" | transloco }}\n </button>\n </div>\n }\n <button variant=\"ghost\" size=\"xs\" class=\"@max-xl:ml-auto\" [class.ml-auto]=\"!selection.active()\" (click)=\"selection.toggleMode()\">\n {{ (selection.active() ? \"documentWorkspaces.common.cancel\" : \"documentWorkspaces.common.select\") | transloco }}\n </button>\n }\n </div>\n\n @if (refreshError(); as message) {\n <p role=\"alert\" class=\"text-sm text-(--font-error-base)\">{{ \"documentWorkspaces.list.refreshError\" | transloco: { error: message } }}</p>\n }\n\n <!-- Only the workspaces scroll: the title, the tabs and the toolbar stay in place. -->\n <div class=\"min-h-0 flex-1 overflow-y-auto\">\n @switch (status()) {\n @case (\"loading\") {\n <p class=\"text-sm text-(--font-primary-muted)\">{{ \"documentWorkspaces.list.loading\" | transloco }}</p>\n }\n @case (\"error\") {\n <div class=\"flex items-center gap-3 text-sm\" data-testid=\"load-error\">\n <span class=\"text-(--font-error-base)\">\n @if (loadError(); as message) {\n <workspace-message [message]=\"message\" />\n } @else {\n {{ \"documentWorkspaces.list.loadError\" | transloco }}\n }\n </span>\n <button variant=\"secondary\" size=\"xs\" (click)=\"load()\">{{ \"documentWorkspaces.common.retry\" | transloco }}</button>\n </div>\n }\n @default {\n @if (!filter.filtered().length) {\n <div class=\"flex flex-col items-center gap-1 py-8 text-center\" data-testid=\"empty\">\n <!-- The illustration is for a list with nothing in it, not for a search that found nothing. -->\n @if (!store.workspaces().length) {\n <empty-folder-icon />\n }\n <p class=\"text-sm text-(--font-primary-muted)\">\n {{ (store.workspaces().length ? \"documentWorkspaces.list.noMatch\" : \"documentWorkspaces.list.empty\") | transloco }}\n </p>\n </div>\n } @else if (viewMode() === \"grid\") {\n <div class=\"grid grid-cols-[repeat(auto-fill,minmax(300px,1fr))] content-start gap-4 pb-8\">\n @for (workspace of filter.filtered(); track workspace.id) {\n <workspace-card\n [workspace]=\"workspace\"\n [query]=\"filter.query()\"\n [selecting]=\"selection.active()\"\n [selected]=\"selection.isSelected(workspace)\"\n [selectable]=\"selection.canSelect(workspace)\"\n (opened)=\"opened.emit(workspace)\"\n (deleteRequested)=\"confirmDelete([workspace])\"\n (selectedChange)=\"selection.toggle(workspace, $event)\" />\n }\n </div>\n } @else {\n <div role=\"table\" class=\"flex flex-col gap-3 pb-8\">\n <div role=\"row\" [attr.data-selecting]=\"selection.active() ? '' : null\" class=\"grid grid-cols-[minmax(0,1fr)_auto_auto_2.5rem] items-center gap-x-4 px-3 data-selecting:grid-cols-[2rem_minmax(0,1fr)_auto_auto_2.5rem] @xl:grid-cols-[minmax(0,1fr)_4.5rem_6.5rem_6.5rem_2.5rem] @xl:data-selecting:grid-cols-[2rem_minmax(0,1fr)_4.5rem_6.5rem_6.5rem_2.5rem] @3xl:grid-cols-[minmax(0,2fr)_5.5rem_7.5rem_9rem_minmax(5rem,1fr)_2.5rem] @3xl:data-selecting:grid-cols-[2rem_minmax(0,2fr)_5.5rem_7.5rem_9rem_minmax(5rem,1fr)_2.5rem] py-1 text-xs font-semibold text-(--font-primary-muted)\">\n @if (selection.active()) {\n <div role=\"columnheader\">\n <input\n type=\"checkbox\"\n class=\"accent-(--bg-primary-base) size-4\"\n [checked]=\"selection.allSelected()\"\n [indeterminate]=\"selection.partiallySelected()\"\n [attr.aria-label]=\"'documentWorkspaces.common.selectAll' | transloco\"\n (change)=\"selection.toggleAll()\" />\n\n </div>\n }\n <div role=\"columnheader\">{{ \"documentWorkspaces.list.columnName\" | transloco }}</div>\n <div role=\"columnheader\">{{ \"documentWorkspaces.list.columnContent\" | transloco }}</div>\n <div role=\"columnheader\">{{ \"documentWorkspaces.list.columnOwner\" | transloco }}</div>\n <div role=\"columnheader\" class=\"hidden @xl:block\">{{ \"documentWorkspaces.list.columnDateUpdated\" | transloco }}</div>\n <div role=\"columnheader\" class=\"hidden @3xl:block\">{{ \"documentWorkspaces.list.columnTags\" | transloco }}</div>\n <div role=\"columnheader\"></div>\n </div>\n @for (workspace of filter.filtered(); track workspace.id) {\n <workspace-row\n [workspace]=\"workspace\"\n [query]=\"filter.query()\"\n [selecting]=\"selection.active()\"\n [selected]=\"selection.isSelected(workspace)\"\n [selectable]=\"selection.canSelect(workspace)\"\n (opened)=\"opened.emit(workspace)\"\n (deleteRequested)=\"confirmDelete([workspace])\"\n (selectedChange)=\"selection.toggle(workspace, $event)\" />\n }\n </div>\n }\n }\n }\n </div>\n ", isInline: true, dependencies: [{ kind: "directive", type: ButtonComponent, selector: "button", inputs: ["class", "variant", "decoration", "scheme", "iconOnly", "size", "solid"] }, { kind: "component", type: EmptyFolderIcon, selector: "empty-folder-icon" }, { kind: "component", type: GridIcon, selector: "grid-icon", inputs: ["class"] }, { kind: "component", type: ListIcon, selector: "list-icon, ListIcon", inputs: ["class"] }, { kind: "component", type: MagnifyingGlassIcon, selector: "magnifying-glass-icon, MagnifyingGlassIcon", inputs: ["class"] }, { kind: "component", type: PlusIcon, selector: "plus-icon, PlusIcon", inputs: ["class"] }, { kind: "directive", type: TooltipDirective, selector: "[tooltip]", inputs: ["tooltip", "strategy", "tooltip-position", "tooltip-delay", "tooltip-duration", "tooltip-offset"] }, { kind: "component", type: TrashIcon, selector: "trash-icon, TrashIcon", inputs: ["class"] }, { kind: "component", type: UserIcon, selector: "user-icon, UserIcon", inputs: ["class"] }, { kind: "component", type: UserRoundIcon, selector: "user-round, UserRound, userround, user-round-icon, UserRoundIcon, userroundicon", inputs: ["class"] }, { kind: "component", type: WorkspaceCard, selector: "workspace-card", inputs: ["workspace", "query", "selecting", "selected", "selectable"], outputs: ["opened", "deleteRequested", "selectedChange"] }, { kind: "component", type: WorkspaceMessage, selector: "workspace-message", inputs: ["message"] }, { kind: "component", type: WorkspaceRow, selector: "workspace-row", inputs: ["workspace", "query", "selecting", "selected", "selectable"], outputs: ["opened", "deleteRequested", "selectedChange"] }, { kind: "component", type: XMarkIcon, selector: "XMarkIcon, xmark-icon, x-mark-icon", inputs: ["class"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
3182
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.29", type: WorkspaceList, isStandalone: true, selector: "workspace-list", outputs: { opened: "opened", createRequested: "createRequested" }, host: { classAttribute: "@container flex h-full min-h-0 flex-col p-3 pb-2 md:p-8 md:pb-4" }, providers: [provideTranslocoScope("document-workspaces")], ngImport: i0, template: "\n <div class=\"mb-1 flex items-center justify-between gap-4\">\n <h1 class=\"text-2xl leading-[31px] font-bold text-(--font-neutral-black)\">{{ \"documentWorkspaces.list.title\" | transloco }}</h1>\n <button size=\"sm\" variant=\"primary\" class=\"gap-1.5 rounded-full\" (click)=\"createRequested.emit()\">\n <plus-icon />\n {{ \"documentWorkspaces.list.addWorkspace\" | transloco }}\n </button>\n </div>\n <p class=\"mb-6 text-base text-(--font-primary-base)\">{{ \"documentWorkspaces.list.subtitle\" | transloco }}</p>\n\n <div role=\"tablist\" class=\"mb-3 flex w-fit max-w-full overflow-x-auto border-b border-(--stroke-primary-lighter)\">\n @for (item of tabs; track item.tab) {\n <button\n variant=\"none\"\n role=\"tab\"\n type=\"button\"\n class=\"flex h-11 items-center gap-1 rounded-t-lg border-b-2 border-transparent px-3 text-sm font-semibold whitespace-nowrap text-(--font-primary-base) hover:bg-(--bg-primary-lightest) aria-selected:border-(--stroke-primary-base) aria-selected:bg-(--bg-neutral-white)\"\n [attr.aria-selected]=\"filter.tab() === item.tab\"\n (click)=\"filter.tab.set(item.tab)\">\n @switch (item.tab) {\n @case (\"editor\") {\n <user-icon class=\"size-4\" />\n }\n @case (\"reader\") {\n <user-round-icon class=\"size-4\" />\n }\n }\n {{ item.labelKey | transloco }}\n <span\n class=\"inline-flex h-4 min-w-4 items-center justify-center rounded-full bg-(--bg-primary-light) px-1 text-[10px] leading-[10px] font-semibold\"\n [attr.data-testid]=\"'badge-' + item.tab\"\n >{{ filter.counts()[item.tab] }}</span\n >\n </button>\n }\n </div>\n\n <div class=\"mb-6 flex flex-wrap items-center gap-2\">\n <div role=\"group\" class=\"inline-flex gap-1 rounded-full bg-(--bg-primary-lighter) p-0.5\">\n <button\n variant=\"none\"\n class=\"inline-flex size-6 items-center justify-center rounded-full text-(--font-primary-base) aria-pressed:bg-(--bg-neutral-white) aria-pressed:shadow-md\"\n [attr.aria-pressed]=\"viewMode() === 'grid'\"\n [attr.aria-label]=\"'documentWorkspaces.common.gridView' | transloco\"\n [tooltip]=\"'documentWorkspaces.common.gridView' | transloco\"\n (click)=\"viewMode.set('grid')\">\n <grid-icon class=\"size-3.5\" />\n </button>\n <button\n variant=\"none\"\n class=\"inline-flex size-6 items-center justify-center rounded-full text-(--font-primary-base) aria-pressed:bg-(--bg-neutral-white) aria-pressed:shadow-md\"\n [attr.aria-pressed]=\"viewMode() === 'list'\"\n [attr.aria-label]=\"'documentWorkspaces.common.listView' | transloco\"\n [tooltip]=\"'documentWorkspaces.common.listView' | transloco\"\n (click)=\"viewMode.set('list')\">\n <list-icon class=\"size-3.5\" />\n </button>\n </div>\n\n <div\n role=\"search\"\n class=\"flex h-9 min-w-32 flex-[0_1_15rem] items-center gap-2 rounded-full border border-(--stroke-primary-light) bg-(--bg-neutral-white) px-3 focus-within:border-(--stroke-primary-base)\">\n <magnifying-glass-icon class=\"size-4 shrink-0 text-(--font-primary-muted)\" />\n <input\n type=\"text\"\n class=\"min-w-0 flex-1 bg-transparent text-xs text-(--font-neutral-black) outline-none placeholder:text-(--font-primary-muted)\"\n [value]=\"filter.query()\"\n [attr.placeholder]=\"'documentWorkspaces.list.searchPlaceholder' | transloco\"\n [attr.aria-label]=\"'documentWorkspaces.list.searchPlaceholder' | transloco\"\n (input)=\"onSearch($event)\" />\n @if (filter.query()) {\n <button variant=\"none\" type=\"button\" class=\"inline-flex\" [attr.aria-label]=\"'documentWorkspaces.common.clearSearch' | transloco\" (click)=\"filter.clearQuery()\">\n <x-mark-icon class=\"size-4\" />\n </button>\n }\n </div>\n\n @if (selection.selectableItems().length) {\n @if (selection.active()) {\n <div class=\"ml-auto flex items-center gap-2 @max-xl:order-last @max-xl:ml-0 @max-xl:basis-full\" data-testid=\"selection-bar\">\n <span class=\"text-sm text-(--font-primary-base)\" aria-live=\"polite\">{{ \"documentWorkspaces.list.selectedCount\" | transloco: { count: selection.count() } }}</span>\n <button variant=\"ghost\" size=\"xs\" (click)=\"selection.toggleAll()\">\n {{ (selection.allSelected() ? \"documentWorkspaces.common.unselectAll\" : \"documentWorkspaces.common.selectAll\") | transloco }}\n </button>\n <button class=\"gap-1.5 text-(--font-error-base) hover:bg-(--bg-error-lightest)\" variant=\"ghost\" size=\"xs\" [disabled]=\"!selection.count()\" (click)=\"deleteSelected()\">\n <trash-icon />\n {{ \"documentWorkspaces.common.delete\" | transloco }}\n </button>\n </div>\n }\n <button variant=\"ghost\" size=\"xs\" class=\"@max-xl:ml-auto\" [class.ml-auto]=\"!selection.active()\" (click)=\"selection.toggleMode()\">\n {{ (selection.active() ? \"documentWorkspaces.common.cancel\" : \"documentWorkspaces.common.select\") | transloco }}\n </button>\n }\n </div>\n\n @if (refreshError(); as message) {\n <p role=\"alert\" class=\"text-sm text-(--font-error-base)\">{{ \"documentWorkspaces.list.refreshError\" | transloco: { error: message } }}</p>\n }\n\n <!-- Only the workspaces scroll: the title, the tabs and the toolbar stay in place. -->\n <div class=\"min-h-0 flex-1 overflow-y-auto\">\n @switch (status()) {\n @case (\"loading\") {\n <p class=\"text-sm text-(--font-primary-muted)\">{{ \"documentWorkspaces.list.loading\" | transloco }}</p>\n }\n @case (\"error\") {\n <div class=\"flex items-center gap-3 text-sm\" data-testid=\"load-error\">\n <span class=\"text-(--font-error-base)\">\n @if (loadError(); as message) {\n <workspace-message [message]=\"message\" />\n } @else {\n {{ \"documentWorkspaces.list.loadError\" | transloco }}\n }\n </span>\n <button variant=\"secondary\" size=\"xs\" (click)=\"load()\">{{ \"documentWorkspaces.common.retry\" | transloco }}</button>\n </div>\n }\n @default {\n @if (!filter.filtered().length) {\n <div class=\"flex flex-col items-center gap-1 py-8 text-center\" data-testid=\"empty\">\n <!-- The illustration is for a list with nothing in it, not for a search that found nothing. -->\n @if (!store.workspaces().length) {\n <empty-folder-icon />\n }\n <p class=\"text-sm text-(--font-primary-muted)\">\n {{ (store.workspaces().length ? \"documentWorkspaces.list.noMatch\" : \"documentWorkspaces.list.empty\") | transloco }}\n </p>\n </div>\n } @else if (viewMode() === \"grid\") {\n <div class=\"grid grid-cols-[repeat(auto-fill,minmax(300px,1fr))] content-start gap-4 pb-8\">\n @for (workspace of filter.filtered(); track workspace.id) {\n <workspace-card\n [workspace]=\"workspace\"\n [query]=\"filter.query()\"\n [selecting]=\"selection.active()\"\n [selected]=\"selection.isSelected(workspace)\"\n [selectable]=\"selection.canSelect(workspace)\"\n (opened)=\"opened.emit(workspace)\"\n (deleteRequested)=\"confirmDelete([workspace])\"\n (selectedChange)=\"selection.toggle(workspace, $event)\" />\n }\n </div>\n } @else {\n <div role=\"table\" class=\"flex flex-col gap-3 pb-8\">\n <div role=\"row\" [attr.data-selecting]=\"selection.active() ? '' : null\" class=\"grid grid-cols-[minmax(0,1fr)_auto_auto_2.5rem] items-center gap-x-4 px-3 data-selecting:grid-cols-[2rem_minmax(0,1fr)_auto_auto_2.5rem] @xl:grid-cols-[minmax(0,1fr)_4.5rem_6.5rem_6.5rem_2.5rem] @xl:data-selecting:grid-cols-[2rem_minmax(0,1fr)_4.5rem_6.5rem_6.5rem_2.5rem] @3xl:grid-cols-[minmax(0,2fr)_5.5rem_7.5rem_9rem_minmax(5rem,1fr)_2.5rem] @3xl:data-selecting:grid-cols-[2rem_minmax(0,2fr)_5.5rem_7.5rem_9rem_minmax(5rem,1fr)_2.5rem] py-1 text-xs font-semibold text-(--font-primary-muted)\">\n @if (selection.active()) {\n <div role=\"columnheader\">\n <input\n type=\"checkbox\"\n class=\"accent-(--bg-primary-base) size-4\"\n [checked]=\"selection.allSelected()\"\n [indeterminate]=\"selection.partiallySelected()\"\n [attr.aria-label]=\"'documentWorkspaces.common.selectAll' | transloco\"\n (change)=\"selection.toggleAll()\" />\n\n </div>\n }\n <div role=\"columnheader\">{{ \"documentWorkspaces.list.columnName\" | transloco }}</div>\n <div role=\"columnheader\">{{ \"documentWorkspaces.list.columnContent\" | transloco }}</div>\n <div role=\"columnheader\">{{ \"documentWorkspaces.list.columnOwner\" | transloco }}</div>\n <div role=\"columnheader\" class=\"hidden @xl:block\">{{ \"documentWorkspaces.list.columnDateUpdated\" | transloco }}</div>\n <div role=\"columnheader\" class=\"hidden @3xl:block\">{{ \"documentWorkspaces.list.columnTags\" | transloco }}</div>\n <div role=\"columnheader\"></div>\n </div>\n @for (workspace of filter.filtered(); track workspace.id) {\n <workspace-row\n [workspace]=\"workspace\"\n [query]=\"filter.query()\"\n [selecting]=\"selection.active()\"\n [selected]=\"selection.isSelected(workspace)\"\n [selectable]=\"selection.canSelect(workspace)\"\n (opened)=\"opened.emit(workspace)\"\n (deleteRequested)=\"confirmDelete([workspace])\"\n (selectedChange)=\"selection.toggle(workspace, $event)\" />\n }\n </div>\n }\n\n @if (store.hasMore() || store.truncated()) {\n <div class=\"flex flex-col items-center gap-2 pb-8\" data-testid=\"load-more\">\n <p class=\"text-sm text-(--font-primary-muted)\">\n {{ \"documentWorkspaces.list.partial\" | transloco: { shown: store.workspaces().length, total: store.pagination().total } }}\n </p>\n @if (loadMoreError(); as message) {\n <p role=\"alert\" class=\"text-sm text-(--font-error-base)\">{{ \"documentWorkspaces.list.loadMoreError\" | transloco: { error: message } }}</p>\n }\n @if (store.hasMore()) {\n <button variant=\"secondary\" size=\"sm\" [disabled]=\"loadingMore()\" (click)=\"loadMore()\">\n {{ \"documentWorkspaces.list.loadMore\" | transloco }}\n </button>\n }\n </div>\n }\n }\n }\n </div>\n ", isInline: true, dependencies: [{ kind: "directive", type: ButtonComponent, selector: "button", inputs: ["class", "variant", "decoration", "scheme", "iconOnly", "size", "solid"] }, { kind: "component", type: EmptyFolderIcon, selector: "empty-folder-icon" }, { kind: "component", type: GridIcon, selector: "grid-icon", inputs: ["class"] }, { kind: "component", type: ListIcon, selector: "list-icon, ListIcon", inputs: ["class"] }, { kind: "component", type: MagnifyingGlassIcon, selector: "magnifying-glass-icon, MagnifyingGlassIcon", inputs: ["class"] }, { kind: "component", type: PlusIcon, selector: "plus-icon, PlusIcon", inputs: ["class"] }, { kind: "directive", type: TooltipDirective, selector: "[tooltip]", inputs: ["tooltip", "strategy", "tooltip-position", "tooltip-delay", "tooltip-duration", "tooltip-offset"] }, { kind: "component", type: TrashIcon, selector: "trash-icon, TrashIcon", inputs: ["class"] }, { kind: "component", type: UserIcon, selector: "user-icon, UserIcon", inputs: ["class"] }, { kind: "component", type: UserRoundIcon, selector: "user-round, UserRound, userround, user-round-icon, UserRoundIcon, userroundicon", inputs: ["class"] }, { kind: "component", type: WorkspaceCard, selector: "workspace-card", inputs: ["workspace", "query", "selecting", "selected", "selectable"], outputs: ["opened", "deleteRequested", "selectedChange"] }, { kind: "component", type: WorkspaceMessage, selector: "workspace-message", inputs: ["message"] }, { kind: "component", type: WorkspaceRow, selector: "workspace-row", inputs: ["workspace", "query", "selecting", "selected", "selectable"], outputs: ["opened", "deleteRequested", "selectedChange"] }, { kind: "component", type: XMarkIcon, selector: "XMarkIcon, xmark-icon, x-mark-icon", inputs: ["class"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
3105
3183
|
}
|
|
3106
3184
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.29", ngImport: i0, type: WorkspaceList, decorators: [{
|
|
3107
3185
|
type: Component,
|
|
@@ -3305,6 +3383,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.29", ngImpo
|
|
|
3305
3383
|
}
|
|
3306
3384
|
</div>
|
|
3307
3385
|
}
|
|
3386
|
+
|
|
3387
|
+
@if (store.hasMore() || store.truncated()) {
|
|
3388
|
+
<div class="flex flex-col items-center gap-2 pb-8" data-testid="load-more">
|
|
3389
|
+
<p class="text-sm text-(--font-primary-muted)">
|
|
3390
|
+
{{ "documentWorkspaces.list.partial" | transloco: { shown: store.workspaces().length, total: store.pagination().total } }}
|
|
3391
|
+
</p>
|
|
3392
|
+
@if (loadMoreError(); as message) {
|
|
3393
|
+
<p role="alert" class="text-sm text-(--font-error-base)">{{ "documentWorkspaces.list.loadMoreError" | transloco: { error: message } }}</p>
|
|
3394
|
+
}
|
|
3395
|
+
@if (store.hasMore()) {
|
|
3396
|
+
<button variant="secondary" size="sm" [disabled]="loadingMore()" (click)="loadMore()">
|
|
3397
|
+
{{ "documentWorkspaces.list.loadMore" | transloco }}
|
|
3398
|
+
</button>
|
|
3399
|
+
}
|
|
3400
|
+
</div>
|
|
3401
|
+
}
|
|
3308
3402
|
}
|
|
3309
3403
|
}
|
|
3310
3404
|
</div>
|