@memberjunction/ng-file-storage 6.2.0-edge.0 → 6.2.0-edge.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/dist/lib/admin/storage-admin-dialog.component.d.ts +1 -1
- package/dist/lib/admin/storage-admin-dialog.component.js +2 -2
- package/dist/lib/admin/storage-admin-dialog.component.js.map +1 -1
- package/dist/lib/category-tree/category-tree.d.ts +83 -9
- package/dist/lib/category-tree/category-tree.d.ts.map +1 -1
- package/dist/lib/category-tree/category-tree.js +192 -50
- package/dist/lib/category-tree/category-tree.js.map +1 -1
- package/dist/lib/file-browser/file-browser.component.d.ts +30 -4
- package/dist/lib/file-browser/file-browser.component.d.ts.map +1 -1
- package/dist/lib/file-browser/file-browser.component.js +68 -16
- package/dist/lib/file-browser/file-browser.component.js.map +1 -1
- package/dist/lib/file-browser/file-grid.component.d.ts +404 -58
- package/dist/lib/file-browser/file-grid.component.d.ts.map +1 -1
- package/dist/lib/file-browser/file-grid.component.js +1040 -396
- package/dist/lib/file-browser/file-grid.component.js.map +1 -1
- package/dist/lib/file-browser/folder-tree.component.d.ts +65 -9
- package/dist/lib/file-browser/folder-tree.component.d.ts.map +1 -1
- package/dist/lib/file-browser/folder-tree.component.js +166 -58
- package/dist/lib/file-browser/folder-tree.component.js.map +1 -1
- package/dist/lib/file-browser/storage-providers-list.component.d.ts +55 -7
- package/dist/lib/file-browser/storage-providers-list.component.d.ts.map +1 -1
- package/dist/lib/file-browser/storage-providers-list.component.js +124 -34
- package/dist/lib/file-browser/storage-providers-list.component.js.map +1 -1
- package/dist/lib/file-upload/file-upload.d.ts +22 -2
- package/dist/lib/file-upload/file-upload.d.ts.map +1 -1
- package/dist/lib/file-upload/file-upload.js +40 -10
- package/dist/lib/file-upload/file-upload.js.map +1 -1
- package/dist/lib/files-grid/files-grid.d.ts +28 -4
- package/dist/lib/files-grid/files-grid.d.ts.map +1 -1
- package/dist/lib/files-grid/files-grid.js +71 -23
- package/dist/lib/files-grid/files-grid.js.map +1 -1
- package/dist/lib/record-attachments/record-attachments.component.d.ts +15 -7
- package/dist/lib/record-attachments/record-attachments.component.d.ts.map +1 -1
- package/dist/lib/record-attachments/record-attachments.component.js +34 -18
- package/dist/lib/record-attachments/record-attachments.component.js.map +1 -1
- package/package.json +13 -13
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"folder-tree.component.js","sourceRoot":"","sources":["../../../src/lib/file-browser/folder-tree.component.ts","../../../src/lib/file-browser/folder-tree.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, EventEmitter, Input, Output, ChangeDetectorRef, inject } from '@angular/core';\nimport { GraphQLDataProvider, GraphQLFileStorageClient } from '@memberjunction/graphql-dataprovider';\nimport { StorageAccountWithProvider } from '@memberjunction/core-entities';\nimport { UUIDsEqual } from '@memberjunction/global';\n\n/**\n * Represents a breadcrumb item in the path\n */\nexport interface BreadcrumbItem {\n label: string;\n path: string;\n}\n\n/**\n * Represents a folder in the tree\n */\nexport interface FolderItem {\n name: string;\n fullPath: string;\n}\n\n/**\n * Folder tree navigation component with breadcrumbs and history navigation.\n * Provides Mac Finder-style navigation with back/forward buttons and breadcrumb path.\n * Loads actual folder structure from storage accounts via GraphQL.\n */\n@Component({\n standalone: false,\n selector: 'mj-folder-tree',\n templateUrl: './folder-tree.component.html',\n styleUrls: ['./folder-tree.component.css']\n})\nexport class FolderTreeComponent {\n /**\n * GraphQL client for file storage operations\n */\n private storageClient: GraphQLFileStorageClient;\n\n /**\n * Currently selected storage account with provider details\n */\n private _account: StorageAccountWithProvider | null = null;\n\n constructor() {\n this.storageClient = new GraphQLFileStorageClient(GraphQLDataProvider.Instance);\n }\n\n @Input()\n set account(value: StorageAccountWithProvider | null) {\n const previousAccount = this._account;\n this._account = value;\n\n if (value && !UUIDsEqual(value.account.ID, previousAccount?.account.ID)) {\n this.resetNavigation();\n this.loadFolders();\n }\n }\n get account(): StorageAccountWithProvider | null {\n return this._account;\n }\n\n /**\n * Emits when a folder is selected in the tree\n */\n @Output() folderSelected = new EventEmitter<string>();\n\n /**\n * Current folder path\n */\n public currentPath: string = '/';\n\n /**\n * Navigation history (for back button)\n */\n public history: string[] = [];\n\n /**\n * Current position in history\n */\n public historyIndex: number = -1;\n\n /**\n * Breadcrumb items for current path\n */\n public breadcrumbs: BreadcrumbItem[] = [];\n\n /**\n * Folders in current path\n */\n public folders: FolderItem[] = [];\n\n private cdr = inject(ChangeDetectorRef);\n\n /**\n * Loading state\n */\n public isLoading: boolean = false;\n\n /**\n * Error message\n */\n public errorMessage: string | null = null;\n\n /**\n * Resets navigation when provider changes\n */\n private resetNavigation(): void {\n this.currentPath = '/';\n this.history = ['/'];\n this.historyIndex = 0;\n this.updateBreadcrumbs();\n }\n\n /**\n * Navigates to a specific folder path\n */\n public navigateToPath(path: string): void {\n // Don't navigate if already at this path\n if (path === this.currentPath) {\n return;\n }\n\n // Add to history if navigating from user action (not back/forward)\n if (this.historyIndex === this.history.length - 1) {\n this.history.push(path);\n this.historyIndex = this.history.length - 1;\n } else {\n // Navigating from middle of history - truncate forward history\n this.history = this.history.slice(0, this.historyIndex + 1);\n this.history.push(path);\n this.historyIndex = this.history.length - 1;\n }\n\n this.currentPath = path;\n this.updateBreadcrumbs();\n this.loadFolders();\n this.folderSelected.emit(path);\n }\n\n /**\n * Refreshes the current folder view without changing navigation\n * Used when folder structure changes (e.g., folder deleted) but we're staying in the same location\n */\n public refresh(): void {\n this.loadFolders();\n }\n\n /**\n * Navigates back in history\n */\n public navigateBack(): void {\n if (!this.canGoBack()) {\n return;\n }\n\n this.historyIndex--;\n this.currentPath = this.history[this.historyIndex];\n this.updateBreadcrumbs();\n this.folderSelected.emit(this.currentPath);\n this.loadFolders();\n }\n\n /**\n * Navigates forward in history\n */\n public navigateForward(): void {\n if (!this.canGoForward()) {\n return;\n }\n\n this.historyIndex++;\n this.currentPath = this.history[this.historyIndex];\n this.updateBreadcrumbs();\n this.folderSelected.emit(this.currentPath);\n this.loadFolders();\n }\n\n /**\n * Checks if can navigate back\n */\n public canGoBack(): boolean {\n return this.historyIndex > 0;\n }\n\n /**\n * Checks if can navigate forward\n */\n public canGoForward(): boolean {\n return this.historyIndex < this.history.length - 1;\n }\n\n /**\n * Updates breadcrumbs based on current path\n */\n private updateBreadcrumbs(): void {\n if (!this.account) {\n this.breadcrumbs = [];\n return;\n }\n\n // Start with account root (show account name)\n const items: BreadcrumbItem[] = [\n {\n label: this.account.account.Name,\n path: '/'\n }\n ];\n\n // Add path segments if not at root\n if (this.currentPath !== '/') {\n const segments = this.currentPath.split('/').filter(s => s.length > 0);\n let builtPath = '';\n\n for (const segment of segments) {\n builtPath += '/' + segment;\n items.push({\n label: segment,\n path: builtPath\n });\n }\n }\n\n this.breadcrumbs = items;\n }\n\n /**\n * Handles breadcrumb click\n */\n public onBreadcrumbClick(item: BreadcrumbItem): void {\n this.navigateToPath(item.path);\n this.loadFolders();\n }\n\n /**\n * Loads folders from the storage account for the current path\n */\n private async loadFolders(): Promise<void> {\n if (!this.account) {\n this.folders = [];\n return;\n }\n\n this.isLoading = true;\n this.errorMessage = null;\n this.cdr.detectChanges();\n\n try {\n // Blob keys (Azure/S3) have NO leading slash and use a trailing slash to denote a\n // directory prefix. currentPath is stored with a leading slash (e.g. \"/test\"), which\n // never matches real keys — so nested-folder listings came back empty (empty folder\n // names). Normalize to a slash-free, trailing-slash prefix before listing.\n const rawPath = this.currentPath && this.currentPath !== '/' ? this.currentPath : '';\n const listPrefix = rawPath ? rawPath.replace(/^\\/+/, '').replace(/\\/+$/, '') + '/' : '';\n const listResult = await this.storageClient.ListObjects(\n this.account.account.ID,\n listPrefix,\n '/'\n );\n\n // Convert prefixes to FolderItems\n const prefixes = listResult.prefixes || [];\n this.folders = prefixes.map((prefix: string) => {\n // Remove trailing slash and get just the folder name\n const cleanPath = prefix.endsWith('/') ? prefix.slice(0, -1) : prefix;\n const name = cleanPath.split('/').pop() || cleanPath;\n\n return {\n name,\n fullPath: prefix\n };\n });\n } catch (error) {\n console.error('Error loading folders:', error);\n this.errorMessage = error instanceof Error ? error.message : 'Failed to load folders';\n this.folders = [];\n } finally {\n this.isLoading = false;\n this.cdr.detectChanges();\n }\n }\n\n /**\n * Handles folder click for navigation\n */\n public onFolderClick(folder: FolderItem): void {\n this.navigateToPath(folder.fullPath);\n this.loadFolders();\n }\n}\n","<div class=\"folder-tree\">\n <!-- Navigation bar with back/forward buttons and breadcrumbs -->\n @if (account) {\n <div class=\"navigation-bar\">\n <!-- Back/Forward buttons -->\n <div class=\"nav-buttons\">\n <button\n type=\"button\"\n [disabled]=\"!canGoBack()\"\n (click)=\"navigateBack()\"\n title=\"Go back\"\n class=\"nav-button\">\n <i class=\"fa-solid fa-chevron-left\"></i>\n </button>\n <button\n type=\"button\"\n [disabled]=\"!canGoForward()\"\n (click)=\"navigateForward()\"\n title=\"Go forward\"\n class=\"nav-button\">\n <i class=\"fa-solid fa-chevron-right\"></i>\n </button>\n </div>\n <!-- Breadcrumb path -->\n <div class=\"breadcrumbs\">\n @for (item of breadcrumbs; track item.path; let last = $last) {\n <button\n type=\"button\"\n (click)=\"onBreadcrumbClick(item)\"\n [class.current]=\"last\"\n class=\"breadcrumb-item\">\n {{ item.label }}\n </button>\n @if (!last) {\n <i class=\"fa-solid fa-chevron-right breadcrumb-separator\"></i>\n }\n }\n </div>\n </div>\n }\n\n <!-- Folder tree content -->\n <div class=\"tree-content\">\n <!-- Loading state -->\n @if (isLoading) {\n <div class=\"tree-loading\">\n <mj-loading text=\"Loading folders...\"></mj-loading>\n </div>\n }\n\n <!-- Error state -->\n @if (errorMessage && !isLoading) {\n <div class=\"tree-error\">\n <i class=\"fa-solid fa-triangle-exclamation error-icon\"></i>\n <p class=\"error-text\">{{ errorMessage }}</p>\n </div>\n }\n\n <!-- Folder list -->\n @if (!isLoading && !errorMessage) {\n <div class=\"folder-list\">\n @for (folder of folders; track folder.name) {\n <div\n class=\"folder-item\"\n (click)=\"onFolderClick(folder)\"\n [title]=\"folder.name\">\n <i class=\"fa-solid fa-folder folder-icon\"></i>\n <span class=\"folder-name\">{{ folder.name }}</span>\n </div>\n }\n <!-- Empty state -->\n @if (folders.length === 0) {\n <div class=\"tree-placeholder\">\n <i class=\"fa-solid fa-folder-open placeholder-icon\"></i>\n <p class=\"placeholder-text\">No folders in this location</p>\n @if (account) {\n <p class=\"placeholder-hint\">Account: {{ account.account.Name }}</p>\n }\n <p class=\"placeholder-hint\">Path: {{ currentPath }}</p>\n </div>\n }\n </div>\n }\n </div>\n</div>\n"]}
|
|
1
|
+
{"version":3,"file":"folder-tree.component.js","sourceRoot":"","sources":["../../../src/lib/file-browser/folder-tree.component.ts","../../../src/lib/file-browser/folder-tree.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, EventEmitter, Input, Output, ChangeDetectorRef, inject } from '@angular/core';\nimport { GraphQLDataProvider, GraphQLFileStorageClient } from '@memberjunction/graphql-dataprovider';\nimport { StorageAccountWithProvider } from '@memberjunction/core-entities';\nimport { UUIDsEqual } from '@memberjunction/global';\n\n/**\n * Represents a breadcrumb item in the path\n */\nexport interface BreadcrumbItem {\n label: string; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n path: string; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n}\n\n/**\n * Represents a folder in the tree\n */\nexport interface FolderItem {\n name: string; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n fullPath: string; // case-violation-ok-legacy-back-compat: the type is named in an exported signature, so consumers build object literals against it; an interface has no runtime carrier for a stub\n}\n\n/**\n * Folder tree navigation component with breadcrumbs and history navigation.\n * Provides Mac Finder-style navigation with back/forward buttons and breadcrumb path.\n * Loads actual folder structure from storage accounts via GraphQL.\n */\n@Component({\n standalone: false,\n selector: 'mj-folder-tree',\n templateUrl: './folder-tree.component.html',\n styleUrls: ['./folder-tree.component.css']\n})\nexport class FolderTreeComponent {\n /**\n * GraphQL client for file storage operations\n */\n private storageClient: GraphQLFileStorageClient;\n\n /**\n * Currently selected storage account with provider details\n */\n private _account: StorageAccountWithProvider | null = null;\n\n constructor() {\n this.storageClient = new GraphQLFileStorageClient(GraphQLDataProvider.Instance);\n }\n\n @Input()\n set Account(value: StorageAccountWithProvider | null) {\n const previousAccount = this._account;\n this._account = value;\n\n if (value && !UUIDsEqual(value.account.ID, previousAccount?.account.ID)) {\n this.resetNavigation();\n this.loadFolders();\n }\n }\n get Account(): StorageAccountWithProvider | null {\n return this._account;\n }\n\n /** @deprecated Use {@link Account}. */\n get account(): StorageAccountWithProvider | null {\n return this.Account;\n }\n /** @deprecated Use {@link Account}. */\n @Input() set account(value: StorageAccountWithProvider | null) {\n this.Account = value;\n }\n\n /**\n * Emits when a folder is selected in the tree\n */\n @Output() FolderSelected = new EventEmitter<string>();\n\n /**\n * @deprecated Use {@link FolderSelected}.\n *\n * The same emitter under the old binding name, so a template still binding\n * (folderSelected) keeps working. Must stay AFTER FolderSelected: class fields\n * initialise in order, and the other way round this captures undefined.\n */\n @Output() folderSelected = this.FolderSelected;\n\n /**\n * Current folder path\n */\n public CurrentPath: string = '/';\n\n /** @deprecated Use {@link CurrentPath}. */\n public get currentPath(): string {\n return this.CurrentPath;\n }\n /** @deprecated Use {@link CurrentPath}. */\n public set currentPath(value: string) {\n this.CurrentPath = value;\n }\n\n /**\n * Navigation history (for back button)\n */\n public History: string[] = [];\n\n /** @deprecated Use {@link History}. */\n public get history(): string[] {\n return this.History;\n }\n /** @deprecated Use {@link History}. */\n public set history(value: string[]) {\n this.History = value;\n }\n\n /**\n * Current position in history\n */\n public HistoryIndex: number = -1;\n\n /** @deprecated Use {@link HistoryIndex}. */\n public get historyIndex(): number {\n return this.HistoryIndex;\n }\n /** @deprecated Use {@link HistoryIndex}. */\n public set historyIndex(value: number) {\n this.HistoryIndex = value;\n }\n\n /**\n * Breadcrumb items for current path\n */\n public Breadcrumbs: BreadcrumbItem[] = [];\n\n /** @deprecated Use {@link Breadcrumbs}. */\n public get breadcrumbs(): BreadcrumbItem[] {\n return this.Breadcrumbs;\n }\n /** @deprecated Use {@link Breadcrumbs}. */\n public set breadcrumbs(value: BreadcrumbItem[]) {\n this.Breadcrumbs = value;\n }\n\n /**\n * Folders in current path\n */\n public Folders: FolderItem[] = [];\n\n /** @deprecated Use {@link Folders}. */\n public get folders(): FolderItem[] {\n return this.Folders;\n }\n /** @deprecated Use {@link Folders}. */\n public set folders(value: FolderItem[]) {\n this.Folders = value;\n }\n\n private cdr = inject(ChangeDetectorRef);\n\n /**\n * Loading state\n */\n public IsLoading: boolean = false;\n\n /** @deprecated Use {@link IsLoading}. */\n public get isLoading(): boolean {\n return this.IsLoading;\n }\n /** @deprecated Use {@link IsLoading}. */\n public set isLoading(value: boolean) {\n this.IsLoading = value;\n }\n\n /**\n * Error message\n */\n public ErrorMessage: string | null = null;\n\n /** @deprecated Use {@link ErrorMessage}. */\n public get errorMessage(): string | null {\n return this.ErrorMessage;\n }\n /** @deprecated Use {@link ErrorMessage}. */\n public set errorMessage(value: string | null) {\n this.ErrorMessage = value;\n }\n\n /**\n * Resets navigation when provider changes\n */\n private resetNavigation(): void {\n this.CurrentPath = '/';\n this.History = ['/'];\n this.HistoryIndex = 0;\n this.updateBreadcrumbs();\n }\n\n /**\n * Navigates to a specific folder path\n */\n public NavigateToPath(path: string): void {\n // Don't navigate if already at this path\n if (path === this.CurrentPath) {\n return;\n }\n\n // Add to history if navigating from user action (not back/forward)\n if (this.HistoryIndex === this.History.length - 1) {\n this.History.push(path);\n this.HistoryIndex = this.History.length - 1;\n } else {\n // Navigating from middle of history - truncate forward history\n this.History = this.History.slice(0, this.HistoryIndex + 1);\n this.History.push(path);\n this.HistoryIndex = this.History.length - 1;\n }\n\n this.CurrentPath = path;\n this.updateBreadcrumbs();\n this.loadFolders();\n this.FolderSelected.emit(path);\n }\n\n /** @deprecated Use {@link NavigateToPath}. */\n public navigateToPath(path: string): void {\n return this.NavigateToPath(path);\n }\n\n /**\n * Refreshes the current folder view without changing navigation\n * Used when folder structure changes (e.g., folder deleted) but we're staying in the same location\n */\n public Refresh(): void {\n this.loadFolders();\n }\n\n /** @deprecated Use {@link Refresh}. */\n public refresh(): void {\n return this.Refresh();\n }\n\n /**\n * Navigates back in history\n */\n public NavigateBack(): void {\n if (!this.CanGoBack()) {\n return;\n }\n\n this.HistoryIndex--;\n this.CurrentPath = this.History[this.HistoryIndex];\n this.updateBreadcrumbs();\n this.FolderSelected.emit(this.CurrentPath);\n this.loadFolders();\n }\n\n /** @deprecated Use {@link NavigateBack}. */\n public navigateBack(): void {\n return this.NavigateBack();\n }\n\n /**\n * Navigates forward in history\n */\n public NavigateForward(): void {\n if (!this.CanGoForward()) {\n return;\n }\n\n this.HistoryIndex++;\n this.CurrentPath = this.History[this.HistoryIndex];\n this.updateBreadcrumbs();\n this.FolderSelected.emit(this.CurrentPath);\n this.loadFolders();\n }\n\n /** @deprecated Use {@link NavigateForward}. */\n public navigateForward(): void {\n return this.NavigateForward();\n }\n\n /**\n * Checks if can navigate back\n */\n public CanGoBack(): boolean {\n return this.HistoryIndex > 0;\n }\n\n /** @deprecated Use {@link CanGoBack}. */\n public canGoBack(): boolean {\n return this.CanGoBack();\n }\n\n /**\n * Checks if can navigate forward\n */\n public CanGoForward(): boolean {\n return this.HistoryIndex < this.History.length - 1;\n }\n\n /** @deprecated Use {@link CanGoForward}. */\n public canGoForward(): boolean {\n return this.CanGoForward();\n }\n\n /**\n * Updates breadcrumbs based on current path\n */\n private updateBreadcrumbs(): void {\n if (!this.Account) {\n this.Breadcrumbs = [];\n return;\n }\n\n // Start with account root (show account name)\n const items: BreadcrumbItem[] = [\n {\n label: this.Account.account.Name,\n path: '/'\n }\n ];\n\n // Add path segments if not at root\n if (this.CurrentPath !== '/') {\n const segments = this.CurrentPath.split('/').filter(s => s.length > 0);\n let builtPath = '';\n\n for (const segment of segments) {\n builtPath += '/' + segment;\n items.push({\n label: segment,\n path: builtPath\n });\n }\n }\n\n this.Breadcrumbs = items;\n }\n\n /**\n * Handles breadcrumb click\n */\n public OnBreadcrumbClick(item: BreadcrumbItem): void {\n this.NavigateToPath(item.path);\n this.loadFolders();\n }\n\n /** @deprecated Use {@link OnBreadcrumbClick}. */\n public onBreadcrumbClick(item: BreadcrumbItem): void {\n return this.OnBreadcrumbClick(item);\n }\n\n /**\n * Loads folders from the storage account for the current path\n */\n private async loadFolders(): Promise<void> {\n if (!this.Account) {\n this.Folders = [];\n return;\n }\n\n this.IsLoading = true;\n this.ErrorMessage = null;\n this.cdr.detectChanges();\n\n try {\n // Blob keys (Azure/S3) have NO leading slash and use a trailing slash to denote a\n // directory prefix. currentPath is stored with a leading slash (e.g. \"/test\"), which\n // never matches real keys — so nested-folder listings came back empty (empty folder\n // names). Normalize to a slash-free, trailing-slash prefix before listing.\n const rawPath = this.CurrentPath && this.CurrentPath !== '/' ? this.CurrentPath : '';\n const listPrefix = rawPath ? rawPath.replace(/^\\/+/, '').replace(/\\/+$/, '') + '/' : '';\n const listResult = await this.storageClient.ListObjects(\n this.Account.account.ID,\n listPrefix,\n '/'\n );\n\n // Convert prefixes to FolderItems\n const prefixes = listResult.prefixes || [];\n this.Folders = prefixes.map((prefix: string) => {\n // Remove trailing slash and get just the folder name\n const cleanPath = prefix.endsWith('/') ? prefix.slice(0, -1) : prefix;\n const name = cleanPath.split('/').pop() || cleanPath;\n\n return {\n name,\n fullPath: prefix\n };\n });\n } catch (error) {\n console.error('Error loading folders:', error);\n this.ErrorMessage = error instanceof Error ? error.message : 'Failed to load folders';\n this.Folders = [];\n } finally {\n this.IsLoading = false;\n this.cdr.detectChanges();\n }\n }\n\n /**\n * Handles folder click for navigation\n */\n public OnFolderClick(folder: FolderItem): void {\n this.NavigateToPath(folder.fullPath);\n this.loadFolders();\n }\n\n /** @deprecated Use {@link OnFolderClick}. */\n public onFolderClick(folder: FolderItem): void {\n return this.OnFolderClick(folder);\n }\n}\n","<div class=\"folder-tree\">\n <!-- Navigation bar with back/forward buttons and breadcrumbs -->\n @if (account) {\n <div class=\"navigation-bar\">\n <!-- Back/Forward buttons -->\n <div class=\"nav-buttons\">\n <button\n type=\"button\"\n [disabled]=\"!canGoBack()\"\n (click)=\"navigateBack()\"\n title=\"Go back\"\n class=\"nav-button\">\n <i class=\"fa-solid fa-chevron-left\"></i>\n </button>\n <button\n type=\"button\"\n [disabled]=\"!canGoForward()\"\n (click)=\"navigateForward()\"\n title=\"Go forward\"\n class=\"nav-button\">\n <i class=\"fa-solid fa-chevron-right\"></i>\n </button>\n </div>\n <!-- Breadcrumb path -->\n <div class=\"breadcrumbs\">\n @for (item of breadcrumbs; track item.path; let last = $last) {\n <button\n type=\"button\"\n (click)=\"onBreadcrumbClick(item)\"\n [class.current]=\"last\"\n class=\"breadcrumb-item\">\n {{ item.label }}\n </button>\n @if (!last) {\n <i class=\"fa-solid fa-chevron-right breadcrumb-separator\"></i>\n }\n }\n </div>\n </div>\n }\n\n <!-- Folder tree content -->\n <div class=\"tree-content\">\n <!-- Loading state -->\n @if (isLoading) {\n <div class=\"tree-loading\">\n <mj-loading text=\"Loading folders...\"></mj-loading>\n </div>\n }\n\n <!-- Error state -->\n @if (errorMessage && !isLoading) {\n <div class=\"tree-error\">\n <i class=\"fa-solid fa-triangle-exclamation error-icon\"></i>\n <p class=\"error-text\">{{ errorMessage }}</p>\n </div>\n }\n\n <!-- Folder list -->\n @if (!isLoading && !errorMessage) {\n <div class=\"folder-list\">\n @for (folder of folders; track folder.name) {\n <div\n class=\"folder-item\"\n (click)=\"onFolderClick(folder)\"\n [title]=\"folder.name\">\n <i class=\"fa-solid fa-folder folder-icon\"></i>\n <span class=\"folder-name\">{{ folder.name }}</span>\n </div>\n }\n <!-- Empty state -->\n @if (folders.length === 0) {\n <div class=\"tree-placeholder\">\n <i class=\"fa-solid fa-folder-open placeholder-icon\"></i>\n <p class=\"placeholder-text\">No folders in this location</p>\n @if (account) {\n <p class=\"placeholder-hint\">Account: {{ account.account.Name }}</p>\n }\n <p class=\"placeholder-hint\">Path: {{ currentPath }}</p>\n </div>\n }\n </div>\n }\n </div>\n</div>\n"]}
|
|
@@ -15,15 +15,31 @@ export declare class StorageProvidersListComponent implements OnInit {
|
|
|
15
15
|
* Emits when an account is selected by the user, or null when no accounts are available.
|
|
16
16
|
* Emits the full account-with-provider object for downstream components to use.
|
|
17
17
|
*/
|
|
18
|
+
AccountSelected: EventEmitter<StorageAccountWithProvider | null>;
|
|
19
|
+
/**
|
|
20
|
+
* @deprecated Use {@link AccountSelected}.
|
|
21
|
+
*
|
|
22
|
+
* The same emitter under the old binding name, so a template still binding
|
|
23
|
+
* (accountSelected) keeps working. Must stay AFTER AccountSelected: class fields
|
|
24
|
+
* initialise in order, and the other way round this captures undefined.
|
|
25
|
+
*/
|
|
18
26
|
accountSelected: EventEmitter<StorageAccountWithProvider | null>;
|
|
19
27
|
/**
|
|
20
28
|
* All available storage accounts with their provider details.
|
|
21
29
|
*/
|
|
22
|
-
|
|
30
|
+
Accounts: StorageAccountWithProvider[];
|
|
31
|
+
/** @deprecated Use {@link Accounts}. */
|
|
32
|
+
get accounts(): StorageAccountWithProvider[];
|
|
33
|
+
/** @deprecated Use {@link Accounts}. */
|
|
34
|
+
set accounts(value: StorageAccountWithProvider[]);
|
|
23
35
|
/**
|
|
24
36
|
* Currently selected account.
|
|
25
37
|
*/
|
|
26
|
-
|
|
38
|
+
SelectedAccount: StorageAccountWithProvider | null;
|
|
39
|
+
/** @deprecated Use {@link SelectedAccount}. */
|
|
40
|
+
get selectedAccount(): StorageAccountWithProvider | null;
|
|
41
|
+
/** @deprecated Use {@link SelectedAccount}. */
|
|
42
|
+
set selectedAccount(value: StorageAccountWithProvider | null);
|
|
27
43
|
/**
|
|
28
44
|
* Loading state indicator.
|
|
29
45
|
*/
|
|
@@ -35,13 +51,29 @@ export declare class StorageProvidersListComponent implements OnInit {
|
|
|
35
51
|
/**
|
|
36
52
|
* Whether current user has admin rights to configure storage accounts or providers.
|
|
37
53
|
*/
|
|
38
|
-
|
|
54
|
+
UserCanManage: boolean;
|
|
55
|
+
/** @deprecated Use {@link UserCanManage}. */
|
|
56
|
+
get userCanManage(): boolean;
|
|
57
|
+
/** @deprecated Use {@link UserCanManage}. */
|
|
58
|
+
set userCanManage(value: boolean);
|
|
39
59
|
/**
|
|
40
60
|
* Admin dialog state
|
|
41
61
|
*/
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
62
|
+
IsManageDialogOpen: boolean;
|
|
63
|
+
/** @deprecated Use {@link IsManageDialogOpen}. */
|
|
64
|
+
get isManageDialogOpen(): boolean;
|
|
65
|
+
/** @deprecated Use {@link IsManageDialogOpen}. */
|
|
66
|
+
set isManageDialogOpen(value: boolean);
|
|
67
|
+
AdminDialogTab: StorageAdminTab;
|
|
68
|
+
/** @deprecated Use {@link AdminDialogTab}. */
|
|
69
|
+
get adminDialogTab(): StorageAdminTab;
|
|
70
|
+
/** @deprecated Use {@link AdminDialogTab}. */
|
|
71
|
+
set adminDialogTab(value: StorageAdminTab);
|
|
72
|
+
AccountToEdit: MJFileStorageAccountEntity | null;
|
|
73
|
+
/** @deprecated Use {@link AccountToEdit}. */
|
|
74
|
+
get accountToEdit(): MJFileStorageAccountEntity | null;
|
|
75
|
+
/** @deprecated Use {@link AccountToEdit}. */
|
|
76
|
+
set accountToEdit(value: MJFileStorageAccountEntity | null);
|
|
45
77
|
Provider: IMetadataProvider | null;
|
|
46
78
|
get ProviderToUse(): IMetadataProvider;
|
|
47
79
|
constructor();
|
|
@@ -58,36 +90,52 @@ export declare class StorageProvidersListComponent implements OnInit {
|
|
|
58
90
|
/**
|
|
59
91
|
* Handles account selection by the user.
|
|
60
92
|
*/
|
|
93
|
+
SelectAccount(accountWithProvider: StorageAccountWithProvider): void;
|
|
94
|
+
/** @deprecated Use {@link SelectAccount}. */
|
|
61
95
|
selectAccount(accountWithProvider: StorageAccountWithProvider): void;
|
|
62
96
|
/**
|
|
63
97
|
* Checks if an account is currently selected.
|
|
64
98
|
*/
|
|
99
|
+
IsSelected(accountWithProvider: StorageAccountWithProvider): boolean;
|
|
100
|
+
/** @deprecated Use {@link IsSelected}. */
|
|
65
101
|
isSelected(accountWithProvider: StorageAccountWithProvider): boolean;
|
|
66
102
|
/**
|
|
67
103
|
* Gets the icon class for a provider based on its name.
|
|
68
104
|
*/
|
|
105
|
+
GetProviderIcon(providerName: string): string;
|
|
106
|
+
/** @deprecated Use {@link GetProviderIcon}. */
|
|
69
107
|
getProviderIcon(providerName: string): string;
|
|
70
108
|
/**
|
|
71
109
|
* Refreshes the accounts list by forcing a reload from the database.
|
|
72
110
|
*/
|
|
111
|
+
Refresh(): void;
|
|
112
|
+
/** @deprecated Use {@link Refresh}. */
|
|
73
113
|
refresh(): void;
|
|
74
114
|
/**
|
|
75
115
|
* Opens the storage administration dialog
|
|
76
116
|
*/
|
|
117
|
+
OpenAdminDialog(tab?: StorageAdminTab, account?: MJFileStorageAccountEntity | null): void;
|
|
118
|
+
/** @deprecated Use {@link OpenAdminDialog}. */
|
|
77
119
|
openAdminDialog(tab?: StorageAdminTab, account?: MJFileStorageAccountEntity | null): void;
|
|
78
120
|
/**
|
|
79
121
|
* Closes the storage administration dialog
|
|
80
122
|
*/
|
|
123
|
+
CloseAdminDialog(): void;
|
|
124
|
+
/** @deprecated Use {@link CloseAdminDialog}. */
|
|
81
125
|
closeAdminDialog(): void;
|
|
82
126
|
/**
|
|
83
127
|
* Handles storage configuration changes from the admin dialog
|
|
84
128
|
*/
|
|
129
|
+
OnAdminAccountsChanged(): void;
|
|
130
|
+
/** @deprecated Use {@link OnAdminAccountsChanged}. */
|
|
85
131
|
onAdminAccountsChanged(): void;
|
|
86
132
|
/**
|
|
87
133
|
* Handles inline edit click on an account item
|
|
88
134
|
*/
|
|
135
|
+
OnEditAccountClick(item: StorageAccountWithProvider, event: MouseEvent): void;
|
|
136
|
+
/** @deprecated Use {@link OnEditAccountClick}. */
|
|
89
137
|
onEditAccountClick(item: StorageAccountWithProvider, event: MouseEvent): void;
|
|
90
138
|
static ɵfac: i0.ɵɵFactoryDeclaration<StorageProvidersListComponent, never>;
|
|
91
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<StorageProvidersListComponent, "mj-storage-providers-list", never, {}, { "accountSelected": "accountSelected"; }, never, never, false, never>;
|
|
139
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<StorageProvidersListComponent, "mj-storage-providers-list", never, {}, { "AccountSelected": "AccountSelected"; "accountSelected": "accountSelected"; }, never, never, false, never>;
|
|
92
140
|
}
|
|
93
141
|
//# sourceMappingURL=storage-providers-list.component.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage-providers-list.component.d.ts","sourceRoot":"","sources":["../../../src/lib/file-browser/storage-providers-list.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,YAAY,EAAE,MAAM,EAAqC,MAAM,eAAe,CAAC;AACnG,OAAO,EAAyB,0BAA0B,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAC9H,OAAO,EAAY,KAAK,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAExE,OAAO,EAAE,eAAe,EAAE,MAAM,yCAAyC,CAAC;;AAE1E;;;;;GAKG;AACH,qBAMa,6BAA8B,YAAW,MAAM;IAC1D,OAAO,CAAC,GAAG,CAA6B;IAExC;;;OAGG;IACO,eAAe,kDAAyD;IAElF;;OAEG;IACI,QAAQ,EAAE,0BAA0B,EAAE,CAAM;IAEnD;;OAEG;IACI,eAAe,EAAE,0BAA0B,GAAG,IAAI,CAAQ;IAEjE;;OAEG;IACI,SAAS,EAAE,OAAO,CAAS;IAElC;;OAEG;IACI,YAAY,EAAE,MAAM,GAAG,IAAI,CAAQ;IAE1C;;OAEG;IACI,aAAa,EAAE,OAAO,CAAS;IAEtC;;OAEG;IACI,kBAAkB,EAAE,OAAO,CAAS;
|
|
1
|
+
{"version":3,"file":"storage-providers-list.component.d.ts","sourceRoot":"","sources":["../../../src/lib/file-browser/storage-providers-list.component.ts"],"names":[],"mappings":"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"}
|
|
@@ -141,15 +141,39 @@ export class StorageProvidersListComponent {
|
|
|
141
141
|
* Emits when an account is selected by the user, or null when no accounts are available.
|
|
142
142
|
* Emits the full account-with-provider object for downstream components to use.
|
|
143
143
|
*/
|
|
144
|
-
|
|
144
|
+
AccountSelected = new EventEmitter();
|
|
145
|
+
/**
|
|
146
|
+
* @deprecated Use {@link AccountSelected}.
|
|
147
|
+
*
|
|
148
|
+
* The same emitter under the old binding name, so a template still binding
|
|
149
|
+
* (accountSelected) keeps working. Must stay AFTER AccountSelected: class fields
|
|
150
|
+
* initialise in order, and the other way round this captures undefined.
|
|
151
|
+
*/
|
|
152
|
+
accountSelected = this.AccountSelected;
|
|
145
153
|
/**
|
|
146
154
|
* All available storage accounts with their provider details.
|
|
147
155
|
*/
|
|
148
|
-
|
|
156
|
+
Accounts = [];
|
|
157
|
+
/** @deprecated Use {@link Accounts}. */
|
|
158
|
+
get accounts() {
|
|
159
|
+
return this.Accounts;
|
|
160
|
+
}
|
|
161
|
+
/** @deprecated Use {@link Accounts}. */
|
|
162
|
+
set accounts(value) {
|
|
163
|
+
this.Accounts = value;
|
|
164
|
+
}
|
|
149
165
|
/**
|
|
150
166
|
* Currently selected account.
|
|
151
167
|
*/
|
|
152
|
-
|
|
168
|
+
SelectedAccount = null;
|
|
169
|
+
/** @deprecated Use {@link SelectedAccount}. */
|
|
170
|
+
get selectedAccount() {
|
|
171
|
+
return this.SelectedAccount;
|
|
172
|
+
}
|
|
173
|
+
/** @deprecated Use {@link SelectedAccount}. */
|
|
174
|
+
set selectedAccount(value) {
|
|
175
|
+
this.SelectedAccount = value;
|
|
176
|
+
}
|
|
153
177
|
/**
|
|
154
178
|
* Loading state indicator.
|
|
155
179
|
*/
|
|
@@ -161,20 +185,52 @@ export class StorageProvidersListComponent {
|
|
|
161
185
|
/**
|
|
162
186
|
* Whether current user has admin rights to configure storage accounts or providers.
|
|
163
187
|
*/
|
|
164
|
-
|
|
188
|
+
UserCanManage = false;
|
|
189
|
+
/** @deprecated Use {@link UserCanManage}. */
|
|
190
|
+
get userCanManage() {
|
|
191
|
+
return this.UserCanManage;
|
|
192
|
+
}
|
|
193
|
+
/** @deprecated Use {@link UserCanManage}. */
|
|
194
|
+
set userCanManage(value) {
|
|
195
|
+
this.UserCanManage = value;
|
|
196
|
+
}
|
|
165
197
|
/**
|
|
166
198
|
* Admin dialog state
|
|
167
199
|
*/
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
200
|
+
IsManageDialogOpen = false;
|
|
201
|
+
/** @deprecated Use {@link IsManageDialogOpen}. */
|
|
202
|
+
get isManageDialogOpen() {
|
|
203
|
+
return this.IsManageDialogOpen;
|
|
204
|
+
}
|
|
205
|
+
/** @deprecated Use {@link IsManageDialogOpen}. */
|
|
206
|
+
set isManageDialogOpen(value) {
|
|
207
|
+
this.IsManageDialogOpen = value;
|
|
208
|
+
}
|
|
209
|
+
AdminDialogTab = 'accounts';
|
|
210
|
+
/** @deprecated Use {@link AdminDialogTab}. */
|
|
211
|
+
get adminDialogTab() {
|
|
212
|
+
return this.AdminDialogTab;
|
|
213
|
+
}
|
|
214
|
+
/** @deprecated Use {@link AdminDialogTab}. */
|
|
215
|
+
set adminDialogTab(value) {
|
|
216
|
+
this.AdminDialogTab = value;
|
|
217
|
+
}
|
|
218
|
+
AccountToEdit = null;
|
|
219
|
+
/** @deprecated Use {@link AccountToEdit}. */
|
|
220
|
+
get accountToEdit() {
|
|
221
|
+
return this.AccountToEdit;
|
|
222
|
+
}
|
|
223
|
+
/** @deprecated Use {@link AccountToEdit}. */
|
|
224
|
+
set accountToEdit(value) {
|
|
225
|
+
this.AccountToEdit = value;
|
|
226
|
+
}
|
|
171
227
|
Provider = null;
|
|
172
228
|
get ProviderToUse() {
|
|
173
229
|
return this.Provider ?? Metadata.Provider;
|
|
174
230
|
}
|
|
175
231
|
constructor() { }
|
|
176
232
|
ngOnInit() {
|
|
177
|
-
if (!this.
|
|
233
|
+
if (!this.UserCanManage) {
|
|
178
234
|
this.checkPermissions();
|
|
179
235
|
}
|
|
180
236
|
this.loadAccounts();
|
|
@@ -193,10 +249,10 @@ export class StorageProvidersListComponent {
|
|
|
193
249
|
const acctEntity = md.Entities.find(e => e.Name === 'MJ: File Storage Accounts');
|
|
194
250
|
const provCanManage = provEntity ? provEntity.GetUserPermisions(user).CanCreate || provEntity.GetUserPermisions(user).CanUpdate : false;
|
|
195
251
|
const acctCanManage = acctEntity ? acctEntity.GetUserPermisions(user).CanCreate || acctEntity.GetUserPermisions(user).CanUpdate : false;
|
|
196
|
-
this.
|
|
252
|
+
this.UserCanManage = !!(provCanManage || acctCanManage);
|
|
197
253
|
}
|
|
198
254
|
catch {
|
|
199
|
-
this.
|
|
255
|
+
this.UserCanManage = false;
|
|
200
256
|
}
|
|
201
257
|
}
|
|
202
258
|
/**
|
|
@@ -210,22 +266,22 @@ export class StorageProvidersListComponent {
|
|
|
210
266
|
const engine = FileStorageEngineBase.Instance;
|
|
211
267
|
await engine.Config(forceRefresh);
|
|
212
268
|
// Only show accounts whose provider is active
|
|
213
|
-
this.
|
|
214
|
-
if (this.
|
|
269
|
+
this.Accounts = engine.AccountsWithProviders.filter(a => a.provider.IsActive !== false);
|
|
270
|
+
if (this.Accounts.length === 0 && !forceRefresh) {
|
|
215
271
|
await engine.Config(true);
|
|
216
|
-
this.
|
|
272
|
+
this.Accounts = engine.AccountsWithProviders.filter(a => a.provider.IsActive !== false);
|
|
217
273
|
}
|
|
218
|
-
console.log('[StorageAccountsList] Loaded accounts:', this.
|
|
274
|
+
console.log('[StorageAccountsList] Loaded accounts:', this.Accounts.map(a => ({
|
|
219
275
|
name: a.account.Name,
|
|
220
276
|
provider: a.provider.Name,
|
|
221
277
|
hasCredential: !!a.account.CredentialID
|
|
222
278
|
})));
|
|
223
279
|
// Auto-select first account if available
|
|
224
|
-
if (this.
|
|
225
|
-
this.
|
|
280
|
+
if (this.Accounts.length > 0) {
|
|
281
|
+
this.SelectAccount(this.Accounts[0]);
|
|
226
282
|
}
|
|
227
283
|
else {
|
|
228
|
-
this.
|
|
284
|
+
this.AccountSelected.emit(null);
|
|
229
285
|
}
|
|
230
286
|
}
|
|
231
287
|
catch (error) {
|
|
@@ -240,20 +296,28 @@ export class StorageProvidersListComponent {
|
|
|
240
296
|
/**
|
|
241
297
|
* Handles account selection by the user.
|
|
242
298
|
*/
|
|
299
|
+
SelectAccount(accountWithProvider) {
|
|
300
|
+
this.SelectedAccount = accountWithProvider;
|
|
301
|
+
this.AccountSelected.emit(accountWithProvider);
|
|
302
|
+
}
|
|
303
|
+
/** @deprecated Use {@link SelectAccount}. */
|
|
243
304
|
selectAccount(accountWithProvider) {
|
|
244
|
-
this.
|
|
245
|
-
this.accountSelected.emit(accountWithProvider);
|
|
305
|
+
return this.SelectAccount(accountWithProvider);
|
|
246
306
|
}
|
|
247
307
|
/**
|
|
248
308
|
* Checks if an account is currently selected.
|
|
249
309
|
*/
|
|
310
|
+
IsSelected(accountWithProvider) {
|
|
311
|
+
return UUIDsEqual(this.SelectedAccount?.account.ID, accountWithProvider.account.ID);
|
|
312
|
+
}
|
|
313
|
+
/** @deprecated Use {@link IsSelected}. */
|
|
250
314
|
isSelected(accountWithProvider) {
|
|
251
|
-
return
|
|
315
|
+
return this.IsSelected(accountWithProvider);
|
|
252
316
|
}
|
|
253
317
|
/**
|
|
254
318
|
* Gets the icon class for a provider based on its name.
|
|
255
319
|
*/
|
|
256
|
-
|
|
320
|
+
GetProviderIcon(providerName) {
|
|
257
321
|
const name = providerName.toLowerCase();
|
|
258
322
|
if (name.includes('aws') || name.includes('s3')) {
|
|
259
323
|
return 'fa-brands fa-aws';
|
|
@@ -280,44 +344,68 @@ export class StorageProvidersListComponent {
|
|
|
280
344
|
return 'fa-solid fa-cloud';
|
|
281
345
|
}
|
|
282
346
|
}
|
|
347
|
+
/** @deprecated Use {@link GetProviderIcon}. */
|
|
348
|
+
getProviderIcon(providerName) {
|
|
349
|
+
return this.GetProviderIcon(providerName);
|
|
350
|
+
}
|
|
283
351
|
/**
|
|
284
352
|
* Refreshes the accounts list by forcing a reload from the database.
|
|
285
353
|
*/
|
|
286
|
-
|
|
354
|
+
Refresh() {
|
|
287
355
|
void this.loadAccounts(true);
|
|
288
356
|
}
|
|
357
|
+
/** @deprecated Use {@link Refresh}. */
|
|
358
|
+
refresh() {
|
|
359
|
+
return this.Refresh();
|
|
360
|
+
}
|
|
289
361
|
/**
|
|
290
362
|
* Opens the storage administration dialog
|
|
291
363
|
*/
|
|
292
|
-
|
|
293
|
-
this.
|
|
294
|
-
this.
|
|
295
|
-
this.
|
|
364
|
+
OpenAdminDialog(tab = 'accounts', account) {
|
|
365
|
+
this.AdminDialogTab = tab;
|
|
366
|
+
this.AccountToEdit = account ?? null;
|
|
367
|
+
this.IsManageDialogOpen = true;
|
|
296
368
|
this.cdr.markForCheck();
|
|
297
369
|
}
|
|
370
|
+
/** @deprecated Use {@link OpenAdminDialog}. */
|
|
371
|
+
openAdminDialog(tab = 'accounts', account) {
|
|
372
|
+
return this.OpenAdminDialog(tab, account);
|
|
373
|
+
}
|
|
298
374
|
/**
|
|
299
375
|
* Closes the storage administration dialog
|
|
300
376
|
*/
|
|
301
|
-
|
|
302
|
-
this.
|
|
303
|
-
this.
|
|
377
|
+
CloseAdminDialog() {
|
|
378
|
+
this.IsManageDialogOpen = false;
|
|
379
|
+
this.AccountToEdit = null;
|
|
304
380
|
this.cdr.markForCheck();
|
|
305
381
|
}
|
|
382
|
+
/** @deprecated Use {@link CloseAdminDialog}. */
|
|
383
|
+
closeAdminDialog() {
|
|
384
|
+
return this.CloseAdminDialog();
|
|
385
|
+
}
|
|
306
386
|
/**
|
|
307
387
|
* Handles storage configuration changes from the admin dialog
|
|
308
388
|
*/
|
|
389
|
+
OnAdminAccountsChanged() {
|
|
390
|
+
this.Refresh();
|
|
391
|
+
}
|
|
392
|
+
/** @deprecated Use {@link OnAdminAccountsChanged}. */
|
|
309
393
|
onAdminAccountsChanged() {
|
|
310
|
-
this.
|
|
394
|
+
return this.OnAdminAccountsChanged();
|
|
311
395
|
}
|
|
312
396
|
/**
|
|
313
397
|
* Handles inline edit click on an account item
|
|
314
398
|
*/
|
|
315
|
-
|
|
399
|
+
OnEditAccountClick(item, event) {
|
|
316
400
|
event.stopPropagation();
|
|
317
|
-
this.
|
|
401
|
+
this.OpenAdminDialog('accounts', item.account);
|
|
402
|
+
}
|
|
403
|
+
/** @deprecated Use {@link OnEditAccountClick}. */
|
|
404
|
+
onEditAccountClick(item, event) {
|
|
405
|
+
return this.OnEditAccountClick(item, event);
|
|
318
406
|
}
|
|
319
407
|
static ɵfac = function StorageProvidersListComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || StorageProvidersListComponent)(); };
|
|
320
|
-
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: StorageProvidersListComponent, selectors: [["mj-storage-providers-list"]], outputs: { accountSelected: "accountSelected" }, standalone: false, decls: 10, vars: 6, consts: [[1, "providers-list"], [1, "providers-header"], [1, "header-title"], [1, "admin-header-actions"], [1, "loading-state"], ["Variant", "error", "Icon", "fa-solid fa-triangle-exclamation", "Size", "compact", 1, "storage-providers-empty-fill", 3, "Title"], ["Icon", "fa-solid fa-folder-plus", "Size", "compact", 1, "storage-providers-empty-fill", 3, "Title", "Message"], [1, "providers-container"], [3, "Visible", "ActiveTab"], ["type", "button", "title", "Add Storage Account", 1, "admin-action-btn", 3, "click"], [1, "fa-solid", "fa-plus"], ["type", "button", "title", "Manage Storage Providers & Accounts", 1, "admin-action-btn", 3, "click"], [1, "fa-solid", "fa-gear"], ["text", "Loading accounts..."], ["type", "button", 1, "mj-empty-action-btn"], ["type", "button", 1, "mj-empty-action-btn", 3, "click"], [1, "provider-item", 3, "selected", "title"], [1, "provider-item", 3, "click", "title"], [1, "provider-icon"], [1, "provider-info"], [1, "provider-name"], [1, "provider-meta"], [1, "provider-type-badge", 3, "title"], ["title", "Supports search", 1, "search-badge"], ["title", "Credentials configured", 1, "credential-badge"], ["title", "No credentials configured", 1, "unconfigured-badge"], ["type", "button", "title", "Edit Storage Account", 1, "provider-edit-btn"], [1, "fa-solid", "fa-magnifying-glass"], [1, "fa-solid", "fa-key"], [1, "fa-solid", "fa-exclamation-triangle"], ["type", "button", "title", "Edit Storage Account", 1, "provider-edit-btn", 3, "click"], [1, "fa-solid", "fa-pen-to-square"], [3, "Close", "AccountsChanged", "Visible", "ActiveTab"]], template: function StorageProvidersListComponent_Template(rf, ctx) { if (rf & 1) {
|
|
408
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: StorageProvidersListComponent, selectors: [["mj-storage-providers-list"]], outputs: { AccountSelected: "AccountSelected", accountSelected: "accountSelected" }, standalone: false, decls: 10, vars: 6, consts: [[1, "providers-list"], [1, "providers-header"], [1, "header-title"], [1, "admin-header-actions"], [1, "loading-state"], ["Variant", "error", "Icon", "fa-solid fa-triangle-exclamation", "Size", "compact", 1, "storage-providers-empty-fill", 3, "Title"], ["Icon", "fa-solid fa-folder-plus", "Size", "compact", 1, "storage-providers-empty-fill", 3, "Title", "Message"], [1, "providers-container"], [3, "Visible", "ActiveTab"], ["type", "button", "title", "Add Storage Account", 1, "admin-action-btn", 3, "click"], [1, "fa-solid", "fa-plus"], ["type", "button", "title", "Manage Storage Providers & Accounts", 1, "admin-action-btn", 3, "click"], [1, "fa-solid", "fa-gear"], ["text", "Loading accounts..."], ["type", "button", 1, "mj-empty-action-btn"], ["type", "button", 1, "mj-empty-action-btn", 3, "click"], [1, "provider-item", 3, "selected", "title"], [1, "provider-item", 3, "click", "title"], [1, "provider-icon"], [1, "provider-info"], [1, "provider-name"], [1, "provider-meta"], [1, "provider-type-badge", 3, "title"], ["title", "Supports search", 1, "search-badge"], ["title", "Credentials configured", 1, "credential-badge"], ["title", "No credentials configured", 1, "unconfigured-badge"], ["type", "button", "title", "Edit Storage Account", 1, "provider-edit-btn"], [1, "fa-solid", "fa-magnifying-glass"], [1, "fa-solid", "fa-key"], [1, "fa-solid", "fa-exclamation-triangle"], ["type", "button", "title", "Edit Storage Account", 1, "provider-edit-btn", 3, "click"], [1, "fa-solid", "fa-pen-to-square"], [3, "Close", "AccountsChanged", "Visible", "ActiveTab"]], template: function StorageProvidersListComponent_Template(rf, ctx) { if (rf & 1) {
|
|
321
409
|
i0.ɵɵelementStart(0, "div", 0)(1, "div", 1)(2, "span", 2);
|
|
322
410
|
i0.ɵɵtext(3, "Storage Accounts");
|
|
323
411
|
i0.ɵɵelementEnd();
|
|
@@ -347,7 +435,9 @@ export class StorageProvidersListComponent {
|
|
|
347
435
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(StorageProvidersListComponent, [{
|
|
348
436
|
type: Component,
|
|
349
437
|
args: [{ standalone: false, selector: 'mj-storage-providers-list', template: "<div class=\"providers-list\">\n <!-- Header -->\n <div class=\"providers-header\">\n <span class=\"header-title\">Storage Accounts</span>\n @if (userCanManage) {\n <div class=\"admin-header-actions\">\n <button\n type=\"button\"\n class=\"admin-action-btn\"\n title=\"Add Storage Account\"\n (click)=\"openAdminDialog('accounts')\">\n <i class=\"fa-solid fa-plus\"></i>\n </button>\n <button\n type=\"button\"\n class=\"admin-action-btn\"\n title=\"Manage Storage Providers & Accounts\"\n (click)=\"openAdminDialog('accounts')\">\n <i class=\"fa-solid fa-gear\"></i>\n </button>\n </div>\n }\n </div>\n\n <!-- Loading State -->\n @if (isLoading) {\n <div class=\"loading-state\">\n <mj-loading text=\"Loading accounts...\"></mj-loading>\n </div>\n }\n\n <!-- Error State -->\n @if (errorMessage && !isLoading) {\n <mj-empty-state\n class=\"storage-providers-empty-fill\"\n Variant=\"error\"\n Icon=\"fa-solid fa-triangle-exclamation\"\n [Title]=\"errorMessage\"\n Size=\"compact\" />\n }\n\n <!-- Empty State -->\n @if (!isLoading && !errorMessage && accounts.length === 0) {\n <mj-empty-state\n class=\"storage-providers-empty-fill\"\n Icon=\"fa-solid fa-folder-plus\"\n [Title]=\"userCanManage ? 'No storage accounts configured' : 'No storage accounts available'\"\n [Message]=\"userCanManage ? 'Configure storage accounts to connect MemberJunction to cloud storage providers.' : 'Contact your administrator to set up file storage accounts.'\"\n Size=\"compact\">\n @if (userCanManage) {\n <button\n type=\"button\"\n class=\"mj-empty-action-btn\"\n (click)=\"openAdminDialog('accounts')\">\n <i class=\"fa-solid fa-plus\"></i>\n <span>Configure Account</span>\n </button>\n }\n </mj-empty-state>\n }\n\n <!-- Account List -->\n @if (!isLoading && !errorMessage && accounts.length > 0) {\n <div class=\"providers-container\">\n @for (item of accounts; track item.account.ID) {\n <div\n class=\"provider-item\"\n [class.selected]=\"isSelected(item)\"\n (click)=\"selectAccount(item)\"\n [title]=\"item.account.Description || item.account.Name\">\n <div class=\"provider-icon\">\n <i [class]=\"getProviderIcon(item.provider.Name)\"></i>\n </div>\n <div class=\"provider-info\">\n <div class=\"provider-name\">{{ item.account.Name }}</div>\n <div class=\"provider-meta\">\n <!-- Provider type badge -->\n <span class=\"provider-type-badge\" [title]=\"item.provider.Name\">\n {{ item.provider.Name }}\n </span>\n <!-- Search support badge -->\n @if (item.provider.SupportsSearch) {\n <span class=\"search-badge\" title=\"Supports search\">\n <i class=\"fa-solid fa-magnifying-glass\"></i>\n </span>\n }\n <!-- Credential configured badge -->\n @if (item.account.CredentialID) {\n <span class=\"credential-badge\" title=\"Credentials configured\">\n <i class=\"fa-solid fa-key\"></i>\n </span>\n }\n <!-- No credential warning -->\n @if (!item.account.CredentialID) {\n <span class=\"unconfigured-badge\" title=\"No credentials configured\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n </span>\n }\n </div>\n </div>\n @if (userCanManage) {\n <button\n type=\"button\"\n class=\"provider-edit-btn\"\n title=\"Edit Storage Account\"\n (click)=\"onEditAccountClick(item, $event)\">\n <i class=\"fa-solid fa-pen-to-square\"></i>\n </button>\n }\n </div>\n }\n </div>\n }\n</div>\n\n<!-- Storage Administration Dialog -->\n@if (isManageDialogOpen) {\n <mj-storage-admin-dialog\n [Visible]=\"isManageDialogOpen\"\n [ActiveTab]=\"adminDialogTab\"\n (Close)=\"closeAdminDialog()\"\n (AccountsChanged)=\"onAdminAccountsChanged()\">\n </mj-storage-admin-dialog>\n}\n", styles: ["/* ===========================\n Storage Providers List\n =========================== */\n\n.providers-list {\n display: flex;\n flex-direction: column;\n height: 100%;\n overflow: hidden;\n}\n\n/* Header with Add Button */\n.providers-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 16px;\n border-bottom: 1px solid var(--mj-border-default);\n background-color: var(--mj-bg-surface-card);\n}\n\n.header-title {\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-primary);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.admin-header-actions {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.admin-action-btn {\n width: 26px;\n height: 26px;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm, 6px);\n background-color: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.15s ease;\n font-size: 11px;\n}\n\n.admin-action-btn:hover {\n background-color: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n}\n\n.provider-edit-btn {\n width: 24px;\n height: 24px;\n border: none;\n border-radius: var(--mj-radius-sm, 4px);\n background-color: transparent;\n color: var(--mj-text-muted);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.15s ease;\n font-size: 11px;\n opacity: 0;\n margin-left: 6px;\n flex-shrink: 0;\n}\n\n.provider-item:hover .provider-edit-btn {\n opacity: 1;\n}\n\n.provider-edit-btn:hover {\n background-color: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n.mj-empty-action-btn {\n margin-top: 12px;\n padding: 6px 12px;\n border-radius: var(--mj-radius-sm, 6px);\n border: 1px solid transparent;\n background-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse, #ffffff);\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n transition: background-color 0.12s;\n}\n\n.mj-empty-action-btn:hover {\n background-color: var(--mj-brand-primary-hover);\n}\n\n.add-provider-btn {\n width: 28px;\n height: 28px;\n border: none;\n border-radius: 50%;\n background-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s ease;\n font-size: 14px;\n}\n\n.add-provider-btn:hover:not(:disabled) {\n background-color: var(--mj-brand-primary-hover);\n transform: scale(1.05);\n}\n\n.add-provider-btn:disabled {\n background-color: var(--mj-border-strong);\n cursor: not-allowed;\n}\n\n/* Loading State */\n.loading-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 24px;\n text-align: center;\n color: var(--mj-text-muted);\n flex: 1;\n}\n\n/* Empty/error states fill the panel vertically (flex-parent gotcha) */\n.storage-providers-empty-fill {\n flex: 1;\n}\n\n/* Provider List Container */\n.providers-container {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n}\n\n/* Individual Provider Item */\n.provider-item {\n display: flex;\n align-items: center;\n padding: 12px 16px;\n cursor: pointer;\n transition: all 0.2s ease;\n border-bottom: 1px solid var(--mj-bg-surface-sunken);\n position: relative;\n}\n\n.provider-item:hover {\n background-color: var(--mj-bg-surface-card);\n}\n\n.provider-item.selected {\n background-color: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n border-left: 3px solid var(--mj-brand-primary);\n}\n\n.provider-item.selected::before {\n content: '';\n position: absolute;\n left: 0;\n top: 0;\n bottom: 0;\n width: 3px;\n background-color: var(--mj-brand-primary);\n}\n\n/* Provider Icon */\n.provider-icon {\n flex-shrink: 0;\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 20px;\n color: var(--mj-text-secondary);\n margin-right: 12px;\n}\n\n.provider-item.selected .provider-icon {\n color: var(--mj-brand-primary);\n}\n\n/* Provider Info */\n.provider-info {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.provider-name {\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.provider-item.selected .provider-name {\n color: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n.provider-meta {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 11px;\n color: var(--mj-text-muted);\n}\n\n/* Default Badge */\n.default-badge {\n display: inline-flex;\n align-items: center;\n color: var(--mj-status-warning);\n}\n\n.default-badge i {\n font-size: 10px;\n}\n\n/* Provider Type Badge */\n.provider-type-badge {\n display: inline-flex;\n align-items: center;\n padding: 2px 6px;\n background-color: var(--mj-bg-surface-sunken);\n border-radius: 10px;\n font-size: 10px;\n color: var(--mj-text-secondary);\n max-width: 100px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.provider-item.selected .provider-type-badge {\n background-color: color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n color: var(--mj-brand-primary);\n}\n\n/* Search Badge */\n.search-badge {\n display: inline-flex;\n align-items: center;\n padding: 2px 6px;\n background-color: var(--mj-bg-surface-sunken);\n border-radius: 10px;\n font-size: 10px;\n color: var(--mj-text-secondary);\n}\n\n.provider-item.selected .search-badge {\n background-color: color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n color: var(--mj-brand-primary);\n}\n\n.search-badge i {\n font-size: 9px;\n}\n\n/* Credential Badge */\n.credential-badge {\n display: inline-flex;\n align-items: center;\n padding: 2px 6px;\n background-color: color-mix(in srgb, var(--mj-status-success) 8%, var(--mj-bg-surface));\n border-radius: 10px;\n font-size: 10px;\n color: var(--mj-status-success);\n}\n\n.provider-item.selected .credential-badge {\n background-color: color-mix(in srgb, var(--mj-status-success) 15%, transparent);\n color: var(--mj-status-success);\n}\n\n.credential-badge i {\n font-size: 9px;\n}\n\n/* Unconfigured Badge */\n.unconfigured-badge {\n display: inline-flex;\n align-items: center;\n color: var(--mj-status-warning);\n}\n\n.unconfigured-badge i {\n font-size: 10px;\n}\n\n/* OAuth Status Badges */\n.oauth-connected-badge {\n display: inline-flex;\n align-items: center;\n padding: 2px 6px;\n background-color: color-mix(in srgb, var(--mj-status-success) 8%, var(--mj-bg-surface));\n border-radius: 10px;\n font-size: 10px;\n color: var(--mj-status-success);\n}\n\n.oauth-connected-badge i {\n font-size: 9px;\n}\n\n.oauth-disconnected-badge {\n display: inline-flex;\n align-items: center;\n padding: 2px 6px;\n background-color: color-mix(in srgb, var(--mj-status-warning) 8%, var(--mj-bg-surface));\n border-radius: 10px;\n font-size: 10px;\n color: var(--mj-status-warning);\n}\n\n.oauth-disconnected-badge i {\n font-size: 9px;\n}\n\n/* Provider Actions (OAuth connect/disconnect buttons) */\n.provider-actions {\n display: flex;\n align-items: center;\n gap: 4px;\n margin-left: 8px;\n}\n\n.connect-btn,\n.disconnect-btn {\n width: 26px;\n height: 26px;\n border: none;\n border-radius: 50%;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s ease;\n font-size: 12px;\n}\n\n.connect-btn {\n background-color: color-mix(in srgb, var(--mj-status-success) 8%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.connect-btn:hover:not(:disabled) {\n background-color: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n transform: scale(1.05);\n}\n\n.connect-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.disconnect-btn {\n background-color: transparent;\n color: var(--mj-text-muted);\n}\n\n.disconnect-btn:hover {\n background-color: color-mix(in srgb, var(--mj-status-error) 8%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n}\n\n/* Remove Provider Button */\n.remove-provider-btn {\n width: 24px;\n height: 24px;\n border: none;\n border-radius: 50%;\n background-color: transparent;\n color: var(--mj-text-disabled);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s ease;\n font-size: 12px;\n opacity: 0;\n margin-left: 8px;\n}\n\n.provider-item:hover .remove-provider-btn {\n opacity: 1;\n}\n\n.remove-provider-btn:hover:not(:disabled) {\n background-color: color-mix(in srgb, var(--mj-status-error) 8%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n}\n\n.remove-provider-btn:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n}\n\n/* Scrollbar Styling */\n.providers-container::-webkit-scrollbar {\n width: 6px;\n}\n\n.providers-container::-webkit-scrollbar-track {\n background: transparent;\n}\n\n.providers-container::-webkit-scrollbar-thumb {\n background: var(--mj-border-strong);\n border-radius: 3px;\n}\n\n.providers-container::-webkit-scrollbar-thumb:hover {\n background: var(--mj-text-disabled);\n}\n\n/* ===========================\n Add Provider Dialog\n =========================== */\n\n.add-provider-dialog {\n min-height: 150px;\n}\n\n.dialog-loading {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 40px 20px;\n}\n\n.dialog-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 30px 20px;\n text-align: center;\n}\n\n.dialog-empty-icon {\n font-size: 40px;\n color: var(--mj-status-success);\n margin-bottom: 12px;\n}\n\n.dialog-empty-message {\n font-size: 16px;\n font-weight: 500;\n color: var(--mj-text-primary);\n margin: 0 0 4px 0;\n}\n\n.dialog-empty-hint {\n font-size: 13px;\n color: var(--mj-text-muted);\n margin: 0;\n}\n\n.dialog-instruction {\n font-size: 13px;\n color: var(--mj-text-secondary);\n margin: 0 0 12px 0;\n padding: 0 4px;\n}\n\n.available-providers {\n max-height: 350px;\n overflow-y: auto;\n}\n\n.available-provider-item {\n display: flex;\n align-items: center;\n padding: 12px;\n border-radius: 8px;\n cursor: pointer;\n transition: all 0.2s ease;\n margin-bottom: 8px;\n border: 1px solid var(--mj-border-default);\n}\n\n.available-provider-item:hover:not(.disabled) {\n background-color: var(--mj-bg-surface-card);\n border-color: var(--mj-brand-primary);\n}\n\n.available-provider-item.disabled {\n opacity: 0.6;\n cursor: not-allowed;\n background-color: var(--mj-bg-surface-card);\n}\n\n.available-provider-item .provider-icon {\n margin-right: 12px;\n}\n\n.available-provider-item .provider-info {\n flex: 1;\n}\n\n.available-provider-item .provider-name {\n font-size: 14px;\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.available-provider-item .provider-description {\n font-size: 12px;\n color: var(--mj-text-muted);\n margin-top: 2px;\n line-height: 1.3;\n}\n\n.available-provider-item .provider-warning {\n font-size: 11px;\n color: var(--mj-status-warning);\n margin-top: 4px;\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.available-provider-item .provider-warning i {\n font-size: 10px;\n}\n\n/* OAuth hint in add provider dialog */\n.available-provider-item .oauth-hint {\n font-size: 11px;\n color: var(--mj-brand-primary);\n margin-top: 4px;\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.available-provider-item .oauth-hint i {\n font-size: 10px;\n}\n\n.available-provider-item.oauth-provider {\n border-color: var(--mj-brand-primary);\n border-style: dashed;\n}\n\n.available-provider-item.oauth-provider:hover:not(.disabled) {\n background-color: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n border-style: solid;\n}\n\n.add-indicator {\n width: 28px;\n height: 28px;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-brand-primary);\n font-size: 14px;\n}\n\n/* Responsive Adjustments */\n@media screen and (max-width: 768px) {\n .providers-header {\n padding: 10px 12px;\n }\n\n .header-title {\n font-size: 12px;\n }\n\n .add-provider-btn {\n width: 26px;\n height: 26px;\n font-size: 12px;\n }\n\n .provider-item {\n padding: 10px 12px;\n }\n\n .provider-icon {\n width: 28px;\n height: 28px;\n font-size: 18px;\n margin-right: 10px;\n }\n\n .provider-name {\n font-size: 12px;\n }\n\n .provider-status {\n font-size: 10px;\n }\n\n .remove-provider-btn {\n width: 22px;\n height: 22px;\n font-size: 11px;\n opacity: 1;\n }\n}\n"] }]
|
|
350
|
-
}], () => [], {
|
|
438
|
+
}], () => [], { AccountSelected: [{
|
|
439
|
+
type: Output
|
|
440
|
+
}], accountSelected: [{
|
|
351
441
|
type: Output
|
|
352
442
|
}] }); })();
|
|
353
443
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(StorageProvidersListComponent, { className: "StorageProvidersListComponent", filePath: "src/lib/file-browser/storage-providers-list.component.ts", lineNumber: 19 }); })();
|