@memberjunction/ng-file-storage 3.1.0 → 3.2.0
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/file-browser/file-browser-demo.component.d.ts +10 -0
- package/dist/lib/file-browser/file-browser-demo.component.d.ts.map +1 -0
- package/dist/lib/file-browser/file-browser-demo.component.js +39 -0
- package/dist/lib/file-browser/file-browser-demo.component.js.map +1 -0
- package/dist/lib/file-browser/file-browser-resource.component.d.ts +30 -0
- package/dist/lib/file-browser/file-browser-resource.component.d.ts.map +1 -0
- package/dist/lib/file-browser/file-browser-resource.component.js +82 -0
- package/dist/lib/file-browser/file-browser-resource.component.js.map +1 -0
- package/dist/lib/file-browser/file-browser.component.d.ts +69 -0
- package/dist/lib/file-browser/file-browser.component.d.ts.map +1 -0
- package/dist/lib/file-browser/file-browser.component.js +132 -0
- package/dist/lib/file-browser/file-browser.component.js.map +1 -0
- package/dist/lib/file-browser/file-grid.component.d.ts +429 -0
- package/dist/lib/file-browser/file-grid.component.d.ts.map +1 -0
- package/dist/lib/file-browser/file-grid.component.js +2394 -0
- package/dist/lib/file-browser/file-grid.component.js.map +1 -0
- package/dist/lib/file-browser/folder-tree.component.d.ts +115 -0
- package/dist/lib/file-browser/folder-tree.component.d.ts.map +1 -0
- package/dist/lib/file-browser/folder-tree.component.js +364 -0
- package/dist/lib/file-browser/folder-tree.component.js.map +1 -0
- package/dist/lib/file-browser/storage-providers-list.component.d.ts +59 -0
- package/dist/lib/file-browser/storage-providers-list.component.d.ts.map +1 -0
- package/dist/lib/file-browser/storage-providers-list.component.js +235 -0
- package/dist/lib/file-browser/storage-providers-list.component.js.map +1 -0
- package/dist/lib/file-upload/file-upload.d.ts +19 -19
- package/dist/lib/module.d.ts +25 -18
- package/dist/lib/module.d.ts.map +1 -1
- package/dist/lib/module.js +44 -4
- package/dist/lib/module.js.map +1 -1
- package/dist/public-api.d.ts +3 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +6 -0
- package/dist/public-api.js.map +1 -1
- package/package.json +9 -8
|
@@ -0,0 +1,2394 @@
|
|
|
1
|
+
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
|
2
|
+
import { GraphQLDataProvider, GraphQLFileStorageClient } from '@memberjunction/graphql-dataprovider';
|
|
3
|
+
import { FileStorageEngine } from '@memberjunction/core-entities';
|
|
4
|
+
import * as i0 from "@angular/core";
|
|
5
|
+
import * as i1 from "@angular/common";
|
|
6
|
+
import * as i2 from "@angular/forms";
|
|
7
|
+
import * as i3 from "@memberjunction/ng-shared-generic";
|
|
8
|
+
import * as i4 from "@progress/kendo-angular-label";
|
|
9
|
+
function FileGridComponent_div_1_button_12_Template(rf, ctx) { if (rf & 1) {
|
|
10
|
+
const _r3 = i0.ɵɵgetCurrentView();
|
|
11
|
+
i0.ɵɵelementStart(0, "button", 30);
|
|
12
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_1_button_12_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.clearSearch()); });
|
|
13
|
+
i0.ɵɵelement(1, "i", 31);
|
|
14
|
+
i0.ɵɵelementEnd();
|
|
15
|
+
} }
|
|
16
|
+
function FileGridComponent_div_1_Template(rf, ctx) { if (rf & 1) {
|
|
17
|
+
const _r1 = i0.ɵɵgetCurrentView();
|
|
18
|
+
i0.ɵɵelementStart(0, "div", 12)(1, "div", 13)(2, "button", 14);
|
|
19
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_1_Template_button_click_2_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.navigateUp()); });
|
|
20
|
+
i0.ɵɵelement(3, "i", 15);
|
|
21
|
+
i0.ɵɵelementEnd();
|
|
22
|
+
i0.ɵɵelementStart(4, "button", 16);
|
|
23
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_1_Template_button_click_4_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.refresh()); });
|
|
24
|
+
i0.ɵɵelement(5, "i", 17);
|
|
25
|
+
i0.ɵɵelementEnd()();
|
|
26
|
+
i0.ɵɵelementStart(6, "div", 18);
|
|
27
|
+
i0.ɵɵelement(7, "i", 19);
|
|
28
|
+
i0.ɵɵelementStart(8, "span", 20);
|
|
29
|
+
i0.ɵɵtext(9);
|
|
30
|
+
i0.ɵɵelementEnd()();
|
|
31
|
+
i0.ɵɵelementStart(10, "div", 21)(11, "input", 22);
|
|
32
|
+
i0.ɵɵtwoWayListener("ngModelChange", function FileGridComponent_div_1_Template_input_ngModelChange_11_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r1.searchQuery, $event) || (ctx_r1.searchQuery = $event); return i0.ɵɵresetView($event); });
|
|
33
|
+
i0.ɵɵlistener("ngModelChange", function FileGridComponent_div_1_Template_input_ngModelChange_11_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onSearchChange($event)); });
|
|
34
|
+
i0.ɵɵelementEnd();
|
|
35
|
+
i0.ɵɵtemplate(12, FileGridComponent_div_1_button_12_Template, 2, 0, "button", 23);
|
|
36
|
+
i0.ɵɵelementStart(13, "select", 24);
|
|
37
|
+
i0.ɵɵtwoWayListener("ngModelChange", function FileGridComponent_div_1_Template_select_ngModelChange_13_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r1.fileTypeFilter, $event) || (ctx_r1.fileTypeFilter = $event); return i0.ɵɵresetView($event); });
|
|
38
|
+
i0.ɵɵlistener("ngModelChange", function FileGridComponent_div_1_Template_select_ngModelChange_13_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onFileTypeFilterChange($event)); });
|
|
39
|
+
i0.ɵɵelementStart(14, "option", 25);
|
|
40
|
+
i0.ɵɵtext(15, "All");
|
|
41
|
+
i0.ɵɵelementEnd();
|
|
42
|
+
i0.ɵɵelementStart(16, "option", 26);
|
|
43
|
+
i0.ɵɵtext(17, "Files");
|
|
44
|
+
i0.ɵɵelementEnd();
|
|
45
|
+
i0.ɵɵelementStart(18, "option", 27);
|
|
46
|
+
i0.ɵɵtext(19, "Folders");
|
|
47
|
+
i0.ɵɵelementEnd()();
|
|
48
|
+
i0.ɵɵelementStart(20, "button", 28);
|
|
49
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_1_Template_button_click_20_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.toggleMultiAccountSearchMode()); });
|
|
50
|
+
i0.ɵɵelement(21, "i", 29);
|
|
51
|
+
i0.ɵɵelementEnd()()();
|
|
52
|
+
} if (rf & 2) {
|
|
53
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
54
|
+
i0.ɵɵadvance(2);
|
|
55
|
+
i0.ɵɵproperty("disabled", !ctx_r1.canNavigateUp());
|
|
56
|
+
i0.ɵɵadvance(7);
|
|
57
|
+
i0.ɵɵtextInterpolate1("", ctx_r1.folderPath || "Root", " /");
|
|
58
|
+
i0.ɵɵadvance(2);
|
|
59
|
+
i0.ɵɵtwoWayProperty("ngModel", ctx_r1.searchQuery);
|
|
60
|
+
i0.ɵɵproperty("disabled", ctx_r1.isLoading || ctx_r1.isMultiProviderSearchMode);
|
|
61
|
+
i0.ɵɵadvance();
|
|
62
|
+
i0.ɵɵproperty("ngIf", ctx_r1.searchQuery && !ctx_r1.isMultiProviderSearchMode);
|
|
63
|
+
i0.ɵɵadvance();
|
|
64
|
+
i0.ɵɵtwoWayProperty("ngModel", ctx_r1.fileTypeFilter);
|
|
65
|
+
i0.ɵɵproperty("disabled", ctx_r1.isLoading || ctx_r1.isMultiProviderSearchMode);
|
|
66
|
+
i0.ɵɵadvance(7);
|
|
67
|
+
i0.ɵɵclassProp("active", ctx_r1.isMultiProviderSearchMode);
|
|
68
|
+
} }
|
|
69
|
+
function FileGridComponent_div_2_i_11_Template(rf, ctx) { if (rf & 1) {
|
|
70
|
+
i0.ɵɵelement(0, "i", 48);
|
|
71
|
+
} }
|
|
72
|
+
function FileGridComponent_div_2_i_12_Template(rf, ctx) { if (rf & 1) {
|
|
73
|
+
i0.ɵɵelement(0, "i", 49);
|
|
74
|
+
} }
|
|
75
|
+
function FileGridComponent_div_2_label_18_span_6_Template(rf, ctx) { if (rf & 1) {
|
|
76
|
+
i0.ɵɵelementStart(0, "span", 55);
|
|
77
|
+
i0.ɵɵtext(1, "(no search)");
|
|
78
|
+
i0.ɵɵelementEnd();
|
|
79
|
+
} }
|
|
80
|
+
function FileGridComponent_div_2_label_18_Template(rf, ctx) { if (rf & 1) {
|
|
81
|
+
const _r5 = i0.ɵɵgetCurrentView();
|
|
82
|
+
i0.ɵɵelementStart(0, "label", 50)(1, "input", 51);
|
|
83
|
+
i0.ɵɵlistener("change", function FileGridComponent_div_2_label_18_Template_input_change_1_listener() { const a_r6 = i0.ɵɵrestoreView(_r5).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.toggleSearchAccount(a_r6.account.ID)); });
|
|
84
|
+
i0.ɵɵelementEnd();
|
|
85
|
+
i0.ɵɵelementStart(2, "span", 52);
|
|
86
|
+
i0.ɵɵtext(3);
|
|
87
|
+
i0.ɵɵelementEnd();
|
|
88
|
+
i0.ɵɵelementStart(4, "span", 53);
|
|
89
|
+
i0.ɵɵtext(5);
|
|
90
|
+
i0.ɵɵelementEnd();
|
|
91
|
+
i0.ɵɵtemplate(6, FileGridComponent_div_2_label_18_span_6_Template, 2, 0, "span", 54);
|
|
92
|
+
i0.ɵɵelementEnd();
|
|
93
|
+
} if (rf & 2) {
|
|
94
|
+
const a_r6 = ctx.$implicit;
|
|
95
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
96
|
+
i0.ɵɵclassProp("disabled", !ctx_r1.accountSupportsSearch(a_r6));
|
|
97
|
+
i0.ɵɵproperty("title", ctx_r1.accountSupportsSearch(a_r6) ? "" : "This account does not support search");
|
|
98
|
+
i0.ɵɵadvance();
|
|
99
|
+
i0.ɵɵproperty("checked", ctx_r1.isAccountSelectedForSearch(a_r6.account.ID))("disabled", !ctx_r1.accountSupportsSearch(a_r6));
|
|
100
|
+
i0.ɵɵadvance(2);
|
|
101
|
+
i0.ɵɵtextInterpolate(a_r6.account.Name);
|
|
102
|
+
i0.ɵɵadvance(2);
|
|
103
|
+
i0.ɵɵtextInterpolate1("(", a_r6.provider.Name, ")");
|
|
104
|
+
i0.ɵɵadvance();
|
|
105
|
+
i0.ɵɵproperty("ngIf", !ctx_r1.accountSupportsSearch(a_r6));
|
|
106
|
+
} }
|
|
107
|
+
function FileGridComponent_div_2_div_19_span_6_Template(rf, ctx) { if (rf & 1) {
|
|
108
|
+
i0.ɵɵelementStart(0, "span", 64);
|
|
109
|
+
i0.ɵɵtext(1);
|
|
110
|
+
i0.ɵɵelementEnd();
|
|
111
|
+
} if (rf & 2) {
|
|
112
|
+
const ctx_r1 = i0.ɵɵnextContext(3);
|
|
113
|
+
i0.ɵɵadvance();
|
|
114
|
+
i0.ɵɵtextInterpolate1(" , ", ctx_r1.multiProviderSearchResults.failedAccounts, " failed ");
|
|
115
|
+
} }
|
|
116
|
+
function FileGridComponent_div_2_div_19_div_12_span_5_Template(rf, ctx) { if (rf & 1) {
|
|
117
|
+
i0.ɵɵelementStart(0, "span", 58);
|
|
118
|
+
i0.ɵɵtext(1);
|
|
119
|
+
i0.ɵɵelementEnd();
|
|
120
|
+
} if (rf & 2) {
|
|
121
|
+
const accountResult_r8 = i0.ɵɵnextContext().$implicit;
|
|
122
|
+
i0.ɵɵadvance();
|
|
123
|
+
i0.ɵɵtextInterpolate1(" (", accountResult_r8.results.length, " result(s)) ");
|
|
124
|
+
} }
|
|
125
|
+
function FileGridComponent_div_2_div_19_div_12_span_6_Template(rf, ctx) { if (rf & 1) {
|
|
126
|
+
i0.ɵɵelementStart(0, "span", 72);
|
|
127
|
+
i0.ɵɵelement(1, "i", 73);
|
|
128
|
+
i0.ɵɵtext(2);
|
|
129
|
+
i0.ɵɵelementEnd();
|
|
130
|
+
} if (rf & 2) {
|
|
131
|
+
const accountResult_r8 = i0.ɵɵnextContext().$implicit;
|
|
132
|
+
i0.ɵɵadvance(2);
|
|
133
|
+
i0.ɵɵtextInterpolate1(" ", accountResult_r8.errorMessage, " ");
|
|
134
|
+
} }
|
|
135
|
+
function FileGridComponent_div_2_div_19_div_12_div_7_div_1_Template(rf, ctx) { if (rf & 1) {
|
|
136
|
+
i0.ɵɵelementStart(0, "div", 76);
|
|
137
|
+
i0.ɵɵelement(1, "i", 77);
|
|
138
|
+
i0.ɵɵelementStart(2, "div", 78)(3, "span", 79);
|
|
139
|
+
i0.ɵɵtext(4);
|
|
140
|
+
i0.ɵɵelementEnd();
|
|
141
|
+
i0.ɵɵelementStart(5, "span", 80);
|
|
142
|
+
i0.ɵɵtext(6);
|
|
143
|
+
i0.ɵɵelementEnd()();
|
|
144
|
+
i0.ɵɵelementStart(7, "span", 81);
|
|
145
|
+
i0.ɵɵtext(8);
|
|
146
|
+
i0.ɵɵelementEnd();
|
|
147
|
+
i0.ɵɵelementStart(9, "span", 82);
|
|
148
|
+
i0.ɵɵtext(10);
|
|
149
|
+
i0.ɵɵelementEnd()();
|
|
150
|
+
} if (rf & 2) {
|
|
151
|
+
const file_r9 = ctx.$implicit;
|
|
152
|
+
const ctx_r1 = i0.ɵɵnextContext(5);
|
|
153
|
+
i0.ɵɵadvance();
|
|
154
|
+
i0.ɵɵclassMap(ctx_r1.getSearchResultIcon(file_r9));
|
|
155
|
+
i0.ɵɵadvance(3);
|
|
156
|
+
i0.ɵɵtextInterpolate(file_r9.name);
|
|
157
|
+
i0.ɵɵadvance(2);
|
|
158
|
+
i0.ɵɵtextInterpolate(file_r9.path);
|
|
159
|
+
i0.ɵɵadvance(2);
|
|
160
|
+
i0.ɵɵtextInterpolate(ctx_r1.formatSearchResultSize(file_r9.size));
|
|
161
|
+
i0.ɵɵadvance(2);
|
|
162
|
+
i0.ɵɵtextInterpolate(ctx_r1.formatSearchResultDate(file_r9.lastModified));
|
|
163
|
+
} }
|
|
164
|
+
function FileGridComponent_div_2_div_19_div_12_div_7_Template(rf, ctx) { if (rf & 1) {
|
|
165
|
+
i0.ɵɵelementStart(0, "div", 74);
|
|
166
|
+
i0.ɵɵtemplate(1, FileGridComponent_div_2_div_19_div_12_div_7_div_1_Template, 11, 6, "div", 75);
|
|
167
|
+
i0.ɵɵelementEnd();
|
|
168
|
+
} if (rf & 2) {
|
|
169
|
+
const accountResult_r8 = i0.ɵɵnextContext().$implicit;
|
|
170
|
+
i0.ɵɵadvance();
|
|
171
|
+
i0.ɵɵproperty("ngForOf", accountResult_r8.results);
|
|
172
|
+
} }
|
|
173
|
+
function FileGridComponent_div_2_div_19_div_12_div_8_Template(rf, ctx) { if (rf & 1) {
|
|
174
|
+
i0.ɵɵelementStart(0, "div", 83);
|
|
175
|
+
i0.ɵɵtext(1, " No matching files found ");
|
|
176
|
+
i0.ɵɵelementEnd();
|
|
177
|
+
} }
|
|
178
|
+
function FileGridComponent_div_2_div_19_div_12_Template(rf, ctx) { if (rf & 1) {
|
|
179
|
+
i0.ɵɵelementStart(0, "div", 65)(1, "div", 66);
|
|
180
|
+
i0.ɵɵelement(2, "i", 67);
|
|
181
|
+
i0.ɵɵelementStart(3, "span", 52);
|
|
182
|
+
i0.ɵɵtext(4);
|
|
183
|
+
i0.ɵɵelementEnd();
|
|
184
|
+
i0.ɵɵtemplate(5, FileGridComponent_div_2_div_19_div_12_span_5_Template, 2, 1, "span", 68)(6, FileGridComponent_div_2_div_19_div_12_span_6_Template, 3, 1, "span", 69);
|
|
185
|
+
i0.ɵɵelementEnd();
|
|
186
|
+
i0.ɵɵtemplate(7, FileGridComponent_div_2_div_19_div_12_div_7_Template, 2, 1, "div", 70)(8, FileGridComponent_div_2_div_19_div_12_div_8_Template, 2, 0, "div", 71);
|
|
187
|
+
i0.ɵɵelementEnd();
|
|
188
|
+
} if (rf & 2) {
|
|
189
|
+
const accountResult_r8 = ctx.$implicit;
|
|
190
|
+
i0.ɵɵclassProp("failed", !accountResult_r8.success);
|
|
191
|
+
i0.ɵɵadvance(4);
|
|
192
|
+
i0.ɵɵtextInterpolate(accountResult_r8.accountName);
|
|
193
|
+
i0.ɵɵadvance();
|
|
194
|
+
i0.ɵɵproperty("ngIf", accountResult_r8.success);
|
|
195
|
+
i0.ɵɵadvance();
|
|
196
|
+
i0.ɵɵproperty("ngIf", !accountResult_r8.success);
|
|
197
|
+
i0.ɵɵadvance();
|
|
198
|
+
i0.ɵɵproperty("ngIf", accountResult_r8.success && accountResult_r8.results.length > 0);
|
|
199
|
+
i0.ɵɵadvance();
|
|
200
|
+
i0.ɵɵproperty("ngIf", accountResult_r8.success && accountResult_r8.results.length === 0);
|
|
201
|
+
} }
|
|
202
|
+
function FileGridComponent_div_2_div_19_Template(rf, ctx) { if (rf & 1) {
|
|
203
|
+
const _r7 = i0.ɵɵgetCurrentView();
|
|
204
|
+
i0.ɵɵelementStart(0, "div", 56)(1, "div", 57)(2, "span", 58);
|
|
205
|
+
i0.ɵɵtext(3);
|
|
206
|
+
i0.ɵɵelementEnd();
|
|
207
|
+
i0.ɵɵelementStart(4, "span", 59);
|
|
208
|
+
i0.ɵɵtext(5);
|
|
209
|
+
i0.ɵɵtemplate(6, FileGridComponent_div_2_div_19_span_6_Template, 2, 1, "span", 60);
|
|
210
|
+
i0.ɵɵtext(7, ") ");
|
|
211
|
+
i0.ɵɵelementEnd();
|
|
212
|
+
i0.ɵɵelementStart(8, "button", 61);
|
|
213
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_2_div_19_Template_button_click_8_listener() { i0.ɵɵrestoreView(_r7); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.clearMultiProviderSearch()); });
|
|
214
|
+
i0.ɵɵelement(9, "i", 31);
|
|
215
|
+
i0.ɵɵtext(10, " Clear ");
|
|
216
|
+
i0.ɵɵelementEnd()();
|
|
217
|
+
i0.ɵɵelementStart(11, "div", 62);
|
|
218
|
+
i0.ɵɵtemplate(12, FileGridComponent_div_2_div_19_div_12_Template, 9, 7, "div", 63);
|
|
219
|
+
i0.ɵɵelementEnd()();
|
|
220
|
+
} if (rf & 2) {
|
|
221
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
222
|
+
i0.ɵɵadvance(3);
|
|
223
|
+
i0.ɵɵtextInterpolate1("", ctx_r1.multiProviderSearchResults.totalResultsReturned, " result(s) found");
|
|
224
|
+
i0.ɵɵadvance(2);
|
|
225
|
+
i0.ɵɵtextInterpolate1(" (", ctx_r1.multiProviderSearchResults.successfulAccounts, " account(s) searched ");
|
|
226
|
+
i0.ɵɵadvance();
|
|
227
|
+
i0.ɵɵproperty("ngIf", ctx_r1.multiProviderSearchResults.failedAccounts > 0);
|
|
228
|
+
i0.ɵɵadvance(6);
|
|
229
|
+
i0.ɵɵproperty("ngForOf", ctx_r1.multiProviderSearchResults.accountResults);
|
|
230
|
+
} }
|
|
231
|
+
function FileGridComponent_div_2_div_20_Template(rf, ctx) { if (rf & 1) {
|
|
232
|
+
i0.ɵɵelementStart(0, "div", 84);
|
|
233
|
+
i0.ɵɵelement(1, "mj-loading", 85);
|
|
234
|
+
i0.ɵɵelementEnd();
|
|
235
|
+
} }
|
|
236
|
+
function FileGridComponent_div_2_Template(rf, ctx) { if (rf & 1) {
|
|
237
|
+
const _r4 = i0.ɵɵgetCurrentView();
|
|
238
|
+
i0.ɵɵelementStart(0, "div", 32)(1, "div", 33)(2, "h3");
|
|
239
|
+
i0.ɵɵelement(3, "i", 34);
|
|
240
|
+
i0.ɵɵtext(4, " Search Across Providers");
|
|
241
|
+
i0.ɵɵelementEnd();
|
|
242
|
+
i0.ɵɵelementStart(5, "button", 35);
|
|
243
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_2_Template_button_click_5_listener() { i0.ɵɵrestoreView(_r4); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.toggleMultiAccountSearchMode()); });
|
|
244
|
+
i0.ɵɵelement(6, "i", 31);
|
|
245
|
+
i0.ɵɵelementEnd()();
|
|
246
|
+
i0.ɵɵelementStart(7, "div", 36)(8, "div", 37)(9, "input", 38);
|
|
247
|
+
i0.ɵɵtwoWayListener("ngModelChange", function FileGridComponent_div_2_Template_input_ngModelChange_9_listener($event) { i0.ɵɵrestoreView(_r4); const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r1.multiProviderSearchQuery, $event) || (ctx_r1.multiProviderSearchQuery = $event); return i0.ɵɵresetView($event); });
|
|
248
|
+
i0.ɵɵlistener("keydown.enter", function FileGridComponent_div_2_Template_input_keydown_enter_9_listener() { i0.ɵɵrestoreView(_r4); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.executeMultiAccountSearch()); });
|
|
249
|
+
i0.ɵɵelementEnd();
|
|
250
|
+
i0.ɵɵelementStart(10, "button", 39);
|
|
251
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_2_Template_button_click_10_listener() { i0.ɵɵrestoreView(_r4); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.executeMultiAccountSearch()); });
|
|
252
|
+
i0.ɵɵtemplate(11, FileGridComponent_div_2_i_11_Template, 1, 0, "i", 40)(12, FileGridComponent_div_2_i_12_Template, 1, 0, "i", 41);
|
|
253
|
+
i0.ɵɵtext(13);
|
|
254
|
+
i0.ɵɵelementEnd()();
|
|
255
|
+
i0.ɵɵelementStart(14, "div", 42)(15, "label", 43);
|
|
256
|
+
i0.ɵɵtext(16, "Select accounts to search:");
|
|
257
|
+
i0.ɵɵelementEnd();
|
|
258
|
+
i0.ɵɵelementStart(17, "div", 44);
|
|
259
|
+
i0.ɵɵtemplate(18, FileGridComponent_div_2_label_18_Template, 7, 8, "label", 45);
|
|
260
|
+
i0.ɵɵelementEnd()()();
|
|
261
|
+
i0.ɵɵtemplate(19, FileGridComponent_div_2_div_19_Template, 13, 4, "div", 46)(20, FileGridComponent_div_2_div_20_Template, 2, 0, "div", 47);
|
|
262
|
+
i0.ɵɵelementEnd();
|
|
263
|
+
} if (rf & 2) {
|
|
264
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
265
|
+
i0.ɵɵadvance(9);
|
|
266
|
+
i0.ɵɵtwoWayProperty("ngModel", ctx_r1.multiProviderSearchQuery);
|
|
267
|
+
i0.ɵɵproperty("disabled", ctx_r1.isSearching);
|
|
268
|
+
i0.ɵɵadvance();
|
|
269
|
+
i0.ɵɵproperty("disabled", ctx_r1.isSearching || !ctx_r1.multiProviderSearchQuery.trim() || ctx_r1.selectedSearchProviders.size === 0);
|
|
270
|
+
i0.ɵɵadvance();
|
|
271
|
+
i0.ɵɵproperty("ngIf", ctx_r1.isSearching);
|
|
272
|
+
i0.ɵɵadvance();
|
|
273
|
+
i0.ɵɵproperty("ngIf", !ctx_r1.isSearching);
|
|
274
|
+
i0.ɵɵadvance();
|
|
275
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.isSearching ? "Searching..." : "Search", " ");
|
|
276
|
+
i0.ɵɵadvance(5);
|
|
277
|
+
i0.ɵɵproperty("ngForOf", ctx_r1.availableAccounts);
|
|
278
|
+
i0.ɵɵadvance();
|
|
279
|
+
i0.ɵɵproperty("ngIf", ctx_r1.multiProviderSearchResults);
|
|
280
|
+
i0.ɵɵadvance();
|
|
281
|
+
i0.ɵɵproperty("ngIf", ctx_r1.isSearching);
|
|
282
|
+
} }
|
|
283
|
+
function FileGridComponent_div_3_Template(rf, ctx) { if (rf & 1) {
|
|
284
|
+
i0.ɵɵelementStart(0, "div", 86);
|
|
285
|
+
i0.ɵɵelement(1, "mj-loading", 87);
|
|
286
|
+
i0.ɵɵelementEnd();
|
|
287
|
+
} }
|
|
288
|
+
function FileGridComponent_div_4_Template(rf, ctx) { if (rf & 1) {
|
|
289
|
+
const _r10 = i0.ɵɵgetCurrentView();
|
|
290
|
+
i0.ɵɵelementStart(0, "div", 88)(1, "div", 89);
|
|
291
|
+
i0.ɵɵelement(2, "i", 90);
|
|
292
|
+
i0.ɵɵelementEnd();
|
|
293
|
+
i0.ɵɵelementStart(3, "p", 91);
|
|
294
|
+
i0.ɵɵtext(4);
|
|
295
|
+
i0.ɵɵelementEnd();
|
|
296
|
+
i0.ɵɵelementStart(5, "button", 92);
|
|
297
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_4_Template_button_click_5_listener() { i0.ɵɵrestoreView(_r10); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.refresh()); });
|
|
298
|
+
i0.ɵɵelement(6, "i", 17);
|
|
299
|
+
i0.ɵɵtext(7, " Retry ");
|
|
300
|
+
i0.ɵɵelementEnd()();
|
|
301
|
+
} if (rf & 2) {
|
|
302
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
303
|
+
i0.ɵɵadvance(4);
|
|
304
|
+
i0.ɵɵtextInterpolate(ctx_r1.errorMessage);
|
|
305
|
+
} }
|
|
306
|
+
function FileGridComponent_div_5_tr_19_Template(rf, ctx) { if (rf & 1) {
|
|
307
|
+
const _r12 = i0.ɵɵgetCurrentView();
|
|
308
|
+
i0.ɵɵelementStart(0, "tr", 101);
|
|
309
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_5_tr_19_Template_tr_click_0_listener($event) { const item_r13 = i0.ɵɵrestoreView(_r12).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.onTileClick(item_r13, $event)); })("dblclick", function FileGridComponent_div_5_tr_19_Template_tr_dblclick_0_listener() { const item_r13 = i0.ɵɵrestoreView(_r12).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.onItemDoubleClick(item_r13)); });
|
|
310
|
+
i0.ɵɵelementStart(1, "td", 102);
|
|
311
|
+
i0.ɵɵelement(2, "i", 103);
|
|
312
|
+
i0.ɵɵelementStart(3, "span", 104);
|
|
313
|
+
i0.ɵɵtext(4);
|
|
314
|
+
i0.ɵɵelementEnd()();
|
|
315
|
+
i0.ɵɵelementStart(5, "td", 97)(6, "span", 105);
|
|
316
|
+
i0.ɵɵtext(7);
|
|
317
|
+
i0.ɵɵelementEnd()();
|
|
318
|
+
i0.ɵɵelementStart(8, "td", 106)(9, "span", 107);
|
|
319
|
+
i0.ɵɵtext(10);
|
|
320
|
+
i0.ɵɵelementEnd()();
|
|
321
|
+
i0.ɵɵelementStart(11, "td", 108)(12, "span", 109);
|
|
322
|
+
i0.ɵɵtext(13);
|
|
323
|
+
i0.ɵɵelementEnd()()();
|
|
324
|
+
} if (rf & 2) {
|
|
325
|
+
const item_r13 = ctx.$implicit;
|
|
326
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
327
|
+
i0.ɵɵclassProp("selected", ctx_r1.selectedItems.includes(item_r13.key));
|
|
328
|
+
i0.ɵɵadvance(2);
|
|
329
|
+
i0.ɵɵclassMap(ctx_r1.getItemIcon(item_r13));
|
|
330
|
+
i0.ɵɵadvance(2);
|
|
331
|
+
i0.ɵɵtextInterpolate(item_r13.name);
|
|
332
|
+
i0.ɵɵadvance(3);
|
|
333
|
+
i0.ɵɵtextInterpolate(ctx_r1.getFileType(item_r13));
|
|
334
|
+
i0.ɵɵadvance(3);
|
|
335
|
+
i0.ɵɵtextInterpolate(item_r13.type === "folder" ? "\u2014" : ctx_r1.formatFileSize(item_r13.size));
|
|
336
|
+
i0.ɵɵadvance(3);
|
|
337
|
+
i0.ɵɵtextInterpolate(ctx_r1.formatDate(item_r13.lastModified));
|
|
338
|
+
} }
|
|
339
|
+
function FileGridComponent_div_5_Template(rf, ctx) { if (rf & 1) {
|
|
340
|
+
const _r11 = i0.ɵɵgetCurrentView();
|
|
341
|
+
i0.ɵɵelementStart(0, "div", 93)(1, "table", 94)(2, "thead")(3, "tr")(4, "th", 95);
|
|
342
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_5_Template_th_click_4_listener() { i0.ɵɵrestoreView(_r11); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onSortChange([{ field: "name", dir: (ctx_r1.sort[0] == null ? null : ctx_r1.sort[0].field) === "name" && (ctx_r1.sort[0] == null ? null : ctx_r1.sort[0].dir) === "asc" ? "desc" : "asc" }])); });
|
|
343
|
+
i0.ɵɵelementStart(5, "span");
|
|
344
|
+
i0.ɵɵtext(6, "Name");
|
|
345
|
+
i0.ɵɵelementEnd();
|
|
346
|
+
i0.ɵɵelement(7, "i", 96);
|
|
347
|
+
i0.ɵɵelementEnd();
|
|
348
|
+
i0.ɵɵelementStart(8, "th", 97);
|
|
349
|
+
i0.ɵɵtext(9, "Type");
|
|
350
|
+
i0.ɵɵelementEnd();
|
|
351
|
+
i0.ɵɵelementStart(10, "th", 98);
|
|
352
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_5_Template_th_click_10_listener() { i0.ɵɵrestoreView(_r11); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onSortChange([{ field: "size", dir: (ctx_r1.sort[0] == null ? null : ctx_r1.sort[0].field) === "size" && (ctx_r1.sort[0] == null ? null : ctx_r1.sort[0].dir) === "asc" ? "desc" : "asc" }])); });
|
|
353
|
+
i0.ɵɵelementStart(11, "span");
|
|
354
|
+
i0.ɵɵtext(12, "Size");
|
|
355
|
+
i0.ɵɵelementEnd();
|
|
356
|
+
i0.ɵɵelement(13, "i", 96);
|
|
357
|
+
i0.ɵɵelementEnd();
|
|
358
|
+
i0.ɵɵelementStart(14, "th", 99);
|
|
359
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_5_Template_th_click_14_listener() { i0.ɵɵrestoreView(_r11); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onSortChange([{ field: "lastModified", dir: (ctx_r1.sort[0] == null ? null : ctx_r1.sort[0].field) === "lastModified" && (ctx_r1.sort[0] == null ? null : ctx_r1.sort[0].dir) === "asc" ? "desc" : "asc" }])); });
|
|
360
|
+
i0.ɵɵelementStart(15, "span");
|
|
361
|
+
i0.ɵɵtext(16, "Modified");
|
|
362
|
+
i0.ɵɵelementEnd();
|
|
363
|
+
i0.ɵɵelement(17, "i", 96);
|
|
364
|
+
i0.ɵɵelementEnd()()();
|
|
365
|
+
i0.ɵɵelementStart(18, "tbody");
|
|
366
|
+
i0.ɵɵtemplate(19, FileGridComponent_div_5_tr_19_Template, 14, 8, "tr", 100);
|
|
367
|
+
i0.ɵɵelementEnd()()();
|
|
368
|
+
} if (rf & 2) {
|
|
369
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
370
|
+
i0.ɵɵadvance(7);
|
|
371
|
+
i0.ɵɵclassProp("fa-caret-up", (ctx_r1.sort[0] == null ? null : ctx_r1.sort[0].field) === "name" && (ctx_r1.sort[0] == null ? null : ctx_r1.sort[0].dir) === "asc")("fa-caret-down", (ctx_r1.sort[0] == null ? null : ctx_r1.sort[0].field) === "name" && (ctx_r1.sort[0] == null ? null : ctx_r1.sort[0].dir) === "desc")("fa-sort", (ctx_r1.sort[0] == null ? null : ctx_r1.sort[0].field) !== "name");
|
|
372
|
+
i0.ɵɵadvance(6);
|
|
373
|
+
i0.ɵɵclassProp("fa-caret-up", (ctx_r1.sort[0] == null ? null : ctx_r1.sort[0].field) === "size" && (ctx_r1.sort[0] == null ? null : ctx_r1.sort[0].dir) === "asc")("fa-caret-down", (ctx_r1.sort[0] == null ? null : ctx_r1.sort[0].field) === "size" && (ctx_r1.sort[0] == null ? null : ctx_r1.sort[0].dir) === "desc")("fa-sort", (ctx_r1.sort[0] == null ? null : ctx_r1.sort[0].field) !== "size");
|
|
374
|
+
i0.ɵɵadvance(4);
|
|
375
|
+
i0.ɵɵclassProp("fa-caret-up", (ctx_r1.sort[0] == null ? null : ctx_r1.sort[0].field) === "lastModified" && (ctx_r1.sort[0] == null ? null : ctx_r1.sort[0].dir) === "asc")("fa-caret-down", (ctx_r1.sort[0] == null ? null : ctx_r1.sort[0].field) === "lastModified" && (ctx_r1.sort[0] == null ? null : ctx_r1.sort[0].dir) === "desc")("fa-sort", (ctx_r1.sort[0] == null ? null : ctx_r1.sort[0].field) !== "lastModified");
|
|
376
|
+
i0.ɵɵadvance(2);
|
|
377
|
+
i0.ɵɵproperty("ngForOf", ctx_r1.filteredItems);
|
|
378
|
+
} }
|
|
379
|
+
function FileGridComponent_div_6_div_2_div_5_Template(rf, ctx) { if (rf & 1) {
|
|
380
|
+
i0.ɵɵelementStart(0, "div", 117);
|
|
381
|
+
i0.ɵɵtext(1);
|
|
382
|
+
i0.ɵɵelementEnd();
|
|
383
|
+
} if (rf & 2) {
|
|
384
|
+
const item_r15 = i0.ɵɵnextContext().$implicit;
|
|
385
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
386
|
+
i0.ɵɵadvance();
|
|
387
|
+
i0.ɵɵtextInterpolate(ctx_r1.formatFileSize(item_r15.size));
|
|
388
|
+
} }
|
|
389
|
+
function FileGridComponent_div_6_div_2_div_6_Template(rf, ctx) { if (rf & 1) {
|
|
390
|
+
i0.ɵɵelementStart(0, "div", 117);
|
|
391
|
+
i0.ɵɵtext(1, "Folder");
|
|
392
|
+
i0.ɵɵelementEnd();
|
|
393
|
+
} }
|
|
394
|
+
function FileGridComponent_div_6_div_2_Template(rf, ctx) { if (rf & 1) {
|
|
395
|
+
const _r14 = i0.ɵɵgetCurrentView();
|
|
396
|
+
i0.ɵɵelementStart(0, "div", 113);
|
|
397
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_6_div_2_Template_div_click_0_listener($event) { const item_r15 = i0.ɵɵrestoreView(_r14).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.onTileClick(item_r15, $event)); })("dblclick", function FileGridComponent_div_6_div_2_Template_div_dblclick_0_listener() { const item_r15 = i0.ɵɵrestoreView(_r14).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.onItemDoubleClick(item_r15)); });
|
|
398
|
+
i0.ɵɵelementStart(1, "div", 114);
|
|
399
|
+
i0.ɵɵelement(2, "i");
|
|
400
|
+
i0.ɵɵelementEnd();
|
|
401
|
+
i0.ɵɵelementStart(3, "div", 115);
|
|
402
|
+
i0.ɵɵtext(4);
|
|
403
|
+
i0.ɵɵelementEnd();
|
|
404
|
+
i0.ɵɵtemplate(5, FileGridComponent_div_6_div_2_div_5_Template, 2, 1, "div", 116)(6, FileGridComponent_div_6_div_2_div_6_Template, 2, 0, "div", 116);
|
|
405
|
+
i0.ɵɵelementEnd();
|
|
406
|
+
} if (rf & 2) {
|
|
407
|
+
const item_r15 = ctx.$implicit;
|
|
408
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
409
|
+
i0.ɵɵclassProp("selected", ctx_r1.selectedItems.includes(item_r15.key));
|
|
410
|
+
i0.ɵɵadvance(2);
|
|
411
|
+
i0.ɵɵclassMap(ctx_r1.getItemIcon(item_r15));
|
|
412
|
+
i0.ɵɵadvance(2);
|
|
413
|
+
i0.ɵɵtextInterpolate(item_r15.name);
|
|
414
|
+
i0.ɵɵadvance();
|
|
415
|
+
i0.ɵɵproperty("ngIf", item_r15.type === "file");
|
|
416
|
+
i0.ɵɵadvance();
|
|
417
|
+
i0.ɵɵproperty("ngIf", item_r15.type === "folder");
|
|
418
|
+
} }
|
|
419
|
+
function FileGridComponent_div_6_Template(rf, ctx) { if (rf & 1) {
|
|
420
|
+
i0.ɵɵelementStart(0, "div", 110)(1, "div", 111);
|
|
421
|
+
i0.ɵɵtemplate(2, FileGridComponent_div_6_div_2_Template, 7, 7, "div", 112);
|
|
422
|
+
i0.ɵɵelementEnd()();
|
|
423
|
+
} if (rf & 2) {
|
|
424
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
425
|
+
i0.ɵɵadvance(2);
|
|
426
|
+
i0.ɵɵproperty("ngForOf", ctx_r1.filteredItems);
|
|
427
|
+
} }
|
|
428
|
+
function FileGridComponent_div_7_Template(rf, ctx) { if (rf & 1) {
|
|
429
|
+
const _r16 = i0.ɵɵgetCurrentView();
|
|
430
|
+
i0.ɵɵelementStart(0, "div", 118)(1, "div", 119);
|
|
431
|
+
i0.ɵɵelement(2, "i", 120);
|
|
432
|
+
i0.ɵɵelementEnd();
|
|
433
|
+
i0.ɵɵelementStart(3, "p", 121);
|
|
434
|
+
i0.ɵɵtext(4, "This folder is empty");
|
|
435
|
+
i0.ɵɵelementEnd();
|
|
436
|
+
i0.ɵɵelementStart(5, "button", 122);
|
|
437
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_7_Template_button_click_5_listener() { i0.ɵɵrestoreView(_r16); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onUploadClick()); });
|
|
438
|
+
i0.ɵɵelement(6, "i", 123);
|
|
439
|
+
i0.ɵɵtext(7, " Upload Files ");
|
|
440
|
+
i0.ɵɵelementEnd()();
|
|
441
|
+
} }
|
|
442
|
+
function FileGridComponent_div_8_Template(rf, ctx) { if (rf & 1) {
|
|
443
|
+
const _r17 = i0.ɵɵgetCurrentView();
|
|
444
|
+
i0.ɵɵelementStart(0, "div", 124)(1, "div", 125)(2, "button", 126);
|
|
445
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_8_Template_button_click_2_listener() { i0.ɵɵrestoreView(_r17); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onUploadClick()); });
|
|
446
|
+
i0.ɵɵelement(3, "i", 123);
|
|
447
|
+
i0.ɵɵtext(4, " Upload ");
|
|
448
|
+
i0.ɵɵelementEnd();
|
|
449
|
+
i0.ɵɵelementStart(5, "button", 126);
|
|
450
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_8_Template_button_click_5_listener() { i0.ɵɵrestoreView(_r17); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onNewFolderClick()); });
|
|
451
|
+
i0.ɵɵelement(6, "i", 127);
|
|
452
|
+
i0.ɵɵtext(7, " New Folder ");
|
|
453
|
+
i0.ɵɵelementEnd();
|
|
454
|
+
i0.ɵɵelementStart(8, "button", 128);
|
|
455
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_8_Template_button_click_8_listener() { i0.ɵɵrestoreView(_r17); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onDownloadClick()); });
|
|
456
|
+
i0.ɵɵelement(9, "i", 129);
|
|
457
|
+
i0.ɵɵtext(10, " Download ");
|
|
458
|
+
i0.ɵɵelementEnd();
|
|
459
|
+
i0.ɵɵelementStart(11, "button", 128);
|
|
460
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_8_Template_button_click_11_listener() { i0.ɵɵrestoreView(_r17); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onRenameClick()); });
|
|
461
|
+
i0.ɵɵelement(12, "i", 130);
|
|
462
|
+
i0.ɵɵtext(13, " Rename ");
|
|
463
|
+
i0.ɵɵelementEnd();
|
|
464
|
+
i0.ɵɵelementStart(14, "button", 128);
|
|
465
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_8_Template_button_click_14_listener() { i0.ɵɵrestoreView(_r17); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onCopyClick()); });
|
|
466
|
+
i0.ɵɵelement(15, "i", 131);
|
|
467
|
+
i0.ɵɵtext(16, " Copy ");
|
|
468
|
+
i0.ɵɵelementEnd();
|
|
469
|
+
i0.ɵɵelementStart(17, "button", 132);
|
|
470
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_8_Template_button_click_17_listener() { i0.ɵɵrestoreView(_r17); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onCopyToAccountClick()); });
|
|
471
|
+
i0.ɵɵelement(18, "i", 133);
|
|
472
|
+
i0.ɵɵtext(19, " Copy to... ");
|
|
473
|
+
i0.ɵɵelementEnd();
|
|
474
|
+
i0.ɵɵelementStart(20, "button", 128);
|
|
475
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_8_Template_button_click_20_listener() { i0.ɵɵrestoreView(_r17); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onMoveClick()); });
|
|
476
|
+
i0.ɵɵelement(21, "i", 134);
|
|
477
|
+
i0.ɵɵtext(22, " Move ");
|
|
478
|
+
i0.ɵɵelementEnd();
|
|
479
|
+
i0.ɵɵelementStart(23, "button", 128);
|
|
480
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_8_Template_button_click_23_listener() { i0.ɵɵrestoreView(_r17); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onDeleteClick()); });
|
|
481
|
+
i0.ɵɵelement(24, "i", 135);
|
|
482
|
+
i0.ɵɵtext(25, " Delete ");
|
|
483
|
+
i0.ɵɵelementEnd()();
|
|
484
|
+
i0.ɵɵelementStart(26, "div", 136)(27, "div", 137)(28, "button", 138);
|
|
485
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_8_Template_button_click_28_listener() { i0.ɵɵrestoreView(_r17); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.viewMode = "list"); });
|
|
486
|
+
i0.ɵɵelement(29, "i", 139);
|
|
487
|
+
i0.ɵɵelementEnd();
|
|
488
|
+
i0.ɵɵelementStart(30, "button", 140);
|
|
489
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_8_Template_button_click_30_listener() { i0.ɵɵrestoreView(_r17); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.viewMode = "grid"); });
|
|
490
|
+
i0.ɵɵelement(31, "i", 141);
|
|
491
|
+
i0.ɵɵelementEnd()();
|
|
492
|
+
i0.ɵɵelementStart(32, "span", 142);
|
|
493
|
+
i0.ɵɵtext(33);
|
|
494
|
+
i0.ɵɵelementEnd()()();
|
|
495
|
+
} if (rf & 2) {
|
|
496
|
+
let tmp_4_0;
|
|
497
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
498
|
+
i0.ɵɵadvance(8);
|
|
499
|
+
i0.ɵɵproperty("disabled", ctx_r1.selectedItems.length !== 1);
|
|
500
|
+
i0.ɵɵadvance(3);
|
|
501
|
+
i0.ɵɵproperty("disabled", ctx_r1.selectedItems.length !== 1);
|
|
502
|
+
i0.ɵɵadvance(3);
|
|
503
|
+
i0.ɵɵproperty("disabled", ctx_r1.selectedItems.length !== 1);
|
|
504
|
+
i0.ɵɵadvance(3);
|
|
505
|
+
i0.ɵɵproperty("disabled", ctx_r1.selectedItems.length !== 1 || ((tmp_4_0 = ctx_r1.getSelectedItem()) == null ? null : tmp_4_0.type) === "folder");
|
|
506
|
+
i0.ɵɵadvance(3);
|
|
507
|
+
i0.ɵɵproperty("disabled", ctx_r1.selectedItems.length !== 1);
|
|
508
|
+
i0.ɵɵadvance(3);
|
|
509
|
+
i0.ɵɵproperty("disabled", ctx_r1.selectedItems.length === 0);
|
|
510
|
+
i0.ɵɵadvance(5);
|
|
511
|
+
i0.ɵɵclassProp("active", ctx_r1.viewMode === "list");
|
|
512
|
+
i0.ɵɵadvance(2);
|
|
513
|
+
i0.ɵɵclassProp("active", ctx_r1.viewMode === "grid");
|
|
514
|
+
i0.ɵɵadvance(3);
|
|
515
|
+
i0.ɵɵtextInterpolate2(" ", ctx_r1.filteredItems.length, " ", ctx_r1.filteredItems.length === 1 ? "item" : "filteredItems", " ");
|
|
516
|
+
} }
|
|
517
|
+
function FileGridComponent_div_9_Template(rf, ctx) { if (rf & 1) {
|
|
518
|
+
i0.ɵɵelementStart(0, "div", 143)(1, "div", 144)(2, "div", 145);
|
|
519
|
+
i0.ɵɵelement(3, "i", 133);
|
|
520
|
+
i0.ɵɵelementEnd();
|
|
521
|
+
i0.ɵɵelementStart(4, "h2", 146);
|
|
522
|
+
i0.ɵɵtext(5, "Drop files to upload");
|
|
523
|
+
i0.ɵɵelementEnd()()();
|
|
524
|
+
} }
|
|
525
|
+
function FileGridComponent_div_10_Template(rf, ctx) { if (rf & 1) {
|
|
526
|
+
i0.ɵɵelementStart(0, "div", 147)(1, "div", 148)(2, "div", 149);
|
|
527
|
+
i0.ɵɵelement(3, "i", 48);
|
|
528
|
+
i0.ɵɵelementStart(4, "span");
|
|
529
|
+
i0.ɵɵtext(5);
|
|
530
|
+
i0.ɵɵelementEnd()();
|
|
531
|
+
i0.ɵɵelementStart(6, "div", 150);
|
|
532
|
+
i0.ɵɵelement(7, "div", 151);
|
|
533
|
+
i0.ɵɵelementEnd();
|
|
534
|
+
i0.ɵɵelementStart(8, "span", 152);
|
|
535
|
+
i0.ɵɵtext(9);
|
|
536
|
+
i0.ɵɵelementEnd()()();
|
|
537
|
+
} if (rf & 2) {
|
|
538
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
539
|
+
i0.ɵɵadvance(5);
|
|
540
|
+
i0.ɵɵtextInterpolate1("Uploading ", ctx_r1.uploadingFileName, "...");
|
|
541
|
+
i0.ɵɵadvance(2);
|
|
542
|
+
i0.ɵɵstyleProp("width", ctx_r1.uploadProgress, "%");
|
|
543
|
+
i0.ɵɵadvance(2);
|
|
544
|
+
i0.ɵɵtextInterpolate1("", ctx_r1.uploadProgress, "%");
|
|
545
|
+
} }
|
|
546
|
+
function FileGridComponent_div_11_i_15_Template(rf, ctx) { if (rf & 1) {
|
|
547
|
+
i0.ɵɵelement(0, "i", 48);
|
|
548
|
+
} }
|
|
549
|
+
function FileGridComponent_div_11_Template(rf, ctx) { if (rf & 1) {
|
|
550
|
+
const _r18 = i0.ɵɵgetCurrentView();
|
|
551
|
+
i0.ɵɵelementStart(0, "div", 153);
|
|
552
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_11_Template_div_click_0_listener() { i0.ɵɵrestoreView(_r18); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onCancelNewFolder()); });
|
|
553
|
+
i0.ɵɵelementStart(1, "div", 154);
|
|
554
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_11_Template_div_click_1_listener($event) { i0.ɵɵrestoreView(_r18); return i0.ɵɵresetView($event.stopPropagation()); });
|
|
555
|
+
i0.ɵɵelementStart(2, "div", 155)(3, "h3");
|
|
556
|
+
i0.ɵɵtext(4, "Create New Folder");
|
|
557
|
+
i0.ɵɵelementEnd();
|
|
558
|
+
i0.ɵɵelementStart(5, "button", 156);
|
|
559
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_11_Template_button_click_5_listener() { i0.ɵɵrestoreView(_r18); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onCancelNewFolder()); });
|
|
560
|
+
i0.ɵɵelement(6, "i", 31);
|
|
561
|
+
i0.ɵɵelementEnd()();
|
|
562
|
+
i0.ɵɵelementStart(7, "div", 157)(8, "label");
|
|
563
|
+
i0.ɵɵtext(9, "Folder name:");
|
|
564
|
+
i0.ɵɵelementEnd();
|
|
565
|
+
i0.ɵɵelementStart(10, "input", 158);
|
|
566
|
+
i0.ɵɵtwoWayListener("ngModelChange", function FileGridComponent_div_11_Template_input_ngModelChange_10_listener($event) { i0.ɵɵrestoreView(_r18); const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r1.newFolderName, $event) || (ctx_r1.newFolderName = $event); return i0.ɵɵresetView($event); });
|
|
567
|
+
i0.ɵɵlistener("keydown.enter", function FileGridComponent_div_11_Template_input_keydown_enter_10_listener() { i0.ɵɵrestoreView(_r18); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onCreateFolder()); });
|
|
568
|
+
i0.ɵɵelementEnd()();
|
|
569
|
+
i0.ɵɵelementStart(11, "div", 159)(12, "button", 160);
|
|
570
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_11_Template_button_click_12_listener() { i0.ɵɵrestoreView(_r18); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onCancelNewFolder()); });
|
|
571
|
+
i0.ɵɵtext(13, "Cancel");
|
|
572
|
+
i0.ɵɵelementEnd();
|
|
573
|
+
i0.ɵɵelementStart(14, "button", 160);
|
|
574
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_11_Template_button_click_14_listener() { i0.ɵɵrestoreView(_r18); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onCreateFolder()); });
|
|
575
|
+
i0.ɵɵtemplate(15, FileGridComponent_div_11_i_15_Template, 1, 0, "i", 40);
|
|
576
|
+
i0.ɵɵtext(16);
|
|
577
|
+
i0.ɵɵelementEnd()()()();
|
|
578
|
+
} if (rf & 2) {
|
|
579
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
580
|
+
i0.ɵɵadvance(10);
|
|
581
|
+
i0.ɵɵtwoWayProperty("ngModel", ctx_r1.newFolderName);
|
|
582
|
+
i0.ɵɵproperty("disabled", ctx_r1.isCreatingFolder);
|
|
583
|
+
i0.ɵɵadvance(2);
|
|
584
|
+
i0.ɵɵproperty("disabled", ctx_r1.isCreatingFolder);
|
|
585
|
+
i0.ɵɵadvance(2);
|
|
586
|
+
i0.ɵɵproperty("disabled", !ctx_r1.newFolderName.trim() || ctx_r1.isCreatingFolder);
|
|
587
|
+
i0.ɵɵadvance();
|
|
588
|
+
i0.ɵɵproperty("ngIf", ctx_r1.isCreatingFolder);
|
|
589
|
+
i0.ɵɵadvance();
|
|
590
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.isCreatingFolder ? "Creating..." : "Create", " ");
|
|
591
|
+
} }
|
|
592
|
+
function FileGridComponent_div_12_p_13_Template(rf, ctx) { if (rf & 1) {
|
|
593
|
+
i0.ɵɵelementStart(0, "p", 163);
|
|
594
|
+
i0.ɵɵtext(1, "This will delete the folder and all its contents.");
|
|
595
|
+
i0.ɵɵelementEnd();
|
|
596
|
+
} }
|
|
597
|
+
function FileGridComponent_div_12_i_18_Template(rf, ctx) { if (rf & 1) {
|
|
598
|
+
i0.ɵɵelement(0, "i", 48);
|
|
599
|
+
} }
|
|
600
|
+
function FileGridComponent_div_12_Template(rf, ctx) { if (rf & 1) {
|
|
601
|
+
const _r19 = i0.ɵɵgetCurrentView();
|
|
602
|
+
i0.ɵɵelementStart(0, "div", 153);
|
|
603
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_12_Template_div_click_0_listener() { i0.ɵɵrestoreView(_r19); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onCancelDelete()); });
|
|
604
|
+
i0.ɵɵelementStart(1, "div", 154);
|
|
605
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_12_Template_div_click_1_listener($event) { i0.ɵɵrestoreView(_r19); return i0.ɵɵresetView($event.stopPropagation()); });
|
|
606
|
+
i0.ɵɵelementStart(2, "div", 155)(3, "h3");
|
|
607
|
+
i0.ɵɵtext(4);
|
|
608
|
+
i0.ɵɵelementEnd();
|
|
609
|
+
i0.ɵɵelementStart(5, "button", 156);
|
|
610
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_12_Template_button_click_5_listener() { i0.ɵɵrestoreView(_r19); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onCancelDelete()); });
|
|
611
|
+
i0.ɵɵelement(6, "i", 31);
|
|
612
|
+
i0.ɵɵelementEnd()();
|
|
613
|
+
i0.ɵɵelementStart(7, "div", 157)(8, "p");
|
|
614
|
+
i0.ɵɵtext(9, "Are you sure you want to delete ");
|
|
615
|
+
i0.ɵɵelementStart(10, "strong");
|
|
616
|
+
i0.ɵɵtext(11);
|
|
617
|
+
i0.ɵɵelementEnd();
|
|
618
|
+
i0.ɵɵtext(12, "?");
|
|
619
|
+
i0.ɵɵelementEnd();
|
|
620
|
+
i0.ɵɵtemplate(13, FileGridComponent_div_12_p_13_Template, 2, 0, "p", 161);
|
|
621
|
+
i0.ɵɵelementEnd();
|
|
622
|
+
i0.ɵɵelementStart(14, "div", 159)(15, "button", 160);
|
|
623
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_12_Template_button_click_15_listener() { i0.ɵɵrestoreView(_r19); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onCancelDelete()); });
|
|
624
|
+
i0.ɵɵtext(16, "Cancel");
|
|
625
|
+
i0.ɵɵelementEnd();
|
|
626
|
+
i0.ɵɵelementStart(17, "button", 162);
|
|
627
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_12_Template_button_click_17_listener() { i0.ɵɵrestoreView(_r19); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onConfirmDelete()); });
|
|
628
|
+
i0.ɵɵtemplate(18, FileGridComponent_div_12_i_18_Template, 1, 0, "i", 40);
|
|
629
|
+
i0.ɵɵtext(19);
|
|
630
|
+
i0.ɵɵelementEnd()()()();
|
|
631
|
+
} if (rf & 2) {
|
|
632
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
633
|
+
i0.ɵɵadvance(4);
|
|
634
|
+
i0.ɵɵtextInterpolate1("Delete ", ctx_r1.itemToDelete.type === "folder" ? "Folder" : "File", "?");
|
|
635
|
+
i0.ɵɵadvance(7);
|
|
636
|
+
i0.ɵɵtextInterpolate(ctx_r1.itemToDelete.name);
|
|
637
|
+
i0.ɵɵadvance(2);
|
|
638
|
+
i0.ɵɵproperty("ngIf", ctx_r1.itemToDelete.type === "folder");
|
|
639
|
+
i0.ɵɵadvance(2);
|
|
640
|
+
i0.ɵɵproperty("disabled", ctx_r1.isDeleting);
|
|
641
|
+
i0.ɵɵadvance(2);
|
|
642
|
+
i0.ɵɵproperty("disabled", ctx_r1.isDeleting);
|
|
643
|
+
i0.ɵɵadvance();
|
|
644
|
+
i0.ɵɵproperty("ngIf", ctx_r1.isDeleting);
|
|
645
|
+
i0.ɵɵadvance();
|
|
646
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.isDeleting ? "Deleting..." : "Delete", " ");
|
|
647
|
+
} }
|
|
648
|
+
function FileGridComponent_div_13_i_15_Template(rf, ctx) { if (rf & 1) {
|
|
649
|
+
i0.ɵɵelement(0, "i", 48);
|
|
650
|
+
} }
|
|
651
|
+
function FileGridComponent_div_13_Template(rf, ctx) { if (rf & 1) {
|
|
652
|
+
const _r20 = i0.ɵɵgetCurrentView();
|
|
653
|
+
i0.ɵɵelementStart(0, "div", 153);
|
|
654
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_13_Template_div_click_0_listener() { i0.ɵɵrestoreView(_r20); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onCancelRename()); });
|
|
655
|
+
i0.ɵɵelementStart(1, "div", 154);
|
|
656
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_13_Template_div_click_1_listener($event) { i0.ɵɵrestoreView(_r20); return i0.ɵɵresetView($event.stopPropagation()); });
|
|
657
|
+
i0.ɵɵelementStart(2, "div", 155)(3, "h3");
|
|
658
|
+
i0.ɵɵtext(4);
|
|
659
|
+
i0.ɵɵelementEnd();
|
|
660
|
+
i0.ɵɵelementStart(5, "button", 156);
|
|
661
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_13_Template_button_click_5_listener() { i0.ɵɵrestoreView(_r20); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onCancelRename()); });
|
|
662
|
+
i0.ɵɵelement(6, "i", 31);
|
|
663
|
+
i0.ɵɵelementEnd()();
|
|
664
|
+
i0.ɵɵelementStart(7, "div", 157)(8, "label");
|
|
665
|
+
i0.ɵɵtext(9, "New name:");
|
|
666
|
+
i0.ɵɵelementEnd();
|
|
667
|
+
i0.ɵɵelementStart(10, "input", 164);
|
|
668
|
+
i0.ɵɵtwoWayListener("ngModelChange", function FileGridComponent_div_13_Template_input_ngModelChange_10_listener($event) { i0.ɵɵrestoreView(_r20); const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r1.newItemName, $event) || (ctx_r1.newItemName = $event); return i0.ɵɵresetView($event); });
|
|
669
|
+
i0.ɵɵlistener("keydown.enter", function FileGridComponent_div_13_Template_input_keydown_enter_10_listener() { i0.ɵɵrestoreView(_r20); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onConfirmRename()); });
|
|
670
|
+
i0.ɵɵelementEnd()();
|
|
671
|
+
i0.ɵɵelementStart(11, "div", 159)(12, "button", 160);
|
|
672
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_13_Template_button_click_12_listener() { i0.ɵɵrestoreView(_r20); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onCancelRename()); });
|
|
673
|
+
i0.ɵɵtext(13, "Cancel");
|
|
674
|
+
i0.ɵɵelementEnd();
|
|
675
|
+
i0.ɵɵelementStart(14, "button", 160);
|
|
676
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_13_Template_button_click_14_listener() { i0.ɵɵrestoreView(_r20); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onConfirmRename()); });
|
|
677
|
+
i0.ɵɵtemplate(15, FileGridComponent_div_13_i_15_Template, 1, 0, "i", 40);
|
|
678
|
+
i0.ɵɵtext(16);
|
|
679
|
+
i0.ɵɵelementEnd()()()();
|
|
680
|
+
} if (rf & 2) {
|
|
681
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
682
|
+
i0.ɵɵadvance(4);
|
|
683
|
+
i0.ɵɵtextInterpolate1("Rename ", ctx_r1.itemToRename.type === "folder" ? "Folder" : "File", "");
|
|
684
|
+
i0.ɵɵadvance(6);
|
|
685
|
+
i0.ɵɵtwoWayProperty("ngModel", ctx_r1.newItemName);
|
|
686
|
+
i0.ɵɵproperty("disabled", ctx_r1.isRenaming);
|
|
687
|
+
i0.ɵɵadvance(2);
|
|
688
|
+
i0.ɵɵproperty("disabled", ctx_r1.isRenaming);
|
|
689
|
+
i0.ɵɵadvance(2);
|
|
690
|
+
i0.ɵɵproperty("disabled", ctx_r1.isRenaming || !ctx_r1.newItemName.trim());
|
|
691
|
+
i0.ɵɵadvance();
|
|
692
|
+
i0.ɵɵproperty("ngIf", ctx_r1.isRenaming);
|
|
693
|
+
i0.ɵɵadvance();
|
|
694
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.isRenaming ? "Renaming..." : "Rename", " ");
|
|
695
|
+
} }
|
|
696
|
+
function FileGridComponent_div_14_i_19_Template(rf, ctx) { if (rf & 1) {
|
|
697
|
+
i0.ɵɵelement(0, "i", 48);
|
|
698
|
+
} }
|
|
699
|
+
function FileGridComponent_div_14_Template(rf, ctx) { if (rf & 1) {
|
|
700
|
+
const _r21 = i0.ɵɵgetCurrentView();
|
|
701
|
+
i0.ɵɵelementStart(0, "div", 153);
|
|
702
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_14_Template_div_click_0_listener() { i0.ɵɵrestoreView(_r21); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onCancelCopy()); });
|
|
703
|
+
i0.ɵɵelementStart(1, "div", 154);
|
|
704
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_14_Template_div_click_1_listener($event) { i0.ɵɵrestoreView(_r21); return i0.ɵɵresetView($event.stopPropagation()); });
|
|
705
|
+
i0.ɵɵelementStart(2, "div", 155)(3, "h3");
|
|
706
|
+
i0.ɵɵtext(4);
|
|
707
|
+
i0.ɵɵelementEnd();
|
|
708
|
+
i0.ɵɵelementStart(5, "button", 156);
|
|
709
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_14_Template_button_click_5_listener() { i0.ɵɵrestoreView(_r21); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onCancelCopy()); });
|
|
710
|
+
i0.ɵɵelement(6, "i", 31);
|
|
711
|
+
i0.ɵɵelementEnd()();
|
|
712
|
+
i0.ɵɵelementStart(7, "div", 157)(8, "p");
|
|
713
|
+
i0.ɵɵtext(9, "Copying: ");
|
|
714
|
+
i0.ɵɵelementStart(10, "strong");
|
|
715
|
+
i0.ɵɵtext(11);
|
|
716
|
+
i0.ɵɵelementEnd()();
|
|
717
|
+
i0.ɵɵelementStart(12, "label");
|
|
718
|
+
i0.ɵɵtext(13, "Destination path:");
|
|
719
|
+
i0.ɵɵelementEnd();
|
|
720
|
+
i0.ɵɵelementStart(14, "input", 165);
|
|
721
|
+
i0.ɵɵtwoWayListener("ngModelChange", function FileGridComponent_div_14_Template_input_ngModelChange_14_listener($event) { i0.ɵɵrestoreView(_r21); const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r1.copyDestinationPath, $event) || (ctx_r1.copyDestinationPath = $event); return i0.ɵɵresetView($event); });
|
|
722
|
+
i0.ɵɵlistener("keydown.enter", function FileGridComponent_div_14_Template_input_keydown_enter_14_listener() { i0.ɵɵrestoreView(_r21); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onConfirmCopy()); });
|
|
723
|
+
i0.ɵɵelementEnd()();
|
|
724
|
+
i0.ɵɵelementStart(15, "div", 159)(16, "button", 160);
|
|
725
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_14_Template_button_click_16_listener() { i0.ɵɵrestoreView(_r21); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onCancelCopy()); });
|
|
726
|
+
i0.ɵɵtext(17, "Cancel");
|
|
727
|
+
i0.ɵɵelementEnd();
|
|
728
|
+
i0.ɵɵelementStart(18, "button", 160);
|
|
729
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_14_Template_button_click_18_listener() { i0.ɵɵrestoreView(_r21); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onConfirmCopy()); });
|
|
730
|
+
i0.ɵɵtemplate(19, FileGridComponent_div_14_i_19_Template, 1, 0, "i", 40);
|
|
731
|
+
i0.ɵɵtext(20);
|
|
732
|
+
i0.ɵɵelementEnd()()()();
|
|
733
|
+
} if (rf & 2) {
|
|
734
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
735
|
+
i0.ɵɵadvance(4);
|
|
736
|
+
i0.ɵɵtextInterpolate1("Copy ", ctx_r1.itemToCopy.type === "folder" ? "Folder" : "File", "");
|
|
737
|
+
i0.ɵɵadvance(7);
|
|
738
|
+
i0.ɵɵtextInterpolate(ctx_r1.itemToCopy.name);
|
|
739
|
+
i0.ɵɵadvance(3);
|
|
740
|
+
i0.ɵɵtwoWayProperty("ngModel", ctx_r1.copyDestinationPath);
|
|
741
|
+
i0.ɵɵproperty("disabled", ctx_r1.isCopying);
|
|
742
|
+
i0.ɵɵadvance(2);
|
|
743
|
+
i0.ɵɵproperty("disabled", ctx_r1.isCopying);
|
|
744
|
+
i0.ɵɵadvance(2);
|
|
745
|
+
i0.ɵɵproperty("disabled", ctx_r1.isCopying || !ctx_r1.copyDestinationPath.trim());
|
|
746
|
+
i0.ɵɵadvance();
|
|
747
|
+
i0.ɵɵproperty("ngIf", ctx_r1.isCopying);
|
|
748
|
+
i0.ɵɵadvance();
|
|
749
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.isCopying ? "Copying..." : "Copy", " ");
|
|
750
|
+
} }
|
|
751
|
+
function FileGridComponent_div_15_i_21_Template(rf, ctx) { if (rf & 1) {
|
|
752
|
+
i0.ɵɵelement(0, "i", 48);
|
|
753
|
+
} }
|
|
754
|
+
function FileGridComponent_div_15_Template(rf, ctx) { if (rf & 1) {
|
|
755
|
+
const _r22 = i0.ɵɵgetCurrentView();
|
|
756
|
+
i0.ɵɵelementStart(0, "div", 153);
|
|
757
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_15_Template_div_click_0_listener() { i0.ɵɵrestoreView(_r22); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onCancelMove()); });
|
|
758
|
+
i0.ɵɵelementStart(1, "div", 154);
|
|
759
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_15_Template_div_click_1_listener($event) { i0.ɵɵrestoreView(_r22); return i0.ɵɵresetView($event.stopPropagation()); });
|
|
760
|
+
i0.ɵɵelementStart(2, "div", 155)(3, "h3");
|
|
761
|
+
i0.ɵɵtext(4);
|
|
762
|
+
i0.ɵɵelementEnd();
|
|
763
|
+
i0.ɵɵelementStart(5, "button", 156);
|
|
764
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_15_Template_button_click_5_listener() { i0.ɵɵrestoreView(_r22); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onCancelMove()); });
|
|
765
|
+
i0.ɵɵelement(6, "i", 31);
|
|
766
|
+
i0.ɵɵelementEnd()();
|
|
767
|
+
i0.ɵɵelementStart(7, "div", 157)(8, "p");
|
|
768
|
+
i0.ɵɵtext(9, "Moving: ");
|
|
769
|
+
i0.ɵɵelementStart(10, "strong");
|
|
770
|
+
i0.ɵɵtext(11);
|
|
771
|
+
i0.ɵɵelementEnd()();
|
|
772
|
+
i0.ɵɵelementStart(12, "label");
|
|
773
|
+
i0.ɵɵtext(13, "Destination folder path:");
|
|
774
|
+
i0.ɵɵelementEnd();
|
|
775
|
+
i0.ɵɵelementStart(14, "input", 166);
|
|
776
|
+
i0.ɵɵtwoWayListener("ngModelChange", function FileGridComponent_div_15_Template_input_ngModelChange_14_listener($event) { i0.ɵɵrestoreView(_r22); const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r1.moveDestinationPath, $event) || (ctx_r1.moveDestinationPath = $event); return i0.ɵɵresetView($event); });
|
|
777
|
+
i0.ɵɵlistener("keydown.enter", function FileGridComponent_div_15_Template_input_keydown_enter_14_listener() { i0.ɵɵrestoreView(_r22); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onConfirmMove()); });
|
|
778
|
+
i0.ɵɵelementEnd();
|
|
779
|
+
i0.ɵɵelementStart(15, "p", 167);
|
|
780
|
+
i0.ɵɵtext(16);
|
|
781
|
+
i0.ɵɵelementEnd()();
|
|
782
|
+
i0.ɵɵelementStart(17, "div", 159)(18, "button", 160);
|
|
783
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_15_Template_button_click_18_listener() { i0.ɵɵrestoreView(_r22); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onCancelMove()); });
|
|
784
|
+
i0.ɵɵtext(19, "Cancel");
|
|
785
|
+
i0.ɵɵelementEnd();
|
|
786
|
+
i0.ɵɵelementStart(20, "button", 160);
|
|
787
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_15_Template_button_click_20_listener() { i0.ɵɵrestoreView(_r22); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onConfirmMove()); });
|
|
788
|
+
i0.ɵɵtemplate(21, FileGridComponent_div_15_i_21_Template, 1, 0, "i", 40);
|
|
789
|
+
i0.ɵɵtext(22);
|
|
790
|
+
i0.ɵɵelementEnd()()()();
|
|
791
|
+
} if (rf & 2) {
|
|
792
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
793
|
+
i0.ɵɵadvance(4);
|
|
794
|
+
i0.ɵɵtextInterpolate1("Move ", ctx_r1.itemToMove.type === "folder" ? "Folder" : "File", "");
|
|
795
|
+
i0.ɵɵadvance(7);
|
|
796
|
+
i0.ɵɵtextInterpolate(ctx_r1.itemToMove.name);
|
|
797
|
+
i0.ɵɵadvance(3);
|
|
798
|
+
i0.ɵɵtwoWayProperty("ngModel", ctx_r1.moveDestinationPath);
|
|
799
|
+
i0.ɵɵproperty("disabled", ctx_r1.isMoving);
|
|
800
|
+
i0.ɵɵadvance(2);
|
|
801
|
+
i0.ɵɵtextInterpolate2(" File will be moved to: ", ctx_r1.moveDestinationPath, "", ctx_r1.itemToMove.name, " ");
|
|
802
|
+
i0.ɵɵadvance(2);
|
|
803
|
+
i0.ɵɵproperty("disabled", ctx_r1.isMoving);
|
|
804
|
+
i0.ɵɵadvance(2);
|
|
805
|
+
i0.ɵɵproperty("disabled", ctx_r1.isMoving || !ctx_r1.moveDestinationPath.trim());
|
|
806
|
+
i0.ɵɵadvance();
|
|
807
|
+
i0.ɵɵproperty("ngIf", ctx_r1.isMoving);
|
|
808
|
+
i0.ɵɵadvance();
|
|
809
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.isMoving ? "Moving..." : "Move", " ");
|
|
810
|
+
} }
|
|
811
|
+
function FileGridComponent_div_16_div_19_Template(rf, ctx) { if (rf & 1) {
|
|
812
|
+
const _r24 = i0.ɵɵgetCurrentView();
|
|
813
|
+
i0.ɵɵelementStart(0, "div", 174)(1, "input", 175);
|
|
814
|
+
i0.ɵɵlistener("change", function FileGridComponent_div_16_div_19_Template_input_change_1_listener() { const a_r25 = i0.ɵɵrestoreView(_r24).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.toggleDestinationAccount(a_r25.account.ID)); });
|
|
815
|
+
i0.ɵɵelementEnd();
|
|
816
|
+
i0.ɵɵelementStart(2, "label", 176);
|
|
817
|
+
i0.ɵɵtext(3);
|
|
818
|
+
i0.ɵɵelementStart(4, "span", 177);
|
|
819
|
+
i0.ɵɵtext(5);
|
|
820
|
+
i0.ɵɵelementEnd()()();
|
|
821
|
+
} if (rf & 2) {
|
|
822
|
+
const a_r25 = ctx.$implicit;
|
|
823
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
824
|
+
i0.ɵɵadvance();
|
|
825
|
+
i0.ɵɵproperty("id", "dest-account-" + a_r25.account.ID)("checked", ctx_r1.isDestinationAccountSelected(a_r25.account.ID))("disabled", ctx_r1.isCopyingToAccount);
|
|
826
|
+
i0.ɵɵadvance();
|
|
827
|
+
i0.ɵɵproperty("for", "dest-account-" + a_r25.account.ID);
|
|
828
|
+
i0.ɵɵadvance();
|
|
829
|
+
i0.ɵɵtextInterpolate1(" ", a_r25.account.Name, " ");
|
|
830
|
+
i0.ɵɵadvance(2);
|
|
831
|
+
i0.ɵɵtextInterpolate(a_r25.provider.Name);
|
|
832
|
+
} }
|
|
833
|
+
function FileGridComponent_div_16_div_23_Template(rf, ctx) { if (rf & 1) {
|
|
834
|
+
i0.ɵɵelementStart(0, "div", 178);
|
|
835
|
+
i0.ɵɵelement(1, "i", 48);
|
|
836
|
+
i0.ɵɵtext(2);
|
|
837
|
+
i0.ɵɵelementEnd();
|
|
838
|
+
} if (rf & 2) {
|
|
839
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
840
|
+
i0.ɵɵadvance(2);
|
|
841
|
+
i0.ɵɵtextInterpolate3(" Copying to ", ctx_r1.copyToAccountProgress.currentAccount, " (", ctx_r1.copyToAccountProgress.current, "/", ctx_r1.copyToAccountProgress.total, ")... ");
|
|
842
|
+
} }
|
|
843
|
+
function FileGridComponent_div_16_i_28_Template(rf, ctx) { if (rf & 1) {
|
|
844
|
+
i0.ɵɵelement(0, "i", 48);
|
|
845
|
+
} }
|
|
846
|
+
function FileGridComponent_div_16_Template(rf, ctx) { if (rf & 1) {
|
|
847
|
+
const _r23 = i0.ɵɵgetCurrentView();
|
|
848
|
+
i0.ɵɵelementStart(0, "div", 153);
|
|
849
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_16_Template_div_click_0_listener() { i0.ɵɵrestoreView(_r23); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onCancelCopyToAccount()); });
|
|
850
|
+
i0.ɵɵelementStart(1, "div", 154);
|
|
851
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_16_Template_div_click_1_listener($event) { i0.ɵɵrestoreView(_r23); return i0.ɵɵresetView($event.stopPropagation()); });
|
|
852
|
+
i0.ɵɵelementStart(2, "div", 155)(3, "h3");
|
|
853
|
+
i0.ɵɵtext(4, "Copy to Another Account");
|
|
854
|
+
i0.ɵɵelementEnd();
|
|
855
|
+
i0.ɵɵelementStart(5, "button", 156);
|
|
856
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_16_Template_button_click_5_listener() { i0.ɵɵrestoreView(_r23); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onCancelCopyToAccount()); });
|
|
857
|
+
i0.ɵɵelement(6, "i", 31);
|
|
858
|
+
i0.ɵɵelementEnd()();
|
|
859
|
+
i0.ɵɵelementStart(7, "div", 157)(8, "p");
|
|
860
|
+
i0.ɵɵtext(9, "Copying: ");
|
|
861
|
+
i0.ɵɵelementStart(10, "strong");
|
|
862
|
+
i0.ɵɵtext(11);
|
|
863
|
+
i0.ɵɵelementEnd()();
|
|
864
|
+
i0.ɵɵelementStart(12, "p", 168);
|
|
865
|
+
i0.ɵɵtext(13, " From: ");
|
|
866
|
+
i0.ɵɵelementStart(14, "strong");
|
|
867
|
+
i0.ɵɵtext(15);
|
|
868
|
+
i0.ɵɵelementEnd()();
|
|
869
|
+
i0.ɵɵelementStart(16, "label");
|
|
870
|
+
i0.ɵɵtext(17, "Select destination account(s):");
|
|
871
|
+
i0.ɵɵelementEnd();
|
|
872
|
+
i0.ɵɵelementStart(18, "div", 169);
|
|
873
|
+
i0.ɵɵtemplate(19, FileGridComponent_div_16_div_19_Template, 6, 6, "div", 170);
|
|
874
|
+
i0.ɵɵelementEnd();
|
|
875
|
+
i0.ɵɵelementStart(20, "label", 171);
|
|
876
|
+
i0.ɵɵtext(21, "Destination filename:");
|
|
877
|
+
i0.ɵɵelementEnd();
|
|
878
|
+
i0.ɵɵelementStart(22, "input", 172);
|
|
879
|
+
i0.ɵɵtwoWayListener("ngModelChange", function FileGridComponent_div_16_Template_input_ngModelChange_22_listener($event) { i0.ɵɵrestoreView(_r23); const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r1.copyToAccountDestinationPath, $event) || (ctx_r1.copyToAccountDestinationPath = $event); return i0.ɵɵresetView($event); });
|
|
880
|
+
i0.ɵɵlistener("keydown.enter", function FileGridComponent_div_16_Template_input_keydown_enter_22_listener() { i0.ɵɵrestoreView(_r23); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onConfirmCopyToAccount()); });
|
|
881
|
+
i0.ɵɵelementEnd();
|
|
882
|
+
i0.ɵɵtemplate(23, FileGridComponent_div_16_div_23_Template, 3, 3, "div", 173);
|
|
883
|
+
i0.ɵɵelementEnd();
|
|
884
|
+
i0.ɵɵelementStart(24, "div", 159)(25, "button", 160);
|
|
885
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_16_Template_button_click_25_listener() { i0.ɵɵrestoreView(_r23); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onCancelCopyToAccount()); });
|
|
886
|
+
i0.ɵɵtext(26, "Cancel");
|
|
887
|
+
i0.ɵɵelementEnd();
|
|
888
|
+
i0.ɵɵelementStart(27, "button", 160);
|
|
889
|
+
i0.ɵɵlistener("click", function FileGridComponent_div_16_Template_button_click_27_listener() { i0.ɵɵrestoreView(_r23); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onConfirmCopyToAccount()); });
|
|
890
|
+
i0.ɵɵtemplate(28, FileGridComponent_div_16_i_28_Template, 1, 0, "i", 40);
|
|
891
|
+
i0.ɵɵtext(29);
|
|
892
|
+
i0.ɵɵelementEnd()()()();
|
|
893
|
+
} if (rf & 2) {
|
|
894
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
895
|
+
i0.ɵɵadvance(11);
|
|
896
|
+
i0.ɵɵtextInterpolate(ctx_r1.itemToCopyToProvider.name);
|
|
897
|
+
i0.ɵɵadvance(4);
|
|
898
|
+
i0.ɵɵtextInterpolate(ctx_r1.account == null ? null : ctx_r1.account.account == null ? null : ctx_r1.account.account.Name);
|
|
899
|
+
i0.ɵɵadvance(4);
|
|
900
|
+
i0.ɵɵproperty("ngForOf", ctx_r1.availableAccounts);
|
|
901
|
+
i0.ɵɵadvance(3);
|
|
902
|
+
i0.ɵɵtwoWayProperty("ngModel", ctx_r1.copyToAccountDestinationPath);
|
|
903
|
+
i0.ɵɵproperty("disabled", ctx_r1.isCopyingToAccount);
|
|
904
|
+
i0.ɵɵadvance();
|
|
905
|
+
i0.ɵɵproperty("ngIf", ctx_r1.copyToAccountProgress);
|
|
906
|
+
i0.ɵɵadvance(2);
|
|
907
|
+
i0.ɵɵproperty("disabled", ctx_r1.isCopyingToAccount);
|
|
908
|
+
i0.ɵɵadvance(2);
|
|
909
|
+
i0.ɵɵproperty("disabled", ctx_r1.isCopyingToAccount || ctx_r1.selectedDestinationAccounts.size === 0 || !ctx_r1.copyToAccountDestinationPath.trim());
|
|
910
|
+
i0.ɵɵadvance();
|
|
911
|
+
i0.ɵɵproperty("ngIf", ctx_r1.isCopyingToAccount);
|
|
912
|
+
i0.ɵɵadvance();
|
|
913
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.isCopyingToAccount ? "Copying..." : "Copy to " + ctx_r1.selectedDestinationAccounts.size + " account" + (ctx_r1.selectedDestinationAccounts.size !== 1 ? "s" : ""), " ");
|
|
914
|
+
} }
|
|
915
|
+
/**
|
|
916
|
+
* Displays files and folders in a grid/list view.
|
|
917
|
+
* Shows file metadata like name, size, type, and last modified date.
|
|
918
|
+
* Supports file selection and navigation to folders.
|
|
919
|
+
*/
|
|
920
|
+
export class FileGridComponent {
|
|
921
|
+
/**
|
|
922
|
+
* The storage account to list files from (includes provider details)
|
|
923
|
+
*/
|
|
924
|
+
account = null;
|
|
925
|
+
/**
|
|
926
|
+
* The current folder path to display
|
|
927
|
+
*/
|
|
928
|
+
folderPath = '';
|
|
929
|
+
/**
|
|
930
|
+
* Emits when a folder is double-clicked for navigation
|
|
931
|
+
*/
|
|
932
|
+
folderNavigate = new EventEmitter();
|
|
933
|
+
/**
|
|
934
|
+
* Emits when the folder structure has changed (e.g., new folder created)
|
|
935
|
+
* This signals that the folder tree should refresh
|
|
936
|
+
*/
|
|
937
|
+
folderStructureChanged = new EventEmitter();
|
|
938
|
+
/**
|
|
939
|
+
* List of files and folders in the current directory
|
|
940
|
+
*/
|
|
941
|
+
items = [];
|
|
942
|
+
/**
|
|
943
|
+
* Currently selected item keys in the grid (Kendo stores keys, not full objects)
|
|
944
|
+
*/
|
|
945
|
+
selectedItems = [];
|
|
946
|
+
/**
|
|
947
|
+
* Loading state indicator
|
|
948
|
+
*/
|
|
949
|
+
isLoading = false;
|
|
950
|
+
/**
|
|
951
|
+
* Error message if loading fails
|
|
952
|
+
*/
|
|
953
|
+
errorMessage = null;
|
|
954
|
+
/**
|
|
955
|
+
* View mode: 'grid' or 'list'
|
|
956
|
+
*/
|
|
957
|
+
viewMode = 'list';
|
|
958
|
+
/**
|
|
959
|
+
* Sort configuration for the grid
|
|
960
|
+
*/
|
|
961
|
+
sort = [
|
|
962
|
+
{ field: 'name', dir: 'asc' }
|
|
963
|
+
];
|
|
964
|
+
/**
|
|
965
|
+
* Search query for filtering files/folders
|
|
966
|
+
*/
|
|
967
|
+
searchQuery = '';
|
|
968
|
+
/**
|
|
969
|
+
* File type filter ('all', 'files', 'folders')
|
|
970
|
+
*/
|
|
971
|
+
fileTypeFilter = 'all';
|
|
972
|
+
/**
|
|
973
|
+
* Filtered list of items based on search and filter criteria
|
|
974
|
+
*/
|
|
975
|
+
filteredItems = [];
|
|
976
|
+
/**
|
|
977
|
+
* Drag-and-drop state
|
|
978
|
+
*/
|
|
979
|
+
isDragging = false;
|
|
980
|
+
/**
|
|
981
|
+
* Upload progress state
|
|
982
|
+
*/
|
|
983
|
+
isUploading = false;
|
|
984
|
+
uploadProgress = 0;
|
|
985
|
+
uploadingFileName = '';
|
|
986
|
+
/**
|
|
987
|
+
* New folder dialog state
|
|
988
|
+
*/
|
|
989
|
+
showNewFolderDialog = false;
|
|
990
|
+
newFolderName = '';
|
|
991
|
+
isCreatingFolder = false;
|
|
992
|
+
/**
|
|
993
|
+
* Delete confirmation dialog state
|
|
994
|
+
*/
|
|
995
|
+
showDeleteDialog = false;
|
|
996
|
+
itemToDelete = null;
|
|
997
|
+
isDeleting = false;
|
|
998
|
+
/**
|
|
999
|
+
* Rename dialog state
|
|
1000
|
+
*/
|
|
1001
|
+
showRenameDialog = false;
|
|
1002
|
+
itemToRename = null;
|
|
1003
|
+
newItemName = '';
|
|
1004
|
+
isRenaming = false;
|
|
1005
|
+
/**
|
|
1006
|
+
* Copy dialog state
|
|
1007
|
+
*/
|
|
1008
|
+
showCopyDialog = false;
|
|
1009
|
+
itemToCopy = null;
|
|
1010
|
+
copyDestinationPath = '';
|
|
1011
|
+
isCopying = false;
|
|
1012
|
+
/**
|
|
1013
|
+
* Move dialog state
|
|
1014
|
+
*/
|
|
1015
|
+
showMoveDialog = false;
|
|
1016
|
+
itemToMove = null;
|
|
1017
|
+
moveDestinationPath = '';
|
|
1018
|
+
isMoving = false;
|
|
1019
|
+
/**
|
|
1020
|
+
* Copy to provider dialog state
|
|
1021
|
+
*/
|
|
1022
|
+
showCopyToProviderDialog = false;
|
|
1023
|
+
itemToCopyToProvider = null;
|
|
1024
|
+
availableAccounts = [];
|
|
1025
|
+
selectedDestinationAccounts = new Set();
|
|
1026
|
+
copyToAccountDestinationPath = '';
|
|
1027
|
+
isCopyingToAccount = false;
|
|
1028
|
+
copyToAccountProgress = null;
|
|
1029
|
+
/**
|
|
1030
|
+
* Multi-provider search state
|
|
1031
|
+
*/
|
|
1032
|
+
isMultiProviderSearchMode = false;
|
|
1033
|
+
multiProviderSearchQuery = '';
|
|
1034
|
+
selectedSearchProviders = new Set();
|
|
1035
|
+
isSearching = false;
|
|
1036
|
+
multiProviderSearchResults = null;
|
|
1037
|
+
/**
|
|
1038
|
+
* GraphQL client for file storage operations
|
|
1039
|
+
*/
|
|
1040
|
+
storageClient;
|
|
1041
|
+
constructor() {
|
|
1042
|
+
this.storageClient = new GraphQLFileStorageClient(GraphQLDataProvider.Instance);
|
|
1043
|
+
}
|
|
1044
|
+
ngOnInit() {
|
|
1045
|
+
// Don't load items here - wait for ngOnChanges when inputs are set
|
|
1046
|
+
}
|
|
1047
|
+
ngOnChanges(changes) {
|
|
1048
|
+
// Reload items when provider or folder path changes
|
|
1049
|
+
if (changes['account'] || changes['folderPath']) {
|
|
1050
|
+
this.loadItems();
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
/**
|
|
1054
|
+
* Loads files and folders from the current path
|
|
1055
|
+
*/
|
|
1056
|
+
async loadItems() {
|
|
1057
|
+
if (!this.account) {
|
|
1058
|
+
this.items = [];
|
|
1059
|
+
return;
|
|
1060
|
+
}
|
|
1061
|
+
const previousItemCount = this.items.length;
|
|
1062
|
+
const previousItemNames = this.items.map(i => i.name);
|
|
1063
|
+
this.isLoading = true;
|
|
1064
|
+
this.errorMessage = null;
|
|
1065
|
+
try {
|
|
1066
|
+
console.log('[FileGrid] Loading items for account:', this.account.account.ID, 'path:', this.folderPath);
|
|
1067
|
+
console.log('[FileGrid] Previous items:', { count: previousItemCount, names: previousItemNames });
|
|
1068
|
+
const listResult = await this.storageClient.ListObjects(this.account.account.ID, this.folderPath || '', '/');
|
|
1069
|
+
console.log('[FileGrid] ListObjects result:', listResult);
|
|
1070
|
+
this.items = [];
|
|
1071
|
+
// Add folders from prefixes first (so they appear before files)
|
|
1072
|
+
if (listResult.prefixes) {
|
|
1073
|
+
for (const prefix of listResult.prefixes) {
|
|
1074
|
+
// Extract the folder name from the prefix path
|
|
1075
|
+
// Prefix comes as "path/to/folder/" so we need to get just "folder"
|
|
1076
|
+
const folderName = prefix.endsWith('/')
|
|
1077
|
+
? prefix.slice(0, -1).split('/').pop()
|
|
1078
|
+
: prefix.split('/').pop();
|
|
1079
|
+
if (folderName) {
|
|
1080
|
+
this.items.push({
|
|
1081
|
+
key: prefix,
|
|
1082
|
+
name: folderName,
|
|
1083
|
+
type: 'folder',
|
|
1084
|
+
size: 0,
|
|
1085
|
+
lastModified: new Date(),
|
|
1086
|
+
contentType: 'application/x-directory'
|
|
1087
|
+
});
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
// Add files from objects
|
|
1092
|
+
if (listResult.objects) {
|
|
1093
|
+
for (const obj of listResult.objects) {
|
|
1094
|
+
// Skip directories from objects - we already added them from prefixes
|
|
1095
|
+
if (obj.isDirectory) {
|
|
1096
|
+
continue;
|
|
1097
|
+
}
|
|
1098
|
+
this.items.push({
|
|
1099
|
+
key: obj.fullPath,
|
|
1100
|
+
name: obj.name,
|
|
1101
|
+
type: 'file',
|
|
1102
|
+
size: obj.size,
|
|
1103
|
+
lastModified: obj.lastModified,
|
|
1104
|
+
contentType: obj.contentType,
|
|
1105
|
+
etag: obj.etag
|
|
1106
|
+
});
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
catch (error) {
|
|
1111
|
+
this.errorMessage = 'Failed to load files';
|
|
1112
|
+
console.error('[FileGrid] Error loading files:', error);
|
|
1113
|
+
}
|
|
1114
|
+
finally {
|
|
1115
|
+
this.isLoading = false;
|
|
1116
|
+
}
|
|
1117
|
+
// Apply filters after loading
|
|
1118
|
+
this.applyFilters();
|
|
1119
|
+
}
|
|
1120
|
+
/**
|
|
1121
|
+
* Applies search and filter criteria to the items list
|
|
1122
|
+
*/
|
|
1123
|
+
applyFilters() {
|
|
1124
|
+
let filtered = [...this.items];
|
|
1125
|
+
// Apply file type filter
|
|
1126
|
+
if (this.fileTypeFilter === 'files') {
|
|
1127
|
+
filtered = filtered.filter(item => item.type === 'file');
|
|
1128
|
+
}
|
|
1129
|
+
else if (this.fileTypeFilter === 'folders') {
|
|
1130
|
+
filtered = filtered.filter(item => item.type === 'folder');
|
|
1131
|
+
}
|
|
1132
|
+
// Apply search query
|
|
1133
|
+
if (this.searchQuery.trim()) {
|
|
1134
|
+
const query = this.searchQuery.toLowerCase();
|
|
1135
|
+
filtered = filtered.filter(item => item.name.toLowerCase().includes(query));
|
|
1136
|
+
}
|
|
1137
|
+
this.filteredItems = filtered;
|
|
1138
|
+
}
|
|
1139
|
+
/**
|
|
1140
|
+
* Handles search query changes
|
|
1141
|
+
*/
|
|
1142
|
+
onSearchChange(query) {
|
|
1143
|
+
this.searchQuery = query;
|
|
1144
|
+
this.applyFilters();
|
|
1145
|
+
}
|
|
1146
|
+
/**
|
|
1147
|
+
* Clears the search query
|
|
1148
|
+
*/
|
|
1149
|
+
clearSearch() {
|
|
1150
|
+
this.searchQuery = '';
|
|
1151
|
+
this.applyFilters();
|
|
1152
|
+
}
|
|
1153
|
+
/**
|
|
1154
|
+
* Handles file type filter changes
|
|
1155
|
+
*/
|
|
1156
|
+
onFileTypeFilterChange(filterType) {
|
|
1157
|
+
this.fileTypeFilter = filterType;
|
|
1158
|
+
this.applyFilters();
|
|
1159
|
+
}
|
|
1160
|
+
/**
|
|
1161
|
+
* Formats a file size in bytes to a human-readable string
|
|
1162
|
+
*/
|
|
1163
|
+
formatFileSize(bytes) {
|
|
1164
|
+
if (bytes === 0)
|
|
1165
|
+
return '0 B';
|
|
1166
|
+
const k = 1024;
|
|
1167
|
+
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
1168
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
1169
|
+
return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i];
|
|
1170
|
+
}
|
|
1171
|
+
/**
|
|
1172
|
+
* Formats a date to a readable string
|
|
1173
|
+
*/
|
|
1174
|
+
formatDate(date) {
|
|
1175
|
+
return date.toLocaleString();
|
|
1176
|
+
}
|
|
1177
|
+
/**
|
|
1178
|
+
* Gets the icon class for a file or folder
|
|
1179
|
+
*/
|
|
1180
|
+
getItemIcon(item) {
|
|
1181
|
+
if (!item || !item.name) {
|
|
1182
|
+
return 'fa-solid fa-file';
|
|
1183
|
+
}
|
|
1184
|
+
if (item.type === 'folder') {
|
|
1185
|
+
return 'fa-solid fa-folder';
|
|
1186
|
+
}
|
|
1187
|
+
// Determine file icon based on content type or extension
|
|
1188
|
+
const extension = item.name.split('.').pop()?.toLowerCase() || '';
|
|
1189
|
+
if (['jpg', 'jpeg', 'png', 'gif', 'bmp', 'svg'].includes(extension)) {
|
|
1190
|
+
return 'fa-solid fa-file-image';
|
|
1191
|
+
}
|
|
1192
|
+
else if (['pdf'].includes(extension)) {
|
|
1193
|
+
return 'fa-solid fa-file-pdf';
|
|
1194
|
+
}
|
|
1195
|
+
else if (['doc', 'docx'].includes(extension)) {
|
|
1196
|
+
return 'fa-solid fa-file-word';
|
|
1197
|
+
}
|
|
1198
|
+
else if (['xls', 'xlsx'].includes(extension)) {
|
|
1199
|
+
return 'fa-solid fa-file-excel';
|
|
1200
|
+
}
|
|
1201
|
+
else if (['ppt', 'pptx'].includes(extension)) {
|
|
1202
|
+
return 'fa-solid fa-file-powerpoint';
|
|
1203
|
+
}
|
|
1204
|
+
else if (['zip', 'rar', '7z', 'tar', 'gz'].includes(extension)) {
|
|
1205
|
+
return 'fa-solid fa-file-zipper';
|
|
1206
|
+
}
|
|
1207
|
+
else if (['mp4', 'avi', 'mov', 'wmv'].includes(extension)) {
|
|
1208
|
+
return 'fa-solid fa-file-video';
|
|
1209
|
+
}
|
|
1210
|
+
else if (['mp3', 'wav', 'ogg', 'flac'].includes(extension)) {
|
|
1211
|
+
return 'fa-solid fa-file-audio';
|
|
1212
|
+
}
|
|
1213
|
+
else if (['txt', 'log'].includes(extension)) {
|
|
1214
|
+
return 'fa-solid fa-file-lines';
|
|
1215
|
+
}
|
|
1216
|
+
else if (['js', 'ts', 'html', 'css', 'json', 'xml', 'py', 'java', 'cpp'].includes(extension)) {
|
|
1217
|
+
return 'fa-solid fa-file-code';
|
|
1218
|
+
}
|
|
1219
|
+
else {
|
|
1220
|
+
return 'fa-solid fa-file';
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
/**
|
|
1224
|
+
* Handles item selection change (not used - Kendo handles selection internally)
|
|
1225
|
+
*/
|
|
1226
|
+
onSelectionChange(selectedKeys) {
|
|
1227
|
+
this.selectedItems = selectedKeys;
|
|
1228
|
+
}
|
|
1229
|
+
/**
|
|
1230
|
+
* Handles tile click in grid view for selection
|
|
1231
|
+
*/
|
|
1232
|
+
onTileClick(item, event) {
|
|
1233
|
+
if (event.ctrlKey || event.metaKey) {
|
|
1234
|
+
// Multi-select: toggle item key
|
|
1235
|
+
const index = this.selectedItems.indexOf(item.key);
|
|
1236
|
+
if (index >= 0) {
|
|
1237
|
+
this.selectedItems.splice(index, 1);
|
|
1238
|
+
}
|
|
1239
|
+
else {
|
|
1240
|
+
this.selectedItems.push(item.key);
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
else if (event.shiftKey && this.selectedItems.length > 0) {
|
|
1244
|
+
// Range select: select from last selected to current
|
|
1245
|
+
const lastSelectedKey = this.selectedItems[this.selectedItems.length - 1];
|
|
1246
|
+
const lastSelected = this.items.find(i => i.key === lastSelectedKey);
|
|
1247
|
+
if (lastSelected) {
|
|
1248
|
+
const lastIndex = this.items.indexOf(lastSelected);
|
|
1249
|
+
const currentIndex = this.items.indexOf(item);
|
|
1250
|
+
const start = Math.min(lastIndex, currentIndex);
|
|
1251
|
+
const end = Math.max(lastIndex, currentIndex);
|
|
1252
|
+
this.selectedItems = this.items.slice(start, end + 1).map(i => i.key);
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
else {
|
|
1256
|
+
// Single select: replace selection
|
|
1257
|
+
this.selectedItems = [item.key];
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
/**
|
|
1261
|
+
* Handles double-click on an item
|
|
1262
|
+
* For folders, navigate into them. For files, open/download them.
|
|
1263
|
+
*/
|
|
1264
|
+
onItemDoubleClick(item) {
|
|
1265
|
+
if (item.type === 'folder') {
|
|
1266
|
+
// Navigate into folder by emitting the folder path
|
|
1267
|
+
console.log('[FileGrid] Navigating to folder:', item.key);
|
|
1268
|
+
this.folderNavigate.emit(item.key);
|
|
1269
|
+
}
|
|
1270
|
+
else {
|
|
1271
|
+
// Download file
|
|
1272
|
+
this.downloadFile(item);
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
/**
|
|
1276
|
+
* Downloads a file by creating a pre-authenticated download URL
|
|
1277
|
+
*/
|
|
1278
|
+
async downloadFile(item) {
|
|
1279
|
+
if (!this.account) {
|
|
1280
|
+
return;
|
|
1281
|
+
}
|
|
1282
|
+
try {
|
|
1283
|
+
console.log('[FileGrid] Downloading file:', item.key);
|
|
1284
|
+
const downloadUrl = await this.storageClient.CreatePreAuthDownloadUrl(this.account.account.ID, item.key);
|
|
1285
|
+
console.log('[FileGrid] Download URL created:', downloadUrl ? 'success' : 'failed');
|
|
1286
|
+
if (downloadUrl) {
|
|
1287
|
+
// Trigger browser download
|
|
1288
|
+
const link = document.createElement('a');
|
|
1289
|
+
link.href = downloadUrl;
|
|
1290
|
+
link.download = item.name;
|
|
1291
|
+
link.target = '_blank'; // Open in new tab as fallback
|
|
1292
|
+
document.body.appendChild(link);
|
|
1293
|
+
link.click();
|
|
1294
|
+
document.body.removeChild(link);
|
|
1295
|
+
console.log('[FileGrid] File download initiated:', item.name);
|
|
1296
|
+
}
|
|
1297
|
+
else {
|
|
1298
|
+
console.error('[FileGrid] Failed to get download URL');
|
|
1299
|
+
this.errorMessage = 'Failed to generate download URL';
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
catch (error) {
|
|
1303
|
+
console.error('[FileGrid] Error downloading file:', error);
|
|
1304
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
1305
|
+
this.errorMessage = `Failed to download file: ${errorMessage}`;
|
|
1306
|
+
// Clear error after 5 seconds
|
|
1307
|
+
setTimeout(() => {
|
|
1308
|
+
if (this.errorMessage?.startsWith('Failed to download')) {
|
|
1309
|
+
this.errorMessage = null;
|
|
1310
|
+
}
|
|
1311
|
+
}, 5000);
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
/**
|
|
1315
|
+
* Toggles between grid and list view
|
|
1316
|
+
*/
|
|
1317
|
+
toggleViewMode() {
|
|
1318
|
+
this.viewMode = this.viewMode === 'grid' ? 'list' : 'grid';
|
|
1319
|
+
}
|
|
1320
|
+
/**
|
|
1321
|
+
* Refreshes the current directory
|
|
1322
|
+
*/
|
|
1323
|
+
refresh() {
|
|
1324
|
+
this.loadItems();
|
|
1325
|
+
}
|
|
1326
|
+
/**
|
|
1327
|
+
* Navigates up to the parent directory
|
|
1328
|
+
*/
|
|
1329
|
+
navigateUp() {
|
|
1330
|
+
if (!this.folderPath) {
|
|
1331
|
+
// Already at root
|
|
1332
|
+
return;
|
|
1333
|
+
}
|
|
1334
|
+
// Remove trailing slash if present
|
|
1335
|
+
const cleanPath = this.folderPath.endsWith('/')
|
|
1336
|
+
? this.folderPath.slice(0, -1)
|
|
1337
|
+
: this.folderPath;
|
|
1338
|
+
// Get parent path by removing last segment
|
|
1339
|
+
const segments = cleanPath.split('/').filter(s => s.length > 0);
|
|
1340
|
+
segments.pop(); // Remove last segment
|
|
1341
|
+
const parentPath = segments.length > 0 ? segments.join('/') + '/' : '';
|
|
1342
|
+
// Emit navigation event to update the folder tree and path
|
|
1343
|
+
this.folderNavigate.emit(parentPath);
|
|
1344
|
+
}
|
|
1345
|
+
/**
|
|
1346
|
+
* Checks if we can navigate up (not at root)
|
|
1347
|
+
*/
|
|
1348
|
+
canNavigateUp() {
|
|
1349
|
+
return this.folderPath !== '' && this.folderPath !== '/';
|
|
1350
|
+
}
|
|
1351
|
+
/**
|
|
1352
|
+
* Handles sort change event from the grid
|
|
1353
|
+
*/
|
|
1354
|
+
onSortChange(sort) {
|
|
1355
|
+
this.sort = sort;
|
|
1356
|
+
}
|
|
1357
|
+
/**
|
|
1358
|
+
* Gets a human-readable file type based on extension
|
|
1359
|
+
*/
|
|
1360
|
+
getFileType(item) {
|
|
1361
|
+
if (item.type === 'folder') {
|
|
1362
|
+
return 'Folder';
|
|
1363
|
+
}
|
|
1364
|
+
const extension = item.name.split('.').pop()?.toLowerCase() || '';
|
|
1365
|
+
const typeMap = {
|
|
1366
|
+
// Documents
|
|
1367
|
+
pdf: 'PDF Document',
|
|
1368
|
+
doc: 'Word Document',
|
|
1369
|
+
docx: 'Word Document',
|
|
1370
|
+
xls: 'Excel Spreadsheet',
|
|
1371
|
+
xlsx: 'Excel Spreadsheet',
|
|
1372
|
+
ppt: 'PowerPoint',
|
|
1373
|
+
pptx: 'PowerPoint',
|
|
1374
|
+
txt: 'Text File',
|
|
1375
|
+
// Images
|
|
1376
|
+
jpg: 'JPEG Image',
|
|
1377
|
+
jpeg: 'JPEG Image',
|
|
1378
|
+
png: 'PNG Image',
|
|
1379
|
+
gif: 'GIF Image',
|
|
1380
|
+
bmp: 'Bitmap Image',
|
|
1381
|
+
svg: 'SVG Image',
|
|
1382
|
+
// Archives
|
|
1383
|
+
zip: 'ZIP Archive',
|
|
1384
|
+
rar: 'RAR Archive',
|
|
1385
|
+
'7z': '7-Zip Archive',
|
|
1386
|
+
tar: 'TAR Archive',
|
|
1387
|
+
gz: 'GZIP Archive',
|
|
1388
|
+
// Media
|
|
1389
|
+
mp4: 'MP4 Video',
|
|
1390
|
+
avi: 'AVI Video',
|
|
1391
|
+
mov: 'QuickTime Video',
|
|
1392
|
+
mp3: 'MP3 Audio',
|
|
1393
|
+
wav: 'WAV Audio',
|
|
1394
|
+
// Code
|
|
1395
|
+
js: 'JavaScript',
|
|
1396
|
+
ts: 'TypeScript',
|
|
1397
|
+
html: 'HTML',
|
|
1398
|
+
css: 'CSS',
|
|
1399
|
+
json: 'JSON',
|
|
1400
|
+
xml: 'XML',
|
|
1401
|
+
py: 'Python',
|
|
1402
|
+
java: 'Java',
|
|
1403
|
+
cpp: 'C++'
|
|
1404
|
+
};
|
|
1405
|
+
return typeMap[extension] || `${extension.toUpperCase()} File`;
|
|
1406
|
+
}
|
|
1407
|
+
/**
|
|
1408
|
+
* Handles upload button click - triggers file input
|
|
1409
|
+
*/
|
|
1410
|
+
onUploadClick() {
|
|
1411
|
+
const fileInput = document.createElement('input');
|
|
1412
|
+
fileInput.type = 'file';
|
|
1413
|
+
fileInput.multiple = true;
|
|
1414
|
+
fileInput.onchange = (event) => {
|
|
1415
|
+
const target = event.target;
|
|
1416
|
+
if (target.files && target.files.length > 0) {
|
|
1417
|
+
this.uploadFiles(Array.from(target.files));
|
|
1418
|
+
}
|
|
1419
|
+
};
|
|
1420
|
+
fileInput.click();
|
|
1421
|
+
}
|
|
1422
|
+
/**
|
|
1423
|
+
* Handles drag enter event
|
|
1424
|
+
*/
|
|
1425
|
+
onDragEnter(event) {
|
|
1426
|
+
event.preventDefault();
|
|
1427
|
+
event.stopPropagation();
|
|
1428
|
+
this.isDragging = true;
|
|
1429
|
+
}
|
|
1430
|
+
/**
|
|
1431
|
+
* Handles drag over event
|
|
1432
|
+
*/
|
|
1433
|
+
onDragOver(event) {
|
|
1434
|
+
event.preventDefault();
|
|
1435
|
+
event.stopPropagation();
|
|
1436
|
+
}
|
|
1437
|
+
/**
|
|
1438
|
+
* Handles drag leave event
|
|
1439
|
+
*/
|
|
1440
|
+
onDragLeave(event) {
|
|
1441
|
+
event.preventDefault();
|
|
1442
|
+
event.stopPropagation();
|
|
1443
|
+
// Only hide if leaving the container itself, not child elements
|
|
1444
|
+
if (event.currentTarget === event.target) {
|
|
1445
|
+
this.isDragging = false;
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
/**
|
|
1449
|
+
* Handles drop event
|
|
1450
|
+
*/
|
|
1451
|
+
onDrop(event) {
|
|
1452
|
+
event.preventDefault();
|
|
1453
|
+
event.stopPropagation();
|
|
1454
|
+
this.isDragging = false;
|
|
1455
|
+
const files = event.dataTransfer?.files;
|
|
1456
|
+
if (files && files.length > 0) {
|
|
1457
|
+
this.uploadFiles(Array.from(files));
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
/**
|
|
1461
|
+
* Uploads multiple files to the current folder
|
|
1462
|
+
*/
|
|
1463
|
+
async uploadFiles(files) {
|
|
1464
|
+
if (!this.account) {
|
|
1465
|
+
return;
|
|
1466
|
+
}
|
|
1467
|
+
console.log('[FileGrid] Uploading files:', files.map(f => f.name));
|
|
1468
|
+
for (let i = 0; i < files.length; i++) {
|
|
1469
|
+
const file = files[i];
|
|
1470
|
+
await this.uploadSingleFile(file, i + 1, files.length);
|
|
1471
|
+
}
|
|
1472
|
+
// Refresh the file list after all uploads complete
|
|
1473
|
+
this.loadItems();
|
|
1474
|
+
}
|
|
1475
|
+
/**
|
|
1476
|
+
* Uploads a single file
|
|
1477
|
+
*/
|
|
1478
|
+
async uploadSingleFile(file, current, total) {
|
|
1479
|
+
if (!this.account) {
|
|
1480
|
+
return;
|
|
1481
|
+
}
|
|
1482
|
+
this.isUploading = true;
|
|
1483
|
+
this.uploadingFileName = `${file.name} (${current}/${total})`;
|
|
1484
|
+
this.uploadProgress = 0;
|
|
1485
|
+
try {
|
|
1486
|
+
// Construct the full object name with folder path
|
|
1487
|
+
// Remove trailing slash from folderPath to avoid double slashes
|
|
1488
|
+
let objectName;
|
|
1489
|
+
if (this.folderPath) {
|
|
1490
|
+
const cleanPath = this.folderPath.endsWith('/')
|
|
1491
|
+
? this.folderPath.slice(0, -1)
|
|
1492
|
+
: this.folderPath;
|
|
1493
|
+
objectName = `${cleanPath}/${file.name}`;
|
|
1494
|
+
}
|
|
1495
|
+
else {
|
|
1496
|
+
objectName = file.name;
|
|
1497
|
+
}
|
|
1498
|
+
console.log('[FileGrid] Getting upload URL for:', objectName);
|
|
1499
|
+
// Get pre-authenticated upload URL
|
|
1500
|
+
const uploadData = await this.storageClient.CreatePreAuthUploadUrl(this.account.account.ID, objectName, file.type || 'application/octet-stream');
|
|
1501
|
+
console.log('[FileGrid] Got upload URL:', uploadData.uploadUrl);
|
|
1502
|
+
// Upload the file using XMLHttpRequest to track progress
|
|
1503
|
+
await this.uploadFileToUrl(file, uploadData.uploadUrl);
|
|
1504
|
+
console.log('[FileGrid] File uploaded successfully:', file.name);
|
|
1505
|
+
}
|
|
1506
|
+
catch (error) {
|
|
1507
|
+
console.error('[FileGrid] Error uploading file:', error);
|
|
1508
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
1509
|
+
// Check if this is an unsupported operation error (e.g., Google Drive doesn't support pre-auth uploads)
|
|
1510
|
+
if (errorMessage.includes('not supported')) {
|
|
1511
|
+
this.errorMessage = `Upload not supported for ${this.account?.account.Name || 'this account'}. Try using a different storage account.`;
|
|
1512
|
+
}
|
|
1513
|
+
else {
|
|
1514
|
+
this.errorMessage = `Failed to upload ${file.name}: ${errorMessage}`;
|
|
1515
|
+
}
|
|
1516
|
+
// Clear error after 5 seconds
|
|
1517
|
+
setTimeout(() => {
|
|
1518
|
+
if (this.errorMessage?.startsWith('Failed to upload') || this.errorMessage?.startsWith('Upload not supported')) {
|
|
1519
|
+
this.errorMessage = null;
|
|
1520
|
+
}
|
|
1521
|
+
}, 5000);
|
|
1522
|
+
}
|
|
1523
|
+
finally {
|
|
1524
|
+
this.isUploading = false;
|
|
1525
|
+
this.uploadProgress = 0;
|
|
1526
|
+
this.uploadingFileName = '';
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
/**
|
|
1530
|
+
* Uploads file to the pre-authenticated URL with progress tracking
|
|
1531
|
+
*/
|
|
1532
|
+
uploadFileToUrl(file, url) {
|
|
1533
|
+
return new Promise((resolve, reject) => {
|
|
1534
|
+
const xhr = new XMLHttpRequest();
|
|
1535
|
+
xhr.upload.addEventListener('progress', (event) => {
|
|
1536
|
+
if (event.lengthComputable) {
|
|
1537
|
+
this.uploadProgress = Math.round((event.loaded / event.total) * 100);
|
|
1538
|
+
}
|
|
1539
|
+
});
|
|
1540
|
+
xhr.addEventListener('load', () => {
|
|
1541
|
+
if (xhr.status >= 200 && xhr.status < 300) {
|
|
1542
|
+
resolve();
|
|
1543
|
+
}
|
|
1544
|
+
else {
|
|
1545
|
+
reject(new Error(`Upload failed with status ${xhr.status}`));
|
|
1546
|
+
}
|
|
1547
|
+
});
|
|
1548
|
+
xhr.addEventListener('error', () => {
|
|
1549
|
+
reject(new Error('Network error during upload'));
|
|
1550
|
+
});
|
|
1551
|
+
xhr.addEventListener('abort', () => {
|
|
1552
|
+
reject(new Error('Upload cancelled'));
|
|
1553
|
+
});
|
|
1554
|
+
// Use POST for Dropbox temporary upload links (required by Dropbox API)
|
|
1555
|
+
// Use PUT for other providers like S3
|
|
1556
|
+
// Dropbox requires POST with application/octet-stream
|
|
1557
|
+
xhr.open('POST', url, true);
|
|
1558
|
+
xhr.setRequestHeader('Content-Type', 'application/octet-stream');
|
|
1559
|
+
xhr.send(file);
|
|
1560
|
+
});
|
|
1561
|
+
}
|
|
1562
|
+
/**
|
|
1563
|
+
* Opens the new folder dialog
|
|
1564
|
+
*/
|
|
1565
|
+
onNewFolderClick() {
|
|
1566
|
+
this.newFolderName = '';
|
|
1567
|
+
this.showNewFolderDialog = true;
|
|
1568
|
+
}
|
|
1569
|
+
/**
|
|
1570
|
+
* Closes the new folder dialog
|
|
1571
|
+
*/
|
|
1572
|
+
onCancelNewFolder() {
|
|
1573
|
+
this.showNewFolderDialog = false;
|
|
1574
|
+
this.newFolderName = '';
|
|
1575
|
+
}
|
|
1576
|
+
/**
|
|
1577
|
+
* Creates a new folder in the current directory
|
|
1578
|
+
*/
|
|
1579
|
+
async onCreateFolder() {
|
|
1580
|
+
if (!this.account || !this.newFolderName.trim()) {
|
|
1581
|
+
return;
|
|
1582
|
+
}
|
|
1583
|
+
this.isCreatingFolder = true;
|
|
1584
|
+
try {
|
|
1585
|
+
// Construct the full folder path
|
|
1586
|
+
// Remove trailing slash from folderPath to avoid double slashes
|
|
1587
|
+
let folderPath;
|
|
1588
|
+
if (this.folderPath) {
|
|
1589
|
+
const cleanPath = this.folderPath.endsWith('/')
|
|
1590
|
+
? this.folderPath.slice(0, -1)
|
|
1591
|
+
: this.folderPath;
|
|
1592
|
+
folderPath = `${cleanPath}/${this.newFolderName.trim()}`;
|
|
1593
|
+
}
|
|
1594
|
+
else {
|
|
1595
|
+
folderPath = this.newFolderName.trim();
|
|
1596
|
+
}
|
|
1597
|
+
console.log('[FileGrid] Creating folder:', folderPath);
|
|
1598
|
+
const success = await this.storageClient.CreateDirectory(this.account.account.ID, folderPath);
|
|
1599
|
+
if (success) {
|
|
1600
|
+
console.log('[FileGrid] Folder created successfully:', folderPath);
|
|
1601
|
+
// Close dialog and refresh
|
|
1602
|
+
this.showNewFolderDialog = false;
|
|
1603
|
+
this.newFolderName = '';
|
|
1604
|
+
// Notify parent that folder structure changed
|
|
1605
|
+
this.folderStructureChanged.emit();
|
|
1606
|
+
// Refresh the file grid
|
|
1607
|
+
this.loadItems();
|
|
1608
|
+
}
|
|
1609
|
+
else {
|
|
1610
|
+
this.errorMessage = 'Failed to create folder';
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
catch (error) {
|
|
1614
|
+
console.error('[FileGrid] Error creating folder:', error);
|
|
1615
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
1616
|
+
this.errorMessage = `Failed to create folder: ${errorMessage}`;
|
|
1617
|
+
// Clear error after 5 seconds
|
|
1618
|
+
setTimeout(() => {
|
|
1619
|
+
if (this.errorMessage?.startsWith('Failed to create')) {
|
|
1620
|
+
this.errorMessage = null;
|
|
1621
|
+
}
|
|
1622
|
+
}, 5000);
|
|
1623
|
+
}
|
|
1624
|
+
finally {
|
|
1625
|
+
this.isCreatingFolder = false;
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
/**
|
|
1629
|
+
* Opens the delete confirmation dialog for the selected items
|
|
1630
|
+
*/
|
|
1631
|
+
onDeleteClick() {
|
|
1632
|
+
if (this.selectedItems.length === 0) {
|
|
1633
|
+
return;
|
|
1634
|
+
}
|
|
1635
|
+
// selectedItems contains keys (strings), not full objects
|
|
1636
|
+
// Find the actual item object from the items array
|
|
1637
|
+
const selectedKey = this.selectedItems[0];
|
|
1638
|
+
const item = this.items.find(i => i.key === selectedKey);
|
|
1639
|
+
if (!item) {
|
|
1640
|
+
console.error('[FileGrid] Could not find selected item with key:', selectedKey);
|
|
1641
|
+
return;
|
|
1642
|
+
}
|
|
1643
|
+
this.itemToDelete = item;
|
|
1644
|
+
this.showDeleteDialog = true;
|
|
1645
|
+
}
|
|
1646
|
+
/**
|
|
1647
|
+
* Closes the delete confirmation dialog
|
|
1648
|
+
*/
|
|
1649
|
+
onCancelDelete() {
|
|
1650
|
+
this.showDeleteDialog = false;
|
|
1651
|
+
this.itemToDelete = null;
|
|
1652
|
+
}
|
|
1653
|
+
/**
|
|
1654
|
+
* Deletes the selected item after confirmation
|
|
1655
|
+
*/
|
|
1656
|
+
async onConfirmDelete() {
|
|
1657
|
+
if (!this.account || !this.itemToDelete) {
|
|
1658
|
+
return;
|
|
1659
|
+
}
|
|
1660
|
+
this.isDeleting = true;
|
|
1661
|
+
try {
|
|
1662
|
+
// Construct the full path to the item
|
|
1663
|
+
const itemPath = this.constructItemPath(this.itemToDelete);
|
|
1664
|
+
console.log('[FileGrid] Deleting item:', itemPath);
|
|
1665
|
+
const success = await this.storageClient.DeleteObject(this.account.account.ID, itemPath);
|
|
1666
|
+
console.log('[FileGrid] Delete result:', { success });
|
|
1667
|
+
if (success) {
|
|
1668
|
+
console.log('[FileGrid] Item deleted successfully:', itemPath);
|
|
1669
|
+
// Check if we deleted a folder (before clearing itemToDelete)
|
|
1670
|
+
const wasFolder = this.itemToDelete.type === 'folder';
|
|
1671
|
+
console.log('[FileGrid] Item details:', {
|
|
1672
|
+
type: this.itemToDelete.type,
|
|
1673
|
+
name: this.itemToDelete.name,
|
|
1674
|
+
key: this.itemToDelete.key,
|
|
1675
|
+
wasFolder
|
|
1676
|
+
});
|
|
1677
|
+
// Close dialog
|
|
1678
|
+
this.showDeleteDialog = false;
|
|
1679
|
+
this.itemToDelete = null;
|
|
1680
|
+
// Clear selection
|
|
1681
|
+
this.selectedItems = [];
|
|
1682
|
+
// If we deleted a folder, notify parent that folder structure changed
|
|
1683
|
+
if (wasFolder) {
|
|
1684
|
+
console.log('[FileGrid] Emitting folderStructureChanged event');
|
|
1685
|
+
this.folderStructureChanged.emit();
|
|
1686
|
+
}
|
|
1687
|
+
// Refresh the file grid
|
|
1688
|
+
console.log('[FileGrid] Calling loadItems() to refresh grid');
|
|
1689
|
+
this.loadItems();
|
|
1690
|
+
}
|
|
1691
|
+
else {
|
|
1692
|
+
console.error('[FileGrid] Delete operation returned false');
|
|
1693
|
+
this.errorMessage = `Failed to delete ${this.itemToDelete.type}`;
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
catch (error) {
|
|
1697
|
+
console.error('[FileGrid] Error deleting item:', error);
|
|
1698
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
1699
|
+
this.errorMessage = `Failed to delete: ${errorMessage}`;
|
|
1700
|
+
// Clear error after 5 seconds
|
|
1701
|
+
setTimeout(() => {
|
|
1702
|
+
if (this.errorMessage?.startsWith('Failed to delete')) {
|
|
1703
|
+
this.errorMessage = null;
|
|
1704
|
+
}
|
|
1705
|
+
}, 5000);
|
|
1706
|
+
}
|
|
1707
|
+
finally {
|
|
1708
|
+
this.isDeleting = false;
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
/**
|
|
1712
|
+
* Opens the rename dialog for the selected item
|
|
1713
|
+
*/
|
|
1714
|
+
onRenameClick() {
|
|
1715
|
+
if (this.selectedItems.length !== 1) {
|
|
1716
|
+
return;
|
|
1717
|
+
}
|
|
1718
|
+
// selectedItems contains keys (strings), not full objects
|
|
1719
|
+
// Find the actual item object from the items array
|
|
1720
|
+
const selectedKey = this.selectedItems[0];
|
|
1721
|
+
const item = this.items.find(i => i.key === selectedKey);
|
|
1722
|
+
if (!item) {
|
|
1723
|
+
console.error('[FileGrid] Could not find selected item with key:', selectedKey);
|
|
1724
|
+
return;
|
|
1725
|
+
}
|
|
1726
|
+
this.itemToRename = item;
|
|
1727
|
+
this.newItemName = item.name;
|
|
1728
|
+
this.showRenameDialog = true;
|
|
1729
|
+
}
|
|
1730
|
+
/**
|
|
1731
|
+
* Closes the rename dialog
|
|
1732
|
+
*/
|
|
1733
|
+
onCancelRename() {
|
|
1734
|
+
this.showRenameDialog = false;
|
|
1735
|
+
this.itemToRename = null;
|
|
1736
|
+
this.newItemName = '';
|
|
1737
|
+
}
|
|
1738
|
+
/**
|
|
1739
|
+
* Renames the selected item after confirmation
|
|
1740
|
+
*/
|
|
1741
|
+
async onConfirmRename() {
|
|
1742
|
+
if (!this.account || !this.itemToRename || !this.newItemName.trim()) {
|
|
1743
|
+
return;
|
|
1744
|
+
}
|
|
1745
|
+
// Check if name actually changed
|
|
1746
|
+
if (this.newItemName.trim() === this.itemToRename.name) {
|
|
1747
|
+
this.onCancelRename();
|
|
1748
|
+
return;
|
|
1749
|
+
}
|
|
1750
|
+
this.isRenaming = true;
|
|
1751
|
+
try {
|
|
1752
|
+
// Construct the old and new paths
|
|
1753
|
+
const oldPath = this.constructItemPath(this.itemToRename);
|
|
1754
|
+
// Build the new path by replacing the old name with the new name
|
|
1755
|
+
const pathParts = oldPath.split('/');
|
|
1756
|
+
pathParts[pathParts.length - 1] = this.newItemName.trim();
|
|
1757
|
+
const newPath = pathParts.join('/');
|
|
1758
|
+
console.log('[FileGrid] Renaming item:', { oldPath, newPath });
|
|
1759
|
+
const success = await this.storageClient.MoveObject(this.account.account.ID, oldPath, newPath);
|
|
1760
|
+
console.log('[FileGrid] Rename result:', { success });
|
|
1761
|
+
if (success) {
|
|
1762
|
+
console.log('[FileGrid] Item renamed successfully:', { oldPath, newPath });
|
|
1763
|
+
// Check if we renamed a folder (before clearing itemToRename)
|
|
1764
|
+
const wasFolder = this.itemToRename.type === 'folder';
|
|
1765
|
+
// Close dialog
|
|
1766
|
+
this.showRenameDialog = false;
|
|
1767
|
+
this.itemToRename = null;
|
|
1768
|
+
this.newItemName = '';
|
|
1769
|
+
// Clear selection
|
|
1770
|
+
this.selectedItems = [];
|
|
1771
|
+
// If we renamed a folder, notify parent that folder structure changed
|
|
1772
|
+
if (wasFolder) {
|
|
1773
|
+
console.log('[FileGrid] Emitting folderStructureChanged event');
|
|
1774
|
+
this.folderStructureChanged.emit();
|
|
1775
|
+
}
|
|
1776
|
+
// Refresh the file grid
|
|
1777
|
+
console.log('[FileGrid] Calling loadItems() to refresh grid');
|
|
1778
|
+
this.loadItems();
|
|
1779
|
+
}
|
|
1780
|
+
else {
|
|
1781
|
+
console.error('[FileGrid] Rename operation returned false');
|
|
1782
|
+
this.errorMessage = `Failed to rename ${this.itemToRename.type}`;
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1785
|
+
catch (error) {
|
|
1786
|
+
console.error('[FileGrid] Error renaming item:', error);
|
|
1787
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
1788
|
+
this.errorMessage = `Failed to rename: ${errorMessage}`;
|
|
1789
|
+
// Clear error after 5 seconds
|
|
1790
|
+
setTimeout(() => {
|
|
1791
|
+
if (this.errorMessage?.startsWith('Failed to rename')) {
|
|
1792
|
+
this.errorMessage = null;
|
|
1793
|
+
}
|
|
1794
|
+
}, 5000);
|
|
1795
|
+
}
|
|
1796
|
+
finally {
|
|
1797
|
+
this.isRenaming = false;
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
/**
|
|
1801
|
+
* Downloads the selected file
|
|
1802
|
+
*/
|
|
1803
|
+
async onDownloadClick() {
|
|
1804
|
+
if (this.selectedItems.length !== 1) {
|
|
1805
|
+
return;
|
|
1806
|
+
}
|
|
1807
|
+
// Find the selected item
|
|
1808
|
+
const selectedKey = this.selectedItems[0];
|
|
1809
|
+
const item = this.items.find(i => i.key === selectedKey);
|
|
1810
|
+
if (!item) {
|
|
1811
|
+
console.error('[FileGrid] Could not find selected item with key:', selectedKey);
|
|
1812
|
+
return;
|
|
1813
|
+
}
|
|
1814
|
+
// Can only download files, not folders
|
|
1815
|
+
if (item.type === 'folder') {
|
|
1816
|
+
this.errorMessage = 'Cannot download folders. Please select a file.';
|
|
1817
|
+
setTimeout(() => {
|
|
1818
|
+
if (this.errorMessage?.includes('Cannot download folders')) {
|
|
1819
|
+
this.errorMessage = null;
|
|
1820
|
+
}
|
|
1821
|
+
}, 3000);
|
|
1822
|
+
return;
|
|
1823
|
+
}
|
|
1824
|
+
if (!this.account) {
|
|
1825
|
+
return;
|
|
1826
|
+
}
|
|
1827
|
+
try {
|
|
1828
|
+
const itemPath = this.constructItemPath(item);
|
|
1829
|
+
console.log('[FileGrid] Creating download URL for:', itemPath);
|
|
1830
|
+
const downloadUrl = await this.storageClient.CreatePreAuthDownloadUrl(this.account.account.ID, itemPath);
|
|
1831
|
+
if (downloadUrl) {
|
|
1832
|
+
console.log('[FileGrid] Download URL created, initiating download');
|
|
1833
|
+
// Create a temporary anchor element to trigger download
|
|
1834
|
+
const link = document.createElement('a');
|
|
1835
|
+
link.href = downloadUrl;
|
|
1836
|
+
link.download = item.name;
|
|
1837
|
+
link.style.display = 'none';
|
|
1838
|
+
document.body.appendChild(link);
|
|
1839
|
+
link.click();
|
|
1840
|
+
document.body.removeChild(link);
|
|
1841
|
+
}
|
|
1842
|
+
else {
|
|
1843
|
+
this.errorMessage = 'Failed to create download URL';
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
catch (error) {
|
|
1847
|
+
console.error('[FileGrid] Error downloading file:', error);
|
|
1848
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
1849
|
+
this.errorMessage = `Failed to download: ${errorMessage}`;
|
|
1850
|
+
// Clear error after 5 seconds
|
|
1851
|
+
setTimeout(() => {
|
|
1852
|
+
if (this.errorMessage?.startsWith('Failed to download')) {
|
|
1853
|
+
this.errorMessage = null;
|
|
1854
|
+
}
|
|
1855
|
+
}, 5000);
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
/**
|
|
1859
|
+
* Opens the copy dialog for the selected item
|
|
1860
|
+
*/
|
|
1861
|
+
onCopyClick() {
|
|
1862
|
+
if (this.selectedItems.length !== 1) {
|
|
1863
|
+
return;
|
|
1864
|
+
}
|
|
1865
|
+
const selectedKey = this.selectedItems[0];
|
|
1866
|
+
const item = this.items.find(i => i.key === selectedKey);
|
|
1867
|
+
if (!item) {
|
|
1868
|
+
console.error('[FileGrid] Could not find selected item with key:', selectedKey);
|
|
1869
|
+
return;
|
|
1870
|
+
}
|
|
1871
|
+
this.itemToCopy = item;
|
|
1872
|
+
this.copyDestinationPath = this.folderPath + item.name + '-copy';
|
|
1873
|
+
this.showCopyDialog = true;
|
|
1874
|
+
}
|
|
1875
|
+
/**
|
|
1876
|
+
* Cancels the copy operation
|
|
1877
|
+
*/
|
|
1878
|
+
onCancelCopy() {
|
|
1879
|
+
this.showCopyDialog = false;
|
|
1880
|
+
this.itemToCopy = null;
|
|
1881
|
+
this.copyDestinationPath = '';
|
|
1882
|
+
}
|
|
1883
|
+
/**
|
|
1884
|
+
* Confirms and executes the copy operation
|
|
1885
|
+
*/
|
|
1886
|
+
async onConfirmCopy() {
|
|
1887
|
+
if (!this.account || !this.itemToCopy || !this.copyDestinationPath.trim()) {
|
|
1888
|
+
return;
|
|
1889
|
+
}
|
|
1890
|
+
this.isCopying = true;
|
|
1891
|
+
try {
|
|
1892
|
+
const sourcePath = this.constructItemPath(this.itemToCopy);
|
|
1893
|
+
const success = await this.storageClient.CopyObject(this.account.account.ID, sourcePath, this.copyDestinationPath.trim());
|
|
1894
|
+
if (success) {
|
|
1895
|
+
// Close dialog
|
|
1896
|
+
this.showCopyDialog = false;
|
|
1897
|
+
this.itemToCopy = null;
|
|
1898
|
+
this.copyDestinationPath = '';
|
|
1899
|
+
// Clear selection
|
|
1900
|
+
this.selectedItems = [];
|
|
1901
|
+
// Refresh the file grid
|
|
1902
|
+
this.loadItems();
|
|
1903
|
+
}
|
|
1904
|
+
else {
|
|
1905
|
+
this.errorMessage = 'Failed to copy item';
|
|
1906
|
+
}
|
|
1907
|
+
}
|
|
1908
|
+
catch (error) {
|
|
1909
|
+
console.error('[FileGrid] Error copying item:', error);
|
|
1910
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
1911
|
+
this.errorMessage = `Failed to copy: ${errorMessage}`;
|
|
1912
|
+
setTimeout(() => {
|
|
1913
|
+
if (this.errorMessage?.startsWith('Failed to copy')) {
|
|
1914
|
+
this.errorMessage = null;
|
|
1915
|
+
}
|
|
1916
|
+
}, 5000);
|
|
1917
|
+
}
|
|
1918
|
+
finally {
|
|
1919
|
+
this.isCopying = false;
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1922
|
+
/**
|
|
1923
|
+
* Opens the move dialog for the selected item
|
|
1924
|
+
*/
|
|
1925
|
+
onMoveClick() {
|
|
1926
|
+
if (this.selectedItems.length !== 1) {
|
|
1927
|
+
return;
|
|
1928
|
+
}
|
|
1929
|
+
const selectedKey = this.selectedItems[0];
|
|
1930
|
+
const item = this.items.find(i => i.key === selectedKey);
|
|
1931
|
+
if (!item) {
|
|
1932
|
+
console.error('[FileGrid] Could not find selected item with key:', selectedKey);
|
|
1933
|
+
return;
|
|
1934
|
+
}
|
|
1935
|
+
this.itemToMove = item;
|
|
1936
|
+
// Suggest a different folder path
|
|
1937
|
+
this.moveDestinationPath = this.folderPath;
|
|
1938
|
+
this.showMoveDialog = true;
|
|
1939
|
+
}
|
|
1940
|
+
/**
|
|
1941
|
+
* Cancels the move operation
|
|
1942
|
+
*/
|
|
1943
|
+
onCancelMove() {
|
|
1944
|
+
this.showMoveDialog = false;
|
|
1945
|
+
this.itemToMove = null;
|
|
1946
|
+
this.moveDestinationPath = '';
|
|
1947
|
+
}
|
|
1948
|
+
/**
|
|
1949
|
+
* Confirms and executes the move operation
|
|
1950
|
+
*/
|
|
1951
|
+
async onConfirmMove() {
|
|
1952
|
+
if (!this.account || !this.itemToMove || !this.moveDestinationPath.trim()) {
|
|
1953
|
+
return;
|
|
1954
|
+
}
|
|
1955
|
+
this.isMoving = true;
|
|
1956
|
+
try {
|
|
1957
|
+
const sourcePath = this.constructItemPath(this.itemToMove);
|
|
1958
|
+
const destPath = this.moveDestinationPath.trim().endsWith('/')
|
|
1959
|
+
? this.moveDestinationPath.trim() + this.itemToMove.name
|
|
1960
|
+
: this.moveDestinationPath.trim();
|
|
1961
|
+
const success = await this.storageClient.MoveObject(this.account.account.ID, sourcePath, destPath);
|
|
1962
|
+
if (success) {
|
|
1963
|
+
const wasFolder = this.itemToMove.type === 'folder';
|
|
1964
|
+
// Close dialog
|
|
1965
|
+
this.showMoveDialog = false;
|
|
1966
|
+
this.itemToMove = null;
|
|
1967
|
+
this.moveDestinationPath = '';
|
|
1968
|
+
// Clear selection
|
|
1969
|
+
this.selectedItems = [];
|
|
1970
|
+
// If we moved a folder, notify parent that folder structure changed
|
|
1971
|
+
if (wasFolder) {
|
|
1972
|
+
this.folderStructureChanged.emit();
|
|
1973
|
+
}
|
|
1974
|
+
// Refresh the file grid
|
|
1975
|
+
this.loadItems();
|
|
1976
|
+
}
|
|
1977
|
+
else {
|
|
1978
|
+
this.errorMessage = 'Failed to move item';
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
catch (error) {
|
|
1982
|
+
console.error('[FileGrid] Error moving item:', error);
|
|
1983
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
1984
|
+
this.errorMessage = `Failed to move: ${errorMessage}`;
|
|
1985
|
+
setTimeout(() => {
|
|
1986
|
+
if (this.errorMessage?.startsWith('Failed to move')) {
|
|
1987
|
+
this.errorMessage = null;
|
|
1988
|
+
}
|
|
1989
|
+
}, 5000);
|
|
1990
|
+
}
|
|
1991
|
+
finally {
|
|
1992
|
+
this.isMoving = false;
|
|
1993
|
+
}
|
|
1994
|
+
}
|
|
1995
|
+
/**
|
|
1996
|
+
* Constructs the full path for an item
|
|
1997
|
+
*/
|
|
1998
|
+
constructItemPath(item) {
|
|
1999
|
+
// Validate that we have either key or name
|
|
2000
|
+
if (!item.key && !item.name) {
|
|
2001
|
+
console.error('[FileGrid] Cannot construct path - item has no key or name:', item);
|
|
2002
|
+
throw new Error('Cannot construct path for item without key or name');
|
|
2003
|
+
}
|
|
2004
|
+
// If item.key exists and is at root level, use it directly
|
|
2005
|
+
if (!this.folderPath || this.folderPath === '/') {
|
|
2006
|
+
return item.key || item.name;
|
|
2007
|
+
}
|
|
2008
|
+
// If the item key already includes the folder path, use it as is
|
|
2009
|
+
if (item.key && item.key.startsWith(this.folderPath)) {
|
|
2010
|
+
return item.key;
|
|
2011
|
+
}
|
|
2012
|
+
// Otherwise, combine folder path with item name
|
|
2013
|
+
const normalizedFolderPath = this.folderPath.endsWith('/')
|
|
2014
|
+
? this.folderPath.slice(0, -1)
|
|
2015
|
+
: this.folderPath;
|
|
2016
|
+
// Make sure we have a name to use
|
|
2017
|
+
const itemName = item.name || item.key;
|
|
2018
|
+
if (!itemName) {
|
|
2019
|
+
console.error('[FileGrid] Cannot construct path - item has no usable name:', item);
|
|
2020
|
+
throw new Error('Cannot construct path for item without name');
|
|
2021
|
+
}
|
|
2022
|
+
return `${normalizedFolderPath}/${itemName}`;
|
|
2023
|
+
}
|
|
2024
|
+
/**
|
|
2025
|
+
* Gets the currently selected item (if exactly one is selected)
|
|
2026
|
+
*/
|
|
2027
|
+
getSelectedItem() {
|
|
2028
|
+
if (this.selectedItems.length !== 1) {
|
|
2029
|
+
return null;
|
|
2030
|
+
}
|
|
2031
|
+
return this.items.find(item => item.key === this.selectedItems[0]) || null;
|
|
2032
|
+
}
|
|
2033
|
+
/**
|
|
2034
|
+
* Opens the copy to account dialog
|
|
2035
|
+
*/
|
|
2036
|
+
async onCopyToAccountClick() {
|
|
2037
|
+
const item = this.getSelectedItem();
|
|
2038
|
+
if (!item || item.type === 'folder') {
|
|
2039
|
+
return;
|
|
2040
|
+
}
|
|
2041
|
+
try {
|
|
2042
|
+
const engine = FileStorageEngine.Instance;
|
|
2043
|
+
await engine.Config(false); // Use cached data if available
|
|
2044
|
+
// Build available accounts (excluding current account)
|
|
2045
|
+
this.availableAccounts = engine.AccountsWithProviders
|
|
2046
|
+
.filter(a => a.account.ID !== this.account?.account.ID);
|
|
2047
|
+
if (this.availableAccounts.length === 0) {
|
|
2048
|
+
this.errorMessage = 'No other storage accounts available';
|
|
2049
|
+
setTimeout(() => {
|
|
2050
|
+
if (this.errorMessage?.includes('No other storage accounts')) {
|
|
2051
|
+
this.errorMessage = null;
|
|
2052
|
+
}
|
|
2053
|
+
}, 3000);
|
|
2054
|
+
return;
|
|
2055
|
+
}
|
|
2056
|
+
this.itemToCopyToProvider = item;
|
|
2057
|
+
this.copyToAccountDestinationPath = item.name; // Default to same filename
|
|
2058
|
+
this.selectedDestinationAccounts.clear();
|
|
2059
|
+
this.copyToAccountProgress = null;
|
|
2060
|
+
this.showCopyToProviderDialog = true;
|
|
2061
|
+
}
|
|
2062
|
+
catch (error) {
|
|
2063
|
+
console.error('[FileGrid] Error loading accounts:', error);
|
|
2064
|
+
this.errorMessage = 'Failed to load storage accounts';
|
|
2065
|
+
}
|
|
2066
|
+
}
|
|
2067
|
+
/**
|
|
2068
|
+
* Cancels the copy to account dialog
|
|
2069
|
+
*/
|
|
2070
|
+
onCancelCopyToAccount() {
|
|
2071
|
+
this.showCopyToProviderDialog = false;
|
|
2072
|
+
this.itemToCopyToProvider = null;
|
|
2073
|
+
this.selectedDestinationAccounts.clear();
|
|
2074
|
+
this.copyToAccountDestinationPath = '';
|
|
2075
|
+
this.copyToAccountProgress = null;
|
|
2076
|
+
}
|
|
2077
|
+
/**
|
|
2078
|
+
* Toggles selection of a destination account for copying
|
|
2079
|
+
*/
|
|
2080
|
+
toggleDestinationAccount(accountId) {
|
|
2081
|
+
if (this.selectedDestinationAccounts.has(accountId)) {
|
|
2082
|
+
this.selectedDestinationAccounts.delete(accountId);
|
|
2083
|
+
}
|
|
2084
|
+
else {
|
|
2085
|
+
this.selectedDestinationAccounts.add(accountId);
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
/**
|
|
2089
|
+
* Checks if an account is selected as a destination
|
|
2090
|
+
*/
|
|
2091
|
+
isDestinationAccountSelected(accountId) {
|
|
2092
|
+
return this.selectedDestinationAccounts.has(accountId);
|
|
2093
|
+
}
|
|
2094
|
+
/**
|
|
2095
|
+
* Executes the cross-account copy to multiple selected accounts
|
|
2096
|
+
*/
|
|
2097
|
+
async onConfirmCopyToAccount() {
|
|
2098
|
+
if (!this.itemToCopyToProvider || this.selectedDestinationAccounts.size === 0 || !this.account) {
|
|
2099
|
+
return;
|
|
2100
|
+
}
|
|
2101
|
+
this.isCopyingToAccount = true;
|
|
2102
|
+
// Get selected accounts
|
|
2103
|
+
const selectedAccounts = this.availableAccounts.filter(a => this.selectedDestinationAccounts.has(a.account.ID));
|
|
2104
|
+
const successfulCopies = [];
|
|
2105
|
+
const failedCopies = [];
|
|
2106
|
+
try {
|
|
2107
|
+
// Construct source path and normalize it
|
|
2108
|
+
const rawSourcePath = this.constructItemPath(this.itemToCopyToProvider);
|
|
2109
|
+
const sourcePath = rawSourcePath.replace(/^\/+|\/+$/g, '').replace(/\/+/g, '/');
|
|
2110
|
+
console.log('[FileGrid] Cross-account copy sourcePath:', { raw: rawSourcePath, normalized: sourcePath });
|
|
2111
|
+
// Copy to each selected account
|
|
2112
|
+
for (let i = 0; i < selectedAccounts.length; i++) {
|
|
2113
|
+
const destAccount = selectedAccounts[i];
|
|
2114
|
+
this.copyToAccountProgress = {
|
|
2115
|
+
current: i + 1,
|
|
2116
|
+
total: selectedAccounts.length,
|
|
2117
|
+
currentAccount: destAccount.account.Name
|
|
2118
|
+
};
|
|
2119
|
+
try {
|
|
2120
|
+
console.log('[FileGrid] Copying to account:', destAccount.account.Name);
|
|
2121
|
+
const copyResult = await this.storageClient.CopyObjectBetweenAccounts(this.account.account.ID, destAccount.account.ID, sourcePath, this.copyToAccountDestinationPath);
|
|
2122
|
+
if (copyResult.success) {
|
|
2123
|
+
successfulCopies.push(destAccount.account.Name);
|
|
2124
|
+
}
|
|
2125
|
+
else {
|
|
2126
|
+
failedCopies.push({ account: destAccount.account.Name, error: copyResult.message });
|
|
2127
|
+
}
|
|
2128
|
+
}
|
|
2129
|
+
catch (error) {
|
|
2130
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
2131
|
+
failedCopies.push({ account: destAccount.account.Name, error: errorMessage });
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
2134
|
+
// Show results
|
|
2135
|
+
if (failedCopies.length === 0) {
|
|
2136
|
+
this.errorMessage = `Successfully copied to ${successfulCopies.length} account${successfulCopies.length > 1 ? 's' : ''}: ${successfulCopies.join(', ')}`;
|
|
2137
|
+
}
|
|
2138
|
+
else if (successfulCopies.length === 0) {
|
|
2139
|
+
this.errorMessage = `Copy failed for all accounts: ${failedCopies.map(f => `${f.account} (${f.error})`).join(', ')}`;
|
|
2140
|
+
}
|
|
2141
|
+
else {
|
|
2142
|
+
this.errorMessage = `Copied to ${successfulCopies.length} account(s). Failed: ${failedCopies.map(f => f.account).join(', ')}`;
|
|
2143
|
+
}
|
|
2144
|
+
setTimeout(() => {
|
|
2145
|
+
if (this.errorMessage?.includes('copied') || this.errorMessage?.includes('Copy failed')) {
|
|
2146
|
+
this.errorMessage = null;
|
|
2147
|
+
}
|
|
2148
|
+
}, 5000);
|
|
2149
|
+
// Close dialog
|
|
2150
|
+
this.showCopyToProviderDialog = false;
|
|
2151
|
+
this.itemToCopyToProvider = null;
|
|
2152
|
+
this.selectedDestinationAccounts.clear();
|
|
2153
|
+
this.copyToAccountDestinationPath = '';
|
|
2154
|
+
this.copyToAccountProgress = null;
|
|
2155
|
+
}
|
|
2156
|
+
catch (error) {
|
|
2157
|
+
console.error('[FileGrid] Error copying to accounts:', error);
|
|
2158
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
2159
|
+
this.errorMessage = `Copy failed: ${errorMessage}`;
|
|
2160
|
+
setTimeout(() => {
|
|
2161
|
+
if (this.errorMessage?.startsWith('Copy failed')) {
|
|
2162
|
+
this.errorMessage = null;
|
|
2163
|
+
}
|
|
2164
|
+
}, 5000);
|
|
2165
|
+
}
|
|
2166
|
+
finally {
|
|
2167
|
+
this.isCopyingToAccount = false;
|
|
2168
|
+
this.copyToAccountProgress = null;
|
|
2169
|
+
}
|
|
2170
|
+
}
|
|
2171
|
+
// ==========================================
|
|
2172
|
+
// Multi-Account Search Methods
|
|
2173
|
+
// ==========================================
|
|
2174
|
+
/**
|
|
2175
|
+
* Toggles multi-account search mode
|
|
2176
|
+
*/
|
|
2177
|
+
toggleMultiAccountSearchMode() {
|
|
2178
|
+
this.isMultiProviderSearchMode = !this.isMultiProviderSearchMode;
|
|
2179
|
+
if (this.isMultiProviderSearchMode) {
|
|
2180
|
+
// Load available accounts if not already loaded
|
|
2181
|
+
if (this.availableAccounts.length === 0) {
|
|
2182
|
+
this.loadAvailableAccountsForSearch();
|
|
2183
|
+
}
|
|
2184
|
+
// Pre-select current account if available
|
|
2185
|
+
if (this.account) {
|
|
2186
|
+
this.selectedSearchProviders.add(this.account.account.ID);
|
|
2187
|
+
}
|
|
2188
|
+
}
|
|
2189
|
+
else {
|
|
2190
|
+
// Clear search results when exiting search mode
|
|
2191
|
+
this.multiProviderSearchResults = null;
|
|
2192
|
+
this.multiProviderSearchQuery = '';
|
|
2193
|
+
}
|
|
2194
|
+
}
|
|
2195
|
+
/**
|
|
2196
|
+
* Loads available accounts for search selection
|
|
2197
|
+
*/
|
|
2198
|
+
async loadAvailableAccountsForSearch() {
|
|
2199
|
+
try {
|
|
2200
|
+
const engine = FileStorageEngine.Instance;
|
|
2201
|
+
await engine.Config(false); // Use cached data if available
|
|
2202
|
+
this.availableAccounts = engine.AccountsWithProviders;
|
|
2203
|
+
}
|
|
2204
|
+
catch (error) {
|
|
2205
|
+
console.error('[FileGrid] Error loading accounts for search:', error);
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
/**
|
|
2209
|
+
* Toggles account selection for search
|
|
2210
|
+
*/
|
|
2211
|
+
toggleSearchAccount(accountID) {
|
|
2212
|
+
if (this.selectedSearchProviders.has(accountID)) {
|
|
2213
|
+
this.selectedSearchProviders.delete(accountID);
|
|
2214
|
+
}
|
|
2215
|
+
else {
|
|
2216
|
+
this.selectedSearchProviders.add(accountID);
|
|
2217
|
+
}
|
|
2218
|
+
}
|
|
2219
|
+
/**
|
|
2220
|
+
* Checks if an account is selected for search
|
|
2221
|
+
*/
|
|
2222
|
+
isAccountSelectedForSearch(accountID) {
|
|
2223
|
+
return this.selectedSearchProviders.has(accountID);
|
|
2224
|
+
}
|
|
2225
|
+
/**
|
|
2226
|
+
* Checks if an account's provider supports search
|
|
2227
|
+
*/
|
|
2228
|
+
accountSupportsSearch(accountWithProvider) {
|
|
2229
|
+
return accountWithProvider.provider.SupportsSearch === true;
|
|
2230
|
+
}
|
|
2231
|
+
/**
|
|
2232
|
+
* Executes multi-account search
|
|
2233
|
+
*/
|
|
2234
|
+
async executeMultiAccountSearch() {
|
|
2235
|
+
if (!this.multiProviderSearchQuery.trim() || this.selectedSearchProviders.size === 0) {
|
|
2236
|
+
return;
|
|
2237
|
+
}
|
|
2238
|
+
this.isSearching = true;
|
|
2239
|
+
this.multiProviderSearchResults = null;
|
|
2240
|
+
try {
|
|
2241
|
+
console.log('[FileGrid] Executing multi-account search:', {
|
|
2242
|
+
accountIds: Array.from(this.selectedSearchProviders),
|
|
2243
|
+
query: this.multiProviderSearchQuery
|
|
2244
|
+
});
|
|
2245
|
+
const searchResult = await this.storageClient.SearchFiles(Array.from(this.selectedSearchProviders), this.multiProviderSearchQuery, { maxResultsPerAccount: 50 });
|
|
2246
|
+
console.log('[FileGrid] Multi-account search result:', searchResult);
|
|
2247
|
+
// Map the client result to the component's expected format
|
|
2248
|
+
this.multiProviderSearchResults = {
|
|
2249
|
+
accountResults: searchResult.accountResults.map((ar) => ({
|
|
2250
|
+
accountID: ar.accountId,
|
|
2251
|
+
accountName: ar.accountName,
|
|
2252
|
+
success: ar.success,
|
|
2253
|
+
errorMessage: ar.errorMessage,
|
|
2254
|
+
results: ar.results.map((r) => ({
|
|
2255
|
+
path: r.path,
|
|
2256
|
+
name: r.name,
|
|
2257
|
+
size: r.size,
|
|
2258
|
+
contentType: r.contentType,
|
|
2259
|
+
lastModified: r.lastModified.toISOString(),
|
|
2260
|
+
relevance: r.relevance,
|
|
2261
|
+
excerpt: r.excerpt,
|
|
2262
|
+
matchInFilename: r.matchInFilename,
|
|
2263
|
+
objectId: r.objectId
|
|
2264
|
+
})),
|
|
2265
|
+
totalMatches: ar.totalMatches,
|
|
2266
|
+
hasMore: ar.hasMore,
|
|
2267
|
+
nextPageToken: ar.nextPageToken
|
|
2268
|
+
})),
|
|
2269
|
+
totalResultsReturned: searchResult.totalResultsReturned,
|
|
2270
|
+
successfulAccounts: searchResult.successfulAccounts,
|
|
2271
|
+
failedAccounts: searchResult.failedAccounts
|
|
2272
|
+
};
|
|
2273
|
+
}
|
|
2274
|
+
catch (error) {
|
|
2275
|
+
console.error('[FileGrid] Error executing multi-provider search:', error);
|
|
2276
|
+
this.errorMessage = 'Search failed. Please try again.';
|
|
2277
|
+
setTimeout(() => {
|
|
2278
|
+
if (this.errorMessage === 'Search failed. Please try again.') {
|
|
2279
|
+
this.errorMessage = null;
|
|
2280
|
+
}
|
|
2281
|
+
}, 5000);
|
|
2282
|
+
}
|
|
2283
|
+
finally {
|
|
2284
|
+
this.isSearching = false;
|
|
2285
|
+
}
|
|
2286
|
+
}
|
|
2287
|
+
/**
|
|
2288
|
+
* Clears multi-provider search results
|
|
2289
|
+
*/
|
|
2290
|
+
clearMultiProviderSearch() {
|
|
2291
|
+
this.multiProviderSearchQuery = '';
|
|
2292
|
+
this.multiProviderSearchResults = null;
|
|
2293
|
+
}
|
|
2294
|
+
/**
|
|
2295
|
+
* Gets icon for a search result based on content type
|
|
2296
|
+
*/
|
|
2297
|
+
getSearchResultIcon(result) {
|
|
2298
|
+
const extension = result.name.split('.').pop()?.toLowerCase() || '';
|
|
2299
|
+
if (['jpg', 'jpeg', 'png', 'gif', 'bmp', 'svg'].includes(extension)) {
|
|
2300
|
+
return 'fa-solid fa-file-image';
|
|
2301
|
+
}
|
|
2302
|
+
else if (['pdf'].includes(extension)) {
|
|
2303
|
+
return 'fa-solid fa-file-pdf';
|
|
2304
|
+
}
|
|
2305
|
+
else if (['doc', 'docx'].includes(extension)) {
|
|
2306
|
+
return 'fa-solid fa-file-word';
|
|
2307
|
+
}
|
|
2308
|
+
else if (['xls', 'xlsx'].includes(extension)) {
|
|
2309
|
+
return 'fa-solid fa-file-excel';
|
|
2310
|
+
}
|
|
2311
|
+
else if (['ppt', 'pptx'].includes(extension)) {
|
|
2312
|
+
return 'fa-solid fa-file-powerpoint';
|
|
2313
|
+
}
|
|
2314
|
+
else if (['zip', 'rar', '7z', 'tar', 'gz'].includes(extension)) {
|
|
2315
|
+
return 'fa-solid fa-file-zipper';
|
|
2316
|
+
}
|
|
2317
|
+
else if (['mp4', 'avi', 'mov', 'wmv'].includes(extension)) {
|
|
2318
|
+
return 'fa-solid fa-file-video';
|
|
2319
|
+
}
|
|
2320
|
+
else if (['mp3', 'wav', 'ogg', 'flac'].includes(extension)) {
|
|
2321
|
+
return 'fa-solid fa-file-audio';
|
|
2322
|
+
}
|
|
2323
|
+
else {
|
|
2324
|
+
return 'fa-solid fa-file';
|
|
2325
|
+
}
|
|
2326
|
+
}
|
|
2327
|
+
/**
|
|
2328
|
+
* Formats file size for search results
|
|
2329
|
+
*/
|
|
2330
|
+
formatSearchResultSize(bytes) {
|
|
2331
|
+
return this.formatFileSize(bytes);
|
|
2332
|
+
}
|
|
2333
|
+
/**
|
|
2334
|
+
* Formats date for search results
|
|
2335
|
+
*/
|
|
2336
|
+
formatSearchResultDate(dateStr) {
|
|
2337
|
+
return new Date(dateStr).toLocaleString();
|
|
2338
|
+
}
|
|
2339
|
+
static ɵfac = function FileGridComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || FileGridComponent)(); };
|
|
2340
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: FileGridComponent, selectors: [["mj-file-grid"]], inputs: { account: "account", folderPath: "folderPath" }, outputs: { folderNavigate: "folderNavigate", folderStructureChanged: "folderStructureChanged" }, features: [i0.ɵɵNgOnChangesFeature], decls: 17, vars: 16, consts: [[1, "file-grid-container", 3, "dragenter", "dragover", "dragleave", "drop"], ["class", "breadcrumb-bar", 4, "ngIf"], ["class", "multi-provider-search-panel", 4, "ngIf"], ["class", "loading-state", 4, "ngIf"], ["class", "error-state", 4, "ngIf"], ["class", "file-table-wrapper", 4, "ngIf"], ["class", "file-grid-wrapper", 4, "ngIf"], ["class", "empty-state", 4, "ngIf"], ["class", "bottom-toolbar", 4, "ngIf"], ["class", "drag-overlay", 4, "ngIf"], ["class", "upload-progress", 4, "ngIf"], ["class", "modal-overlay", 3, "click", 4, "ngIf"], [1, "breadcrumb-bar"], [1, "nav-buttons"], ["title", "Go up to parent folder", 1, "nav-btn", 3, "click", "disabled"], [1, "fa-solid", "fa-arrow-up"], ["title", "Refresh", 1, "nav-btn", 3, "click"], [1, "fa-solid", "fa-arrows-rotate"], [1, "breadcrumb-path"], [1, "fa-solid", "fa-folder"], [1, "path-text"], [1, "search-bar"], ["type", "text", "placeholder", "Search files and folders...", 1, "search-input", 3, "ngModelChange", "ngModel", "disabled"], ["class", "search-clear-btn", "title", "Clear search", 3, "click", 4, "ngIf"], [1, "file-type-filter", 3, "ngModelChange", "ngModel", "disabled"], ["value", "all"], ["value", "files"], ["value", "folders"], ["title", "Search across all storage providers", 1, "multi-search-btn", 3, "click"], [1, "fa-solid", "fa-magnifying-glass-plus"], ["title", "Clear search", 1, "search-clear-btn", 3, "click"], [1, "fa-solid", "fa-xmark"], [1, "multi-provider-search-panel"], [1, "search-panel-header"], [1, "fa-solid", "fa-magnifying-glass"], [1, "close-panel-btn", 3, "click"], [1, "search-panel-body"], [1, "search-input-row"], ["type", "text", "placeholder", "Enter search term...", 1, "multi-search-input", 3, "ngModelChange", "keydown.enter", "ngModel", "disabled"], [1, "search-execute-btn", 3, "click", "disabled"], ["class", "fa-solid fa-spinner fa-spin", 4, "ngIf"], ["class", "fa-solid fa-search", 4, "ngIf"], [1, "provider-selection"], [1, "provider-selection-label"], [1, "provider-checkboxes"], ["class", "provider-checkbox", 3, "disabled", "title", 4, "ngFor", "ngForOf"], ["class", "search-results-container", 4, "ngIf"], ["class", "searching-state", 4, "ngIf"], [1, "fa-solid", "fa-spinner", "fa-spin"], [1, "fa-solid", "fa-search"], [1, "provider-checkbox", 3, "title"], ["type", "checkbox", 3, "change", "checked", "disabled"], [1, "provider-name"], [1, "provider-type"], ["class", "no-search-badge", 4, "ngIf"], [1, "no-search-badge"], [1, "search-results-container"], [1, "search-results-summary"], [1, "result-count"], [1, "provider-stats"], ["class", "failed-count", 4, "ngIf"], [1, "clear-results-btn", 3, "click"], [1, "provider-results-list"], ["class", "provider-result-group", 3, "failed", 4, "ngFor", "ngForOf"], [1, "failed-count"], [1, "provider-result-group"], [1, "provider-result-header"], [1, "fa-solid", "fa-cloud"], ["class", "result-count", 4, "ngIf"], ["class", "error-badge", 4, "ngIf"], ["class", "provider-result-files", 4, "ngIf"], ["class", "no-results-message", 4, "ngIf"], [1, "error-badge"], [1, "fa-solid", "fa-exclamation-triangle"], [1, "provider-result-files"], ["class", "search-result-item", 4, "ngFor", "ngForOf"], [1, "search-result-item"], [1, "file-icon"], [1, "file-info"], [1, "file-name"], [1, "file-path"], [1, "file-size"], [1, "file-date"], [1, "no-results-message"], [1, "searching-state"], ["text", "Searching across providers..."], [1, "loading-state"], ["text", "Loading files..."], [1, "error-state"], [1, "error-icon"], [1, "fa-solid", "fa-circle-exclamation"], [1, "error-message"], [1, "retry-btn", 3, "click"], [1, "file-table-wrapper"], [1, "file-table"], [1, "col-name", 3, "click"], [1, "fa-solid", "sort-icon"], [1, "col-type"], [1, "col-size", 3, "click"], [1, "col-modified", 3, "click"], ["class", "file-row", 3, "selected", "click", "dblclick", 4, "ngFor", "ngForOf"], [1, "file-row", 3, "click", "dblclick"], [1, "col-name"], [1, "item-icon"], [1, "item-name"], [1, "item-type"], [1, "col-size"], [1, "item-size"], [1, "col-modified"], [1, "item-date"], [1, "file-grid-wrapper"], [1, "file-grid"], ["class", "grid-item", 3, "selected", "click", "dblclick", 4, "ngFor", "ngForOf"], [1, "grid-item", 3, "click", "dblclick"], [1, "grid-icon"], [1, "grid-name"], ["class", "grid-meta", 4, "ngIf"], [1, "grid-meta"], [1, "empty-state"], [1, "empty-icon"], [1, "fa-solid", "fa-folder-open"], [1, "empty-message"], [1, "upload-btn", 3, "click"], [1, "fa-solid", "fa-upload"], [1, "bottom-toolbar"], [1, "toolbar-left"], [1, "toolbar-btn", 3, "click"], [1, "fa-solid", "fa-folder-plus"], [1, "toolbar-btn", 3, "click", "disabled"], [1, "fa-solid", "fa-download"], [1, "fa-solid", "fa-pen"], [1, "fa-solid", "fa-copy"], ["title", "Copy file to another storage account", 1, "toolbar-btn", 3, "click", "disabled"], [1, "fa-solid", "fa-cloud-arrow-up"], [1, "fa-solid", "fa-arrows-up-down-left-right"], [1, "fa-solid", "fa-trash"], [1, "toolbar-right"], [1, "view-toggle"], ["title", "List View", 1, "view-btn", 3, "click"], [1, "fa-solid", "fa-list"], ["title", "Grid View", 1, "view-btn", 3, "click"], [1, "fa-solid", "fa-th"], [1, "item-count"], [1, "drag-overlay"], [1, "drag-content"], [1, "drag-icon"], [1, "drag-title"], [1, "upload-progress"], [1, "progress-content"], [1, "progress-info"], [1, "progress-bar-wrap"], [1, "progress-bar"], [1, "progress-percent"], [1, "modal-overlay", 3, "click"], [1, "modal-dialog", 3, "click"], [1, "modal-header"], [1, "modal-close", 3, "click"], [1, "modal-body"], ["type", "text", "placeholder", "Enter folder name", 3, "ngModelChange", "keydown.enter", "ngModel", "disabled"], [1, "modal-footer"], [3, "click", "disabled"], ["class", "warning", 4, "ngIf"], [1, "danger", 3, "click", "disabled"], [1, "warning"], ["type", "text", "placeholder", "Enter new name", 3, "ngModelChange", "keydown.enter", "ngModel", "disabled"], ["type", "text", "placeholder", "Enter destination path", 3, "ngModelChange", "keydown.enter", "ngModel", "disabled"], ["type", "text", "placeholder", "Enter destination folder (e.g., folder1/folder2/)", 3, "ngModelChange", "keydown.enter", "ngModel", "disabled"], [2, "font-size", "11px", "color", "#666", "margin-top", "8px"], [2, "font-size", "12px", "color", "#666", "margin-bottom", "16px"], [1, "account-checkbox-list"], ["class", "account-checkbox-item", 4, "ngFor", "ngForOf"], [2, "margin-top", "16px"], ["type", "text", "placeholder", "Enter destination filename", 3, "ngModelChange", "keydown.enter", "ngModel", "disabled"], ["class", "copy-progress", 4, "ngIf"], [1, "account-checkbox-item"], ["type", "checkbox", 3, "change", "id", "checked", "disabled"], [3, "for"], [1, "provider-badge"], [1, "copy-progress"]], template: function FileGridComponent_Template(rf, ctx) { if (rf & 1) {
|
|
2341
|
+
i0.ɵɵelementStart(0, "div", 0);
|
|
2342
|
+
i0.ɵɵlistener("dragenter", function FileGridComponent_Template_div_dragenter_0_listener($event) { return ctx.onDragEnter($event); })("dragover", function FileGridComponent_Template_div_dragover_0_listener($event) { return ctx.onDragOver($event); })("dragleave", function FileGridComponent_Template_div_dragleave_0_listener($event) { return ctx.onDragLeave($event); })("drop", function FileGridComponent_Template_div_drop_0_listener($event) { return ctx.onDrop($event); });
|
|
2343
|
+
i0.ɵɵtemplate(1, FileGridComponent_div_1_Template, 22, 9, "div", 1)(2, FileGridComponent_div_2_Template, 21, 9, "div", 2)(3, FileGridComponent_div_3_Template, 2, 0, "div", 3)(4, FileGridComponent_div_4_Template, 8, 1, "div", 4)(5, FileGridComponent_div_5_Template, 20, 19, "div", 5)(6, FileGridComponent_div_6_Template, 3, 1, "div", 6)(7, FileGridComponent_div_7_Template, 8, 0, "div", 7)(8, FileGridComponent_div_8_Template, 34, 12, "div", 8)(9, FileGridComponent_div_9_Template, 6, 0, "div", 9)(10, FileGridComponent_div_10_Template, 10, 4, "div", 10);
|
|
2344
|
+
i0.ɵɵelementEnd();
|
|
2345
|
+
i0.ɵɵtemplate(11, FileGridComponent_div_11_Template, 17, 6, "div", 11)(12, FileGridComponent_div_12_Template, 20, 7, "div", 11)(13, FileGridComponent_div_13_Template, 17, 7, "div", 11)(14, FileGridComponent_div_14_Template, 21, 8, "div", 11)(15, FileGridComponent_div_15_Template, 23, 10, "div", 11)(16, FileGridComponent_div_16_Template, 30, 10, "div", 11);
|
|
2346
|
+
} if (rf & 2) {
|
|
2347
|
+
i0.ɵɵadvance();
|
|
2348
|
+
i0.ɵɵproperty("ngIf", !ctx.isLoading && !ctx.errorMessage);
|
|
2349
|
+
i0.ɵɵadvance();
|
|
2350
|
+
i0.ɵɵproperty("ngIf", ctx.isMultiProviderSearchMode);
|
|
2351
|
+
i0.ɵɵadvance();
|
|
2352
|
+
i0.ɵɵproperty("ngIf", ctx.isLoading && !ctx.isMultiProviderSearchMode);
|
|
2353
|
+
i0.ɵɵadvance();
|
|
2354
|
+
i0.ɵɵproperty("ngIf", ctx.errorMessage && !ctx.isLoading);
|
|
2355
|
+
i0.ɵɵadvance();
|
|
2356
|
+
i0.ɵɵproperty("ngIf", !ctx.isLoading && !ctx.errorMessage && ctx.filteredItems.length > 0 && ctx.viewMode === "list");
|
|
2357
|
+
i0.ɵɵadvance();
|
|
2358
|
+
i0.ɵɵproperty("ngIf", !ctx.isLoading && !ctx.errorMessage && ctx.filteredItems.length > 0 && ctx.viewMode === "grid");
|
|
2359
|
+
i0.ɵɵadvance();
|
|
2360
|
+
i0.ɵɵproperty("ngIf", !ctx.isLoading && !ctx.errorMessage && ctx.filteredItems.length === 0);
|
|
2361
|
+
i0.ɵɵadvance();
|
|
2362
|
+
i0.ɵɵproperty("ngIf", !ctx.isLoading && !ctx.errorMessage);
|
|
2363
|
+
i0.ɵɵadvance();
|
|
2364
|
+
i0.ɵɵproperty("ngIf", ctx.isDragging);
|
|
2365
|
+
i0.ɵɵadvance();
|
|
2366
|
+
i0.ɵɵproperty("ngIf", ctx.isUploading);
|
|
2367
|
+
i0.ɵɵadvance();
|
|
2368
|
+
i0.ɵɵproperty("ngIf", ctx.showNewFolderDialog);
|
|
2369
|
+
i0.ɵɵadvance();
|
|
2370
|
+
i0.ɵɵproperty("ngIf", ctx.showDeleteDialog && ctx.itemToDelete);
|
|
2371
|
+
i0.ɵɵadvance();
|
|
2372
|
+
i0.ɵɵproperty("ngIf", ctx.showRenameDialog && ctx.itemToRename);
|
|
2373
|
+
i0.ɵɵadvance();
|
|
2374
|
+
i0.ɵɵproperty("ngIf", ctx.showCopyDialog && ctx.itemToCopy);
|
|
2375
|
+
i0.ɵɵadvance();
|
|
2376
|
+
i0.ɵɵproperty("ngIf", ctx.showMoveDialog && ctx.itemToMove);
|
|
2377
|
+
i0.ɵɵadvance();
|
|
2378
|
+
i0.ɵɵproperty("ngIf", ctx.showCopyToProviderDialog && ctx.itemToCopyToProvider);
|
|
2379
|
+
} }, dependencies: [i1.NgForOf, i1.NgIf, i2.NgSelectOption, i2.ɵNgSelectMultipleOption, i2.DefaultValueAccessor, i2.SelectControlValueAccessor, i2.NgControlStatus, i2.NgModel, i3.LoadingComponent, i4.LabelDirective], styles: ["\n\n\n\n\n[_nghost-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n width: 100%;\n background-color: #fff;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n}\n\n.file-grid-container[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n width: 100%;\n position: relative;\n}\n\n\n\n\n\n\n.breadcrumb-bar[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n padding: 6px 10px;\n background-color: #f8f8f8;\n border-bottom: 1px solid #ccc;\n gap: 10px;\n flex-wrap: wrap;\n}\n\n.nav-buttons[_ngcontent-%COMP%] {\n display: flex;\n gap: 4px;\n}\n\n.nav-btn[_ngcontent-%COMP%] {\n background: #fff;\n border: 1px solid #ccc;\n padding: 4px 8px;\n cursor: pointer;\n border-radius: 3px;\n font-size: 12px;\n color: #333;\n transition: background-color 0.15s;\n}\n\n.nav-btn[_ngcontent-%COMP%]:hover:not(:disabled) {\n background-color: #e8e8e8;\n}\n\n.nav-btn[_ngcontent-%COMP%]:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.breadcrumb-path[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n color: #333;\n}\n\n.breadcrumb-path[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: #666;\n}\n\n.path-text[_ngcontent-%COMP%] {\n font-weight: 500;\n}\n\n.search-bar[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-left: auto;\n position: relative;\n}\n\n.search-input[_ngcontent-%COMP%] {\n padding: 4px 30px 4px 8px;\n border: 1px solid #ccc;\n border-radius: 3px;\n font-size: 12px;\n font-family: inherit;\n min-width: 200px;\n}\n\n.search-input[_ngcontent-%COMP%]:focus {\n outline: none;\n border-color: #007AFF;\n}\n\n.search-clear-btn[_ngcontent-%COMP%] {\n position: absolute;\n right: 142px;\n background: none;\n border: none;\n cursor: pointer;\n color: #999;\n padding: 4px;\n font-size: 12px;\n}\n\n.search-clear-btn[_ngcontent-%COMP%]:hover {\n color: #333;\n}\n\n.file-type-filter[_ngcontent-%COMP%] {\n padding: 4px 8px;\n border: 1px solid #ccc;\n border-radius: 3px;\n font-size: 12px;\n font-family: inherit;\n background: #fff;\n cursor: pointer;\n}\n\n.file-type-filter[_ngcontent-%COMP%]:focus {\n outline: none;\n border-color: #007AFF;\n}\n\n\n\n\n\n\n.file-table-wrapper[_ngcontent-%COMP%] {\n flex: 1;\n overflow: auto;\n border-bottom: 1px solid #ccc;\n}\n\n.file-table[_ngcontent-%COMP%] {\n width: 100%;\n border-collapse: collapse;\n font-size: 12px;\n}\n\n.file-table[_ngcontent-%COMP%] thead[_ngcontent-%COMP%] {\n position: sticky;\n top: 0;\n background-color: #f0f0f0;\n z-index: 10;\n}\n\n.file-table[_ngcontent-%COMP%] th[_ngcontent-%COMP%] {\n text-align: left;\n padding: 6px 10px;\n border-bottom: 1px solid #ccc;\n font-weight: 600;\n color: #333;\n cursor: pointer;\n user-select: none;\n}\n\n.file-table[_ngcontent-%COMP%] th[_ngcontent-%COMP%]:hover {\n background-color: #e8e8e8;\n}\n\n.file-table[_ngcontent-%COMP%] th[_ngcontent-%COMP%] span[_ngcontent-%COMP%] {\n margin-right: 4px;\n}\n\n.sort-icon[_ngcontent-%COMP%] {\n font-size: 10px;\n color: #666;\n}\n\n.file-table[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] tr[_ngcontent-%COMP%] {\n border-bottom: 1px solid #e8e8e8;\n cursor: pointer;\n transition: background-color 0.1s;\n}\n\n.file-table[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] tr[_ngcontent-%COMP%]:hover {\n background-color: #f8f8f8;\n}\n\n.file-table[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] tr.selected[_ngcontent-%COMP%] {\n background-color: #cce8ff;\n}\n\n.file-table[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] tr.selected[_ngcontent-%COMP%]:hover {\n background-color: #b3d9ff;\n}\n\n.file-table[_ngcontent-%COMP%] td[_ngcontent-%COMP%] {\n padding: 6px 10px;\n color: #333;\n}\n\n\n\n.col-name[_ngcontent-%COMP%] {\n width: 50%;\n}\n\n.col-type[_ngcontent-%COMP%] {\n width: 15%;\n}\n\n.col-size[_ngcontent-%COMP%] {\n width: 15%;\n}\n\n.col-modified[_ngcontent-%COMP%] {\n width: 20%;\n}\n\n\n\n.col-name[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.item-icon[_ngcontent-%COMP%] {\n font-size: 14px;\n color: #666;\n flex-shrink: 0;\n}\n\n.item-name[_ngcontent-%COMP%] {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n\n\n.fa-folder[_ngcontent-%COMP%] {\n color: #FFB84D;\n}\n\n\n\n.fa-file-pdf[_ngcontent-%COMP%] {\n color: #D32F2F;\n}\n\n.fa-file-word[_ngcontent-%COMP%] {\n color: #2B579A;\n}\n\n.fa-file-excel[_ngcontent-%COMP%] {\n color: #217346;\n}\n\n.fa-file-image[_ngcontent-%COMP%] {\n color: #673AB7;\n}\n\n.fa-file-video[_ngcontent-%COMP%] {\n color: #F57C00;\n}\n\n.fa-file-audio[_ngcontent-%COMP%] {\n color: #D32F2F;\n}\n\n.fa-file-archive[_ngcontent-%COMP%] {\n color: #795548;\n}\n\n.fa-file-code[_ngcontent-%COMP%] {\n color: #388E3C;\n}\n\n\n\n\n\n\n.file-grid-wrapper[_ngcontent-%COMP%] {\n flex: 1;\n overflow: auto;\n padding: 10px;\n border-bottom: 1px solid #ccc;\n}\n\n.file-grid[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));\n gap: 12px;\n}\n\n.grid-item[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 12px 8px;\n border: 1px solid #e8e8e8;\n border-radius: 4px;\n cursor: pointer;\n transition: all 0.15s;\n background: #fff;\n}\n\n.grid-item[_ngcontent-%COMP%]:hover {\n background-color: #f8f8f8;\n border-color: #ccc;\n}\n\n.grid-item.selected[_ngcontent-%COMP%] {\n background-color: #cce8ff;\n border-color: #007AFF;\n}\n\n.grid-icon[_ngcontent-%COMP%] {\n font-size: 48px;\n margin-bottom: 8px;\n color: #666;\n}\n\n.grid-icon[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n display: block;\n}\n\n.grid-name[_ngcontent-%COMP%] {\n font-size: 12px;\n color: #333;\n text-align: center;\n word-break: break-word;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n max-width: 100%;\n}\n\n.grid-meta[_ngcontent-%COMP%] {\n font-size: 11px;\n color: #666;\n margin-top: 4px;\n}\n\n\n\n\n\n\n.bottom-toolbar[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 6px 10px;\n background-color: #f8f8f8;\n border-top: 1px solid #ccc;\n min-height: 36px;\n}\n\n.toolbar-left[_ngcontent-%COMP%] {\n display: flex;\n gap: 6px;\n}\n\n.toolbar-btn[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n background: #fff;\n border: 1px solid #ccc;\n padding: 5px 12px;\n cursor: pointer;\n border-radius: 3px;\n font-size: 12px;\n color: #333;\n transition: background-color 0.15s;\n}\n\n.toolbar-btn[_ngcontent-%COMP%]:hover:not(:disabled) {\n background-color: #e8e8e8;\n}\n\n.toolbar-btn[_ngcontent-%COMP%]:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.toolbar-btn[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 12px;\n}\n\n.toolbar-right[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n}\n\n.view-toggle[_ngcontent-%COMP%] {\n display: flex;\n border: 1px solid #ccc;\n border-radius: 3px;\n overflow: hidden;\n}\n\n.view-btn[_ngcontent-%COMP%] {\n background: #fff;\n border: none;\n border-right: 1px solid #ccc;\n padding: 4px 10px;\n cursor: pointer;\n font-size: 12px;\n color: #666;\n transition: all 0.15s;\n}\n\n.view-btn[_ngcontent-%COMP%]:last-child {\n border-right: none;\n}\n\n.view-btn[_ngcontent-%COMP%]:hover {\n background-color: #f0f0f0;\n color: #333;\n}\n\n.view-btn.active[_ngcontent-%COMP%] {\n background-color: #007AFF;\n color: #fff;\n}\n\n.item-count[_ngcontent-%COMP%] {\n font-size: 11px;\n color: #666;\n}\n\n\n\n\n\n\n.loading-state[_ngcontent-%COMP%], \n.error-state[_ngcontent-%COMP%], \n.empty-state[_ngcontent-%COMP%] {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 40px 20px;\n}\n\n.error-icon[_ngcontent-%COMP%], \n.empty-icon[_ngcontent-%COMP%] {\n font-size: 48px;\n color: #999;\n margin-bottom: 16px;\n}\n\n.error-message[_ngcontent-%COMP%], \n.empty-message[_ngcontent-%COMP%] {\n font-size: 14px;\n color: #666;\n margin-bottom: 16px;\n}\n\n.retry-btn[_ngcontent-%COMP%], \n.upload-btn[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n background: #fff;\n border: 1px solid #ccc;\n padding: 8px 16px;\n cursor: pointer;\n border-radius: 3px;\n font-size: 13px;\n color: #333;\n transition: background-color 0.15s;\n}\n\n.retry-btn[_ngcontent-%COMP%]:hover, \n.upload-btn[_ngcontent-%COMP%]:hover {\n background-color: #f0f0f0;\n}\n\n\n\n\n\n\n.drag-overlay[_ngcontent-%COMP%] {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: rgba(255, 255, 255, 0.95);\n z-index: 1000;\n display: flex;\n align-items: center;\n justify-content: center;\n border: 3px dashed #007AFF;\n}\n\n.drag-content[_ngcontent-%COMP%] {\n text-align: center;\n}\n\n.drag-icon[_ngcontent-%COMP%] {\n font-size: 64px;\n color: #007AFF;\n margin-bottom: 16px;\n}\n\n.drag-title[_ngcontent-%COMP%] {\n font-size: 20px;\n font-weight: 600;\n color: #007AFF;\n margin: 0;\n}\n\n\n\n\n\n\n.upload-progress[_ngcontent-%COMP%] {\n position: absolute;\n bottom: 40px;\n right: 20px;\n background: #fff;\n border: 1px solid #ccc;\n border-radius: 4px;\n padding: 12px 16px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\n min-width: 300px;\n z-index: 100;\n}\n\n.progress-content[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.progress-info[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 13px;\n color: #333;\n}\n\n.progress-bar-wrap[_ngcontent-%COMP%] {\n width: 100%;\n height: 6px;\n background-color: #e0e0e0;\n border-radius: 3px;\n overflow: hidden;\n}\n\n.progress-bar[_ngcontent-%COMP%] {\n height: 100%;\n background-color: #007AFF;\n transition: width 0.3s ease;\n}\n\n.progress-percent[_ngcontent-%COMP%] {\n font-size: 12px;\n color: #666;\n text-align: right;\n}\n\n\n\n\n\n\n.modal-overlay[_ngcontent-%COMP%] {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: rgba(0, 0, 0, 0.5);\n z-index: 2000;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.modal-dialog[_ngcontent-%COMP%] {\n background: #fff;\n border: 1px solid #999;\n border-radius: 4px;\n min-width: 400px;\n max-width: 90%;\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);\n}\n\n.modal-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 16px;\n background-color: #f8f8f8;\n border-bottom: 1px solid #ccc;\n}\n\n.modal-header[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 14px;\n font-weight: 600;\n color: #333;\n}\n\n.modal-close[_ngcontent-%COMP%] {\n background: none;\n border: none;\n cursor: pointer;\n font-size: 16px;\n color: #666;\n padding: 4px;\n}\n\n.modal-close[_ngcontent-%COMP%]:hover {\n color: #333;\n}\n\n.modal-body[_ngcontent-%COMP%] {\n padding: 16px;\n}\n\n.modal-body[_ngcontent-%COMP%] label[_ngcontent-%COMP%] {\n display: block;\n margin-bottom: 6px;\n font-size: 12px;\n font-weight: 600;\n color: #333;\n}\n\n.modal-body[_ngcontent-%COMP%] input[type=\"text\"][_ngcontent-%COMP%] {\n width: 100%;\n padding: 6px 10px;\n border: 1px solid #ccc;\n border-radius: 3px;\n font-size: 13px;\n font-family: inherit;\n}\n\n.modal-body[_ngcontent-%COMP%] input[type=\"text\"][_ngcontent-%COMP%]:focus {\n outline: none;\n border-color: #007AFF;\n}\n\n.modal-body[_ngcontent-%COMP%] select[_ngcontent-%COMP%], \n.modal-body[_ngcontent-%COMP%] .provider-select[_ngcontent-%COMP%] {\n width: 100%;\n padding: 6px 10px;\n border: 1px solid #ccc;\n border-radius: 3px;\n font-size: 13px;\n font-family: inherit;\n background-color: #fff;\n cursor: pointer;\n}\n\n.modal-body[_ngcontent-%COMP%] select[_ngcontent-%COMP%]:focus, \n.modal-body[_ngcontent-%COMP%] .provider-select[_ngcontent-%COMP%]:focus {\n outline: none;\n border-color: #007AFF;\n}\n\n.modal-body[_ngcontent-%COMP%] select[_ngcontent-%COMP%]:disabled, \n.modal-body[_ngcontent-%COMP%] .provider-select[_ngcontent-%COMP%]:disabled {\n background-color: #f5f5f5;\n cursor: not-allowed;\n}\n\n.modal-body[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0 0 12px 0;\n font-size: 13px;\n color: #333;\n}\n\n.modal-body[_ngcontent-%COMP%] .warning[_ngcontent-%COMP%] {\n color: #D32F2F;\n font-size: 12px;\n}\n\n\n\n.account-checkbox-list[_ngcontent-%COMP%] {\n max-height: 200px;\n overflow-y: auto;\n border: 1px solid #ccc;\n border-radius: 4px;\n padding: 8px;\n background: #fafafa;\n}\n\n.account-checkbox-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 4px;\n border-radius: 3px;\n cursor: pointer;\n}\n\n.account-checkbox-item[_ngcontent-%COMP%]:hover {\n background-color: #e8f4fd;\n}\n\n.account-checkbox-item[_ngcontent-%COMP%] input[type=\"checkbox\"][_ngcontent-%COMP%] {\n width: 16px;\n height: 16px;\n cursor: pointer;\n accent-color: #007AFF;\n}\n\n.account-checkbox-item[_ngcontent-%COMP%] label[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n cursor: pointer;\n font-size: 13px;\n font-weight: normal;\n margin: 0;\n}\n\n.provider-badge[_ngcontent-%COMP%] {\n font-size: 11px;\n color: #666;\n background: #e0e0e0;\n padding: 2px 6px;\n border-radius: 10px;\n}\n\n.copy-progress[_ngcontent-%COMP%] {\n margin-top: 12px;\n padding: 8px 12px;\n background: #e8f4fd;\n border-radius: 4px;\n font-size: 12px;\n color: #0066cc;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.modal-footer[_ngcontent-%COMP%] {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n padding: 12px 16px;\n background-color: #f8f8f8;\n border-top: 1px solid #ccc;\n}\n\n.modal-footer[_ngcontent-%COMP%] button[_ngcontent-%COMP%] {\n padding: 6px 16px;\n border: 1px solid #ccc;\n background: #fff;\n cursor: pointer;\n border-radius: 3px;\n font-size: 12px;\n font-weight: 500;\n transition: background-color 0.15s;\n}\n\n.modal-footer[_ngcontent-%COMP%] button[_ngcontent-%COMP%]:hover:not(:disabled) {\n background-color: #f0f0f0;\n}\n\n.modal-footer[_ngcontent-%COMP%] button[_ngcontent-%COMP%]:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.modal-footer[_ngcontent-%COMP%] button.danger[_ngcontent-%COMP%] {\n background-color: #D32F2F;\n color: #fff;\n border-color: #D32F2F;\n}\n\n.modal-footer[_ngcontent-%COMP%] button.danger[_ngcontent-%COMP%]:hover:not(:disabled) {\n background-color: #B71C1C;\n}\n\n\n\n\n\n\n.file-table-wrapper[_ngcontent-%COMP%]::-webkit-scrollbar {\n width: 12px;\n height: 12px;\n}\n\n.file-table-wrapper[_ngcontent-%COMP%]::-webkit-scrollbar-track {\n background: #f0f0f0;\n}\n\n.file-table-wrapper[_ngcontent-%COMP%]::-webkit-scrollbar-thumb {\n background: #c0c0c0;\n border: 2px solid #f0f0f0;\n}\n\n.file-table-wrapper[_ngcontent-%COMP%]::-webkit-scrollbar-thumb:hover {\n background: #a0a0a0;\n}\n\n\n\n\n\n\n@media screen and (max-width: 768px) {\n .file-table[_ngcontent-%COMP%] {\n font-size: 11px;\n }\n\n .file-table[_ngcontent-%COMP%] th[_ngcontent-%COMP%], \n .file-table[_ngcontent-%COMP%] td[_ngcontent-%COMP%] {\n padding: 4px 6px;\n }\n\n .col-type[_ngcontent-%COMP%], \n .col-size[_ngcontent-%COMP%] {\n display: none;\n }\n\n .col-name[_ngcontent-%COMP%] {\n width: 60%;\n }\n\n .col-modified[_ngcontent-%COMP%] {\n width: 40%;\n }\n\n .toolbar-btn[_ngcontent-%COMP%] span[_ngcontent-%COMP%] {\n display: none;\n }\n\n .modal-dialog[_ngcontent-%COMP%] {\n min-width: 90%;\n }\n}\n\n\n\n\n\n\n.multi-search-btn[_ngcontent-%COMP%] {\n background: #fff;\n border: 1px solid #ccc;\n padding: 4px 8px;\n cursor: pointer;\n border-radius: 3px;\n font-size: 12px;\n color: #666;\n transition: all 0.15s;\n}\n\n.multi-search-btn[_ngcontent-%COMP%]:hover {\n background-color: #e8e8e8;\n color: #333;\n}\n\n.multi-search-btn.active[_ngcontent-%COMP%] {\n background-color: #007AFF;\n color: #fff;\n border-color: #007AFF;\n}\n\n.multi-provider-search-panel[_ngcontent-%COMP%] {\n background: #fff;\n border-bottom: 1px solid #ccc;\n display: flex;\n flex-direction: column;\n max-height: 70vh;\n overflow: hidden;\n}\n\n.search-panel-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 16px;\n background-color: #f0f7ff;\n border-bottom: 1px solid #cce8ff;\n}\n\n.search-panel-header[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 14px;\n font-weight: 600;\n color: #007AFF;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.close-panel-btn[_ngcontent-%COMP%] {\n background: none;\n border: none;\n cursor: pointer;\n font-size: 16px;\n color: #666;\n padding: 4px;\n}\n\n.close-panel-btn[_ngcontent-%COMP%]:hover {\n color: #333;\n}\n\n.search-panel-body[_ngcontent-%COMP%] {\n padding: 16px;\n}\n\n.search-input-row[_ngcontent-%COMP%] {\n display: flex;\n gap: 8px;\n margin-bottom: 16px;\n}\n\n.multi-search-input[_ngcontent-%COMP%] {\n flex: 1;\n padding: 8px 12px;\n border: 1px solid #ccc;\n border-radius: 4px;\n font-size: 13px;\n font-family: inherit;\n}\n\n.multi-search-input[_ngcontent-%COMP%]:focus {\n outline: none;\n border-color: #007AFF;\n}\n\n.search-execute-btn[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n background: #007AFF;\n color: #fff;\n border: none;\n padding: 8px 16px;\n cursor: pointer;\n border-radius: 4px;\n font-size: 13px;\n font-weight: 500;\n transition: background-color 0.15s;\n}\n\n.search-execute-btn[_ngcontent-%COMP%]:hover:not(:disabled) {\n background-color: #0056b3;\n}\n\n.search-execute-btn[_ngcontent-%COMP%]:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.provider-selection[_ngcontent-%COMP%] {\n margin-bottom: 8px;\n}\n\n.provider-selection-label[_ngcontent-%COMP%] {\n display: block;\n font-size: 12px;\n font-weight: 600;\n color: #333;\n margin-bottom: 8px;\n}\n\n.provider-checkboxes[_ngcontent-%COMP%] {\n display: flex;\n flex-wrap: wrap;\n gap: 12px;\n}\n\n.provider-checkbox[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 13px;\n color: #333;\n cursor: pointer;\n padding: 4px 8px;\n border: 1px solid #e0e0e0;\n border-radius: 4px;\n background: #fafafa;\n transition: all 0.15s;\n}\n\n.provider-checkbox[_ngcontent-%COMP%]:hover:not(.disabled) {\n border-color: #007AFF;\n background: #f0f7ff;\n}\n\n.provider-checkbox.disabled[_ngcontent-%COMP%] {\n opacity: 0.6;\n cursor: not-allowed;\n background: #f5f5f5;\n}\n\n.provider-checkbox[_ngcontent-%COMP%] input[type=\"checkbox\"][_ngcontent-%COMP%] {\n cursor: pointer;\n}\n\n.provider-checkbox[_ngcontent-%COMP%] .provider-name[_ngcontent-%COMP%] {\n font-weight: 500;\n}\n\n.no-search-badge[_ngcontent-%COMP%] {\n font-size: 10px;\n color: #999;\n font-style: italic;\n}\n\n\n\n.search-results-container[_ngcontent-%COMP%] {\n flex: 1;\n overflow: auto;\n border-top: 1px solid #e0e0e0;\n}\n\n.search-results-summary[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 10px 16px;\n background: #f8f8f8;\n border-bottom: 1px solid #e0e0e0;\n}\n\n.search-results-summary[_ngcontent-%COMP%] .result-count[_ngcontent-%COMP%] {\n font-weight: 600;\n font-size: 13px;\n color: #333;\n}\n\n.search-results-summary[_ngcontent-%COMP%] .provider-stats[_ngcontent-%COMP%] {\n font-size: 12px;\n color: #666;\n}\n\n.search-results-summary[_ngcontent-%COMP%] .failed-count[_ngcontent-%COMP%] {\n color: #D32F2F;\n}\n\n.clear-results-btn[_ngcontent-%COMP%] {\n margin-left: auto;\n background: none;\n border: 1px solid #ccc;\n padding: 4px 10px;\n cursor: pointer;\n border-radius: 3px;\n font-size: 11px;\n color: #666;\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.clear-results-btn[_ngcontent-%COMP%]:hover {\n background: #f0f0f0;\n color: #333;\n}\n\n.provider-results-list[_ngcontent-%COMP%] {\n padding: 8px 0;\n}\n\n.provider-result-group[_ngcontent-%COMP%] {\n margin-bottom: 16px;\n}\n\n.provider-result-group[_ngcontent-%COMP%]:last-child {\n margin-bottom: 0;\n}\n\n.provider-result-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 16px;\n background: #f5f5f5;\n font-size: 13px;\n font-weight: 600;\n color: #333;\n}\n\n.provider-result-header[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: #007AFF;\n}\n\n.provider-result-header[_ngcontent-%COMP%] .result-count[_ngcontent-%COMP%] {\n font-weight: normal;\n color: #666;\n}\n\n.provider-result-group.failed[_ngcontent-%COMP%] .provider-result-header[_ngcontent-%COMP%] {\n background: #fff5f5;\n}\n\n.provider-result-group.failed[_ngcontent-%COMP%] .provider-result-header[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: #D32F2F;\n}\n\n.error-badge[_ngcontent-%COMP%] {\n color: #D32F2F;\n font-weight: normal;\n font-size: 12px;\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.provider-result-files[_ngcontent-%COMP%] {\n padding: 0;\n}\n\n.search-result-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 8px 16px 8px 40px;\n border-bottom: 1px solid #f0f0f0;\n cursor: pointer;\n transition: background-color 0.1s;\n}\n\n.search-result-item[_ngcontent-%COMP%]:hover {\n background-color: #f8f8f8;\n}\n\n.search-result-item[_ngcontent-%COMP%]:last-child {\n border-bottom: none;\n}\n\n.search-result-item[_ngcontent-%COMP%] .file-icon[_ngcontent-%COMP%] {\n font-size: 16px;\n color: #666;\n flex-shrink: 0;\n}\n\n.search-result-item[_ngcontent-%COMP%] .file-info[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.search-result-item[_ngcontent-%COMP%] .file-name[_ngcontent-%COMP%] {\n font-size: 13px;\n font-weight: 500;\n color: #333;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.search-result-item[_ngcontent-%COMP%] .file-path[_ngcontent-%COMP%] {\n font-size: 11px;\n color: #999;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.search-result-item[_ngcontent-%COMP%] .file-size[_ngcontent-%COMP%] {\n font-size: 12px;\n color: #666;\n flex-shrink: 0;\n min-width: 60px;\n text-align: right;\n}\n\n.search-result-item[_ngcontent-%COMP%] .file-date[_ngcontent-%COMP%] {\n font-size: 12px;\n color: #666;\n flex-shrink: 0;\n min-width: 140px;\n text-align: right;\n}\n\n.no-results-message[_ngcontent-%COMP%] {\n padding: 16px 40px;\n font-size: 12px;\n color: #999;\n font-style: italic;\n}\n\n.searching-state[_ngcontent-%COMP%] {\n padding: 40px 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n}"] });
|
|
2380
|
+
}
|
|
2381
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(FileGridComponent, [{
|
|
2382
|
+
type: Component,
|
|
2383
|
+
args: [{ selector: 'mj-file-grid', template: "<div class=\"file-grid-container\"\n (dragenter)=\"onDragEnter($event)\"\n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\">\n <!-- Breadcrumb Navigation Bar -->\n <div class=\"breadcrumb-bar\" *ngIf=\"!isLoading && !errorMessage\">\n <div class=\"nav-buttons\">\n <button class=\"nav-btn\"\n [disabled]=\"!canNavigateUp()\"\n (click)=\"navigateUp()\"\n title=\"Go up to parent folder\">\n <i class=\"fa-solid fa-arrow-up\"></i>\n </button>\n <button class=\"nav-btn\" (click)=\"refresh()\" title=\"Refresh\">\n <i class=\"fa-solid fa-arrows-rotate\"></i>\n </button>\n </div>\n <div class=\"breadcrumb-path\">\n <i class=\"fa-solid fa-folder\"></i>\n <span class=\"path-text\">{{ folderPath || 'Root' }} /</span>\n </div>\n <div class=\"search-bar\">\n <input type=\"text\"\n class=\"search-input\"\n [(ngModel)]=\"searchQuery\"\n (ngModelChange)=\"onSearchChange($event)\"\n placeholder=\"Search files and folders...\"\n [disabled]=\"isLoading || isMultiProviderSearchMode\">\n <button class=\"search-clear-btn\"\n *ngIf=\"searchQuery && !isMultiProviderSearchMode\"\n (click)=\"clearSearch()\"\n title=\"Clear search\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n <select class=\"file-type-filter\"\n [(ngModel)]=\"fileTypeFilter\"\n (ngModelChange)=\"onFileTypeFilterChange($event)\"\n [disabled]=\"isLoading || isMultiProviderSearchMode\">\n <option value=\"all\">All</option>\n <option value=\"files\">Files</option>\n <option value=\"folders\">Folders</option>\n </select>\n <button class=\"multi-search-btn\"\n [class.active]=\"isMultiProviderSearchMode\"\n (click)=\"toggleMultiAccountSearchMode()\"\n title=\"Search across all storage providers\">\n <i class=\"fa-solid fa-magnifying-glass-plus\"></i>\n </button>\n </div>\n </div>\n\n <!-- Multi-Provider Search Panel -->\n <div class=\"multi-provider-search-panel\" *ngIf=\"isMultiProviderSearchMode\">\n <div class=\"search-panel-header\">\n <h3><i class=\"fa-solid fa-magnifying-glass\"></i> Search Across Providers</h3>\n <button class=\"close-panel-btn\" (click)=\"toggleMultiAccountSearchMode()\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n\n <div class=\"search-panel-body\">\n <div class=\"search-input-row\">\n <input type=\"text\"\n class=\"multi-search-input\"\n [(ngModel)]=\"multiProviderSearchQuery\"\n placeholder=\"Enter search term...\"\n (keydown.enter)=\"executeMultiAccountSearch()\"\n [disabled]=\"isSearching\">\n <button class=\"search-execute-btn\"\n (click)=\"executeMultiAccountSearch()\"\n [disabled]=\"isSearching || !multiProviderSearchQuery.trim() || selectedSearchProviders.size === 0\">\n <i class=\"fa-solid fa-spinner fa-spin\" *ngIf=\"isSearching\"></i>\n <i class=\"fa-solid fa-search\" *ngIf=\"!isSearching\"></i>\n {{ isSearching ? 'Searching...' : 'Search' }}\n </button>\n </div>\n\n <div class=\"provider-selection\">\n <label class=\"provider-selection-label\">Select accounts to search:</label>\n <div class=\"provider-checkboxes\">\n <label *ngFor=\"let a of availableAccounts\"\n class=\"provider-checkbox\"\n [class.disabled]=\"!accountSupportsSearch(a)\"\n [title]=\"accountSupportsSearch(a) ? '' : 'This account does not support search'\">\n <input type=\"checkbox\"\n [checked]=\"isAccountSelectedForSearch(a.account.ID)\"\n (change)=\"toggleSearchAccount(a.account.ID)\"\n [disabled]=\"!accountSupportsSearch(a)\">\n <span class=\"provider-name\">{{ a.account.Name }}</span>\n <span class=\"provider-type\">({{ a.provider.Name }})</span>\n <span class=\"no-search-badge\" *ngIf=\"!accountSupportsSearch(a)\">(no search)</span>\n </label>\n </div>\n </div>\n </div>\n\n <!-- Search Results -->\n <div class=\"search-results-container\" *ngIf=\"multiProviderSearchResults\">\n <div class=\"search-results-summary\">\n <span class=\"result-count\">{{ multiProviderSearchResults.totalResultsReturned }} result(s) found</span>\n <span class=\"provider-stats\">\n ({{ multiProviderSearchResults.successfulAccounts }} account(s) searched\n <span *ngIf=\"multiProviderSearchResults.failedAccounts > 0\" class=\"failed-count\">\n , {{ multiProviderSearchResults.failedAccounts }} failed\n </span>)\n </span>\n <button class=\"clear-results-btn\" (click)=\"clearMultiProviderSearch()\">\n <i class=\"fa-solid fa-xmark\"></i> Clear\n </button>\n </div>\n\n <div class=\"provider-results-list\">\n <div *ngFor=\"let accountResult of multiProviderSearchResults.accountResults\"\n class=\"provider-result-group\"\n [class.failed]=\"!accountResult.success\">\n <div class=\"provider-result-header\">\n <i class=\"fa-solid fa-cloud\"></i>\n <span class=\"provider-name\">{{ accountResult.accountName }}</span>\n <span class=\"result-count\" *ngIf=\"accountResult.success\">\n ({{ accountResult.results.length }} result(s))\n </span>\n <span class=\"error-badge\" *ngIf=\"!accountResult.success\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n {{ accountResult.errorMessage }}\n </span>\n </div>\n\n <div class=\"provider-result-files\" *ngIf=\"accountResult.success && accountResult.results.length > 0\">\n <div *ngFor=\"let file of accountResult.results\" class=\"search-result-item\">\n <i [class]=\"getSearchResultIcon(file)\" class=\"file-icon\"></i>\n <div class=\"file-info\">\n <span class=\"file-name\">{{ file.name }}</span>\n <span class=\"file-path\">{{ file.path }}</span>\n </div>\n <span class=\"file-size\">{{ formatSearchResultSize(file.size) }}</span>\n <span class=\"file-date\">{{ formatSearchResultDate(file.lastModified) }}</span>\n </div>\n </div>\n\n <div class=\"no-results-message\" *ngIf=\"accountResult.success && accountResult.results.length === 0\">\n No matching files found\n </div>\n </div>\n </div>\n </div>\n\n <!-- Searching State -->\n <div class=\"searching-state\" *ngIf=\"isSearching\">\n <mj-loading text=\"Searching across providers...\"></mj-loading>\n </div>\n </div>\n\n <!-- Loading State -->\n <div class=\"loading-state\" *ngIf=\"isLoading && !isMultiProviderSearchMode\">\n <mj-loading text=\"Loading files...\"></mj-loading>\n </div>\n\n <!-- Error State -->\n <div class=\"error-state\" *ngIf=\"errorMessage && !isLoading\">\n <div class=\"error-icon\">\n <i class=\"fa-solid fa-circle-exclamation\"></i>\n </div>\n <p class=\"error-message\">{{ errorMessage }}</p>\n <button class=\"retry-btn\" (click)=\"refresh()\">\n <i class=\"fa-solid fa-arrows-rotate\"></i>\n Retry\n </button>\n </div>\n\n <!-- List View -->\n <div class=\"file-table-wrapper\" *ngIf=\"!isLoading && !errorMessage && filteredItems.length > 0 && viewMode === 'list'\">\n <table class=\"file-table\">\n <thead>\n <tr>\n <th class=\"col-name\" (click)=\"onSortChange([{ field: 'name', dir: sort[0]?.field === 'name' && sort[0]?.dir === 'asc' ? 'desc' : 'asc' }])\">\n <span>Name</span>\n <i class=\"fa-solid sort-icon\"\n [class.fa-caret-up]=\"sort[0]?.field === 'name' && sort[0]?.dir === 'asc'\"\n [class.fa-caret-down]=\"sort[0]?.field === 'name' && sort[0]?.dir === 'desc'\"\n [class.fa-sort]=\"sort[0]?.field !== 'name'\"></i>\n </th>\n <th class=\"col-type\">Type</th>\n <th class=\"col-size\" (click)=\"onSortChange([{ field: 'size', dir: sort[0]?.field === 'size' && sort[0]?.dir === 'asc' ? 'desc' : 'asc' }])\">\n <span>Size</span>\n <i class=\"fa-solid sort-icon\"\n [class.fa-caret-up]=\"sort[0]?.field === 'size' && sort[0]?.dir === 'asc'\"\n [class.fa-caret-down]=\"sort[0]?.field === 'size' && sort[0]?.dir === 'desc'\"\n [class.fa-sort]=\"sort[0]?.field !== 'size'\"></i>\n </th>\n <th class=\"col-modified\" (click)=\"onSortChange([{ field: 'lastModified', dir: sort[0]?.field === 'lastModified' && sort[0]?.dir === 'asc' ? 'desc' : 'asc' }])\">\n <span>Modified</span>\n <i class=\"fa-solid sort-icon\"\n [class.fa-caret-up]=\"sort[0]?.field === 'lastModified' && sort[0]?.dir === 'asc'\"\n [class.fa-caret-down]=\"sort[0]?.field === 'lastModified' && sort[0]?.dir === 'desc'\"\n [class.fa-sort]=\"sort[0]?.field !== 'lastModified'\"></i>\n </th>\n </tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let item of filteredItems\"\n class=\"file-row\"\n [class.selected]=\"selectedItems.includes(item.key)\"\n (click)=\"onTileClick(item, $event)\"\n (dblclick)=\"onItemDoubleClick(item)\">\n <td class=\"col-name\">\n <i [class]=\"getItemIcon(item)\" class=\"item-icon\"></i>\n <span class=\"item-name\">{{ item.name }}</span>\n </td>\n <td class=\"col-type\">\n <span class=\"item-type\">{{ getFileType(item) }}</span>\n </td>\n <td class=\"col-size\">\n <span class=\"item-size\">{{ item.type === 'folder' ? '\u2014' : formatFileSize(item.size) }}</span>\n </td>\n <td class=\"col-modified\">\n <span class=\"item-date\">{{ formatDate(item.lastModified) }}</span>\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n\n <!-- Grid View -->\n <div class=\"file-grid-wrapper\" *ngIf=\"!isLoading && !errorMessage && filteredItems.length > 0 && viewMode === 'grid'\">\n <div class=\"file-grid\">\n <div *ngFor=\"let item of filteredItems\"\n class=\"grid-item\"\n [class.selected]=\"selectedItems.includes(item.key)\"\n (click)=\"onTileClick(item, $event)\"\n (dblclick)=\"onItemDoubleClick(item)\">\n <div class=\"grid-icon\">\n <i [class]=\"getItemIcon(item)\"></i>\n </div>\n <div class=\"grid-name\">{{ item.name }}</div>\n <div class=\"grid-meta\" *ngIf=\"item.type === 'file'\">{{ formatFileSize(item.size) }}</div>\n <div class=\"grid-meta\" *ngIf=\"item.type === 'folder'\">Folder</div>\n </div>\n </div>\n </div>\n\n <!-- Empty State -->\n <div class=\"empty-state\" *ngIf=\"!isLoading && !errorMessage && filteredItems.length === 0\">\n <div class=\"empty-icon\">\n <i class=\"fa-solid fa-folder-open\"></i>\n </div>\n <p class=\"empty-message\">This folder is empty</p>\n <button class=\"upload-btn\" (click)=\"onUploadClick()\">\n <i class=\"fa-solid fa-upload\"></i>\n Upload Files\n </button>\n </div>\n\n <!-- Bottom Toolbar -->\n <div class=\"bottom-toolbar\" *ngIf=\"!isLoading && !errorMessage\">\n <div class=\"toolbar-left\">\n <button class=\"toolbar-btn\" (click)=\"onUploadClick()\">\n <i class=\"fa-solid fa-upload\"></i>\n Upload\n </button>\n <button class=\"toolbar-btn\" (click)=\"onNewFolderClick()\">\n <i class=\"fa-solid fa-folder-plus\"></i>\n New Folder\n </button>\n <button class=\"toolbar-btn\"\n [disabled]=\"selectedItems.length !== 1\"\n (click)=\"onDownloadClick()\">\n <i class=\"fa-solid fa-download\"></i>\n Download\n </button>\n <button class=\"toolbar-btn\"\n [disabled]=\"selectedItems.length !== 1\"\n (click)=\"onRenameClick()\">\n <i class=\"fa-solid fa-pen\"></i>\n Rename\n </button>\n <button class=\"toolbar-btn\"\n [disabled]=\"selectedItems.length !== 1\"\n (click)=\"onCopyClick()\">\n <i class=\"fa-solid fa-copy\"></i>\n Copy\n </button>\n <button class=\"toolbar-btn\"\n [disabled]=\"selectedItems.length !== 1 || getSelectedItem()?.type === 'folder'\"\n (click)=\"onCopyToAccountClick()\"\n title=\"Copy file to another storage account\">\n <i class=\"fa-solid fa-cloud-arrow-up\"></i>\n Copy to...\n </button>\n <button class=\"toolbar-btn\"\n [disabled]=\"selectedItems.length !== 1\"\n (click)=\"onMoveClick()\">\n <i class=\"fa-solid fa-arrows-up-down-left-right\"></i>\n Move\n </button>\n <button class=\"toolbar-btn\"\n [disabled]=\"selectedItems.length === 0\"\n (click)=\"onDeleteClick()\">\n <i class=\"fa-solid fa-trash\"></i>\n Delete\n </button>\n </div>\n <div class=\"toolbar-right\">\n <!-- View Toggle -->\n <div class=\"view-toggle\">\n <button class=\"view-btn\"\n [class.active]=\"viewMode === 'list'\"\n (click)=\"viewMode = 'list'\"\n title=\"List View\">\n <i class=\"fa-solid fa-list\"></i>\n </button>\n <button class=\"view-btn\"\n [class.active]=\"viewMode === 'grid'\"\n (click)=\"viewMode = 'grid'\"\n title=\"Grid View\">\n <i class=\"fa-solid fa-th\"></i>\n </button>\n </div>\n <span class=\"item-count\">\n {{ filteredItems.length }} {{ filteredItems.length === 1 ? 'item' : 'filteredItems' }}\n </span>\n </div>\n </div>\n\n <!-- Drag & Drop Overlay -->\n <div class=\"drag-overlay\" *ngIf=\"isDragging\">\n <div class=\"drag-content\">\n <div class=\"drag-icon\">\n <i class=\"fa-solid fa-cloud-arrow-up\"></i>\n </div>\n <h2 class=\"drag-title\">Drop files to upload</h2>\n </div>\n </div>\n\n <!-- Upload Progress -->\n <div class=\"upload-progress\" *ngIf=\"isUploading\">\n <div class=\"progress-content\">\n <div class=\"progress-info\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span>Uploading {{ uploadingFileName }}...</span>\n </div>\n <div class=\"progress-bar-wrap\">\n <div class=\"progress-bar\" [style.width.%]=\"uploadProgress\"></div>\n </div>\n <span class=\"progress-percent\">{{ uploadProgress }}%</span>\n </div>\n </div>\n</div>\n\n<!-- New Folder Modal -->\n<div class=\"modal-overlay\" *ngIf=\"showNewFolderDialog\" (click)=\"onCancelNewFolder()\">\n <div class=\"modal-dialog\" (click)=\"$event.stopPropagation()\">\n <div class=\"modal-header\">\n <h3>Create New Folder</h3>\n <button class=\"modal-close\" (click)=\"onCancelNewFolder()\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n <div class=\"modal-body\">\n <label>Folder name:</label>\n <input type=\"text\"\n [(ngModel)]=\"newFolderName\"\n [disabled]=\"isCreatingFolder\"\n placeholder=\"Enter folder name\"\n (keydown.enter)=\"onCreateFolder()\">\n </div>\n <div class=\"modal-footer\">\n <button (click)=\"onCancelNewFolder()\" [disabled]=\"isCreatingFolder\">Cancel</button>\n <button (click)=\"onCreateFolder()\" [disabled]=\"!newFolderName.trim() || isCreatingFolder\">\n <i class=\"fa-solid fa-spinner fa-spin\" *ngIf=\"isCreatingFolder\"></i>\n {{ isCreatingFolder ? 'Creating...' : 'Create' }}\n </button>\n </div>\n </div>\n</div>\n\n<!-- Delete Confirmation Modal -->\n<div class=\"modal-overlay\" *ngIf=\"showDeleteDialog && itemToDelete\" (click)=\"onCancelDelete()\">\n <div class=\"modal-dialog\" (click)=\"$event.stopPropagation()\">\n <div class=\"modal-header\">\n <h3>Delete {{ itemToDelete.type === 'folder' ? 'Folder' : 'File' }}?</h3>\n <button class=\"modal-close\" (click)=\"onCancelDelete()\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n <div class=\"modal-body\">\n <p>Are you sure you want to delete <strong>{{ itemToDelete.name }}</strong>?</p>\n <p *ngIf=\"itemToDelete.type === 'folder'\" class=\"warning\">This will delete the folder and all its contents.</p>\n </div>\n <div class=\"modal-footer\">\n <button (click)=\"onCancelDelete()\" [disabled]=\"isDeleting\">Cancel</button>\n <button (click)=\"onConfirmDelete()\" [disabled]=\"isDeleting\" class=\"danger\">\n <i class=\"fa-solid fa-spinner fa-spin\" *ngIf=\"isDeleting\"></i>\n {{ isDeleting ? 'Deleting...' : 'Delete' }}\n </button>\n </div>\n </div>\n</div>\n\n<!-- Rename Modal -->\n<div class=\"modal-overlay\" *ngIf=\"showRenameDialog && itemToRename\" (click)=\"onCancelRename()\">\n <div class=\"modal-dialog\" (click)=\"$event.stopPropagation()\">\n <div class=\"modal-header\">\n <h3>Rename {{ itemToRename.type === 'folder' ? 'Folder' : 'File' }}</h3>\n <button class=\"modal-close\" (click)=\"onCancelRename()\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n <div class=\"modal-body\">\n <label>New name:</label>\n <input type=\"text\"\n [(ngModel)]=\"newItemName\"\n [disabled]=\"isRenaming\"\n placeholder=\"Enter new name\"\n (keydown.enter)=\"onConfirmRename()\">\n </div>\n <div class=\"modal-footer\">\n <button (click)=\"onCancelRename()\" [disabled]=\"isRenaming\">Cancel</button>\n <button (click)=\"onConfirmRename()\" [disabled]=\"isRenaming || !newItemName.trim()\">\n <i class=\"fa-solid fa-spinner fa-spin\" *ngIf=\"isRenaming\"></i>\n {{ isRenaming ? 'Renaming...' : 'Rename' }}\n </button>\n </div>\n </div>\n</div>\n\n<!-- Copy Modal -->\n<div class=\"modal-overlay\" *ngIf=\"showCopyDialog && itemToCopy\" (click)=\"onCancelCopy()\">\n <div class=\"modal-dialog\" (click)=\"$event.stopPropagation()\">\n <div class=\"modal-header\">\n <h3>Copy {{ itemToCopy.type === 'folder' ? 'Folder' : 'File' }}</h3>\n <button class=\"modal-close\" (click)=\"onCancelCopy()\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n <div class=\"modal-body\">\n <p>Copying: <strong>{{ itemToCopy.name }}</strong></p>\n <label>Destination path:</label>\n <input type=\"text\"\n [(ngModel)]=\"copyDestinationPath\"\n [disabled]=\"isCopying\"\n placeholder=\"Enter destination path\"\n (keydown.enter)=\"onConfirmCopy()\">\n </div>\n <div class=\"modal-footer\">\n <button (click)=\"onCancelCopy()\" [disabled]=\"isCopying\">Cancel</button>\n <button (click)=\"onConfirmCopy()\" [disabled]=\"isCopying || !copyDestinationPath.trim()\">\n <i class=\"fa-solid fa-spinner fa-spin\" *ngIf=\"isCopying\"></i>\n {{ isCopying ? 'Copying...' : 'Copy' }}\n </button>\n </div>\n </div>\n</div>\n\n<!-- Move Modal -->\n<div class=\"modal-overlay\" *ngIf=\"showMoveDialog && itemToMove\" (click)=\"onCancelMove()\">\n <div class=\"modal-dialog\" (click)=\"$event.stopPropagation()\">\n <div class=\"modal-header\">\n <h3>Move {{ itemToMove.type === 'folder' ? 'Folder' : 'File' }}</h3>\n <button class=\"modal-close\" (click)=\"onCancelMove()\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n <div class=\"modal-body\">\n <p>Moving: <strong>{{ itemToMove.name }}</strong></p>\n <label>Destination folder path:</label>\n <input type=\"text\"\n [(ngModel)]=\"moveDestinationPath\"\n [disabled]=\"isMoving\"\n placeholder=\"Enter destination folder (e.g., folder1/folder2/)\"\n (keydown.enter)=\"onConfirmMove()\">\n <p style=\"font-size: 11px; color: #666; margin-top: 8px;\">\n File will be moved to: {{ moveDestinationPath }}{{ itemToMove.name }}\n </p>\n </div>\n <div class=\"modal-footer\">\n <button (click)=\"onCancelMove()\" [disabled]=\"isMoving\">Cancel</button>\n <button (click)=\"onConfirmMove()\" [disabled]=\"isMoving || !moveDestinationPath.trim()\">\n <i class=\"fa-solid fa-spinner fa-spin\" *ngIf=\"isMoving\"></i>\n {{ isMoving ? 'Moving...' : 'Move' }}\n </button>\n </div>\n </div>\n</div>\n\n<!-- Copy to Account Modal -->\n<div class=\"modal-overlay\" *ngIf=\"showCopyToProviderDialog && itemToCopyToProvider\" (click)=\"onCancelCopyToAccount()\">\n <div class=\"modal-dialog\" (click)=\"$event.stopPropagation()\">\n <div class=\"modal-header\">\n <h3>Copy to Another Account</h3>\n <button class=\"modal-close\" (click)=\"onCancelCopyToAccount()\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n <div class=\"modal-body\">\n <p>Copying: <strong>{{ itemToCopyToProvider.name }}</strong></p>\n <p style=\"font-size: 12px; color: #666; margin-bottom: 16px;\">\n From: <strong>{{ account?.account?.Name }}</strong>\n </p>\n\n <label>Select destination account(s):</label>\n <div class=\"account-checkbox-list\">\n <div *ngFor=\"let a of availableAccounts\" class=\"account-checkbox-item\">\n <input type=\"checkbox\"\n [id]=\"'dest-account-' + a.account.ID\"\n [checked]=\"isDestinationAccountSelected(a.account.ID)\"\n [disabled]=\"isCopyingToAccount\"\n (change)=\"toggleDestinationAccount(a.account.ID)\">\n <label [for]=\"'dest-account-' + a.account.ID\">\n {{ a.account.Name }} <span class=\"provider-badge\">{{ a.provider.Name }}</span>\n </label>\n </div>\n </div>\n\n <label style=\"margin-top: 16px;\">Destination filename:</label>\n <input type=\"text\"\n [(ngModel)]=\"copyToAccountDestinationPath\"\n [disabled]=\"isCopyingToAccount\"\n placeholder=\"Enter destination filename\"\n (keydown.enter)=\"onConfirmCopyToAccount()\">\n <div *ngIf=\"copyToAccountProgress\" class=\"copy-progress\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n Copying to {{ copyToAccountProgress.currentAccount }} ({{ copyToAccountProgress.current }}/{{ copyToAccountProgress.total }})...\n </div>\n </div>\n <div class=\"modal-footer\">\n <button (click)=\"onCancelCopyToAccount()\" [disabled]=\"isCopyingToAccount\">Cancel</button>\n <button (click)=\"onConfirmCopyToAccount()\"\n [disabled]=\"isCopyingToAccount || selectedDestinationAccounts.size === 0 || !copyToAccountDestinationPath.trim()\">\n <i class=\"fa-solid fa-spinner fa-spin\" *ngIf=\"isCopyingToAccount\"></i>\n {{ isCopyingToAccount ? 'Copying...' : 'Copy to ' + selectedDestinationAccounts.size + ' account' + (selectedDestinationAccounts.size !== 1 ? 's' : '') }}\n </button>\n </div>\n </div>\n</div>\n", styles: ["/* ===========================\n File Grid - Traditional File Manager Style\n =========================== */\n\n:host {\n display: flex;\n flex-direction: column;\n height: 100%;\n width: 100%;\n background-color: #fff;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n}\n\n.file-grid-container {\n display: flex;\n flex-direction: column;\n height: 100%;\n width: 100%;\n position: relative;\n}\n\n/* ===========================\n Breadcrumb Navigation Bar\n =========================== */\n\n.breadcrumb-bar {\n display: flex;\n align-items: center;\n padding: 6px 10px;\n background-color: #f8f8f8;\n border-bottom: 1px solid #ccc;\n gap: 10px;\n flex-wrap: wrap;\n}\n\n.nav-buttons {\n display: flex;\n gap: 4px;\n}\n\n.nav-btn {\n background: #fff;\n border: 1px solid #ccc;\n padding: 4px 8px;\n cursor: pointer;\n border-radius: 3px;\n font-size: 12px;\n color: #333;\n transition: background-color 0.15s;\n}\n\n.nav-btn:hover:not(:disabled) {\n background-color: #e8e8e8;\n}\n\n.nav-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.breadcrumb-path {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n color: #333;\n}\n\n.breadcrumb-path i {\n color: #666;\n}\n\n.path-text {\n font-weight: 500;\n}\n\n.search-bar {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-left: auto;\n position: relative;\n}\n\n.search-input {\n padding: 4px 30px 4px 8px;\n border: 1px solid #ccc;\n border-radius: 3px;\n font-size: 12px;\n font-family: inherit;\n min-width: 200px;\n}\n\n.search-input:focus {\n outline: none;\n border-color: #007AFF;\n}\n\n.search-clear-btn {\n position: absolute;\n right: 142px;\n background: none;\n border: none;\n cursor: pointer;\n color: #999;\n padding: 4px;\n font-size: 12px;\n}\n\n.search-clear-btn:hover {\n color: #333;\n}\n\n.file-type-filter {\n padding: 4px 8px;\n border: 1px solid #ccc;\n border-radius: 3px;\n font-size: 12px;\n font-family: inherit;\n background: #fff;\n cursor: pointer;\n}\n\n.file-type-filter:focus {\n outline: none;\n border-color: #007AFF;\n}\n\n/* ===========================\n File Table\n =========================== */\n\n.file-table-wrapper {\n flex: 1;\n overflow: auto;\n border-bottom: 1px solid #ccc;\n}\n\n.file-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 12px;\n}\n\n.file-table thead {\n position: sticky;\n top: 0;\n background-color: #f0f0f0;\n z-index: 10;\n}\n\n.file-table th {\n text-align: left;\n padding: 6px 10px;\n border-bottom: 1px solid #ccc;\n font-weight: 600;\n color: #333;\n cursor: pointer;\n user-select: none;\n}\n\n.file-table th:hover {\n background-color: #e8e8e8;\n}\n\n.file-table th span {\n margin-right: 4px;\n}\n\n.sort-icon {\n font-size: 10px;\n color: #666;\n}\n\n.file-table tbody tr {\n border-bottom: 1px solid #e8e8e8;\n cursor: pointer;\n transition: background-color 0.1s;\n}\n\n.file-table tbody tr:hover {\n background-color: #f8f8f8;\n}\n\n.file-table tbody tr.selected {\n background-color: #cce8ff;\n}\n\n.file-table tbody tr.selected:hover {\n background-color: #b3d9ff;\n}\n\n.file-table td {\n padding: 6px 10px;\n color: #333;\n}\n\n/* Column Widths */\n.col-name {\n width: 50%;\n}\n\n.col-type {\n width: 15%;\n}\n\n.col-size {\n width: 15%;\n}\n\n.col-modified {\n width: 20%;\n}\n\n/* Name Column with Icon */\n.col-name {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.item-icon {\n font-size: 14px;\n color: #666;\n flex-shrink: 0;\n}\n\n.item-name {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* Folder Icons */\n.fa-folder {\n color: #FFB84D;\n}\n\n/* File Type Icons */\n.fa-file-pdf {\n color: #D32F2F;\n}\n\n.fa-file-word {\n color: #2B579A;\n}\n\n.fa-file-excel {\n color: #217346;\n}\n\n.fa-file-image {\n color: #673AB7;\n}\n\n.fa-file-video {\n color: #F57C00;\n}\n\n.fa-file-audio {\n color: #D32F2F;\n}\n\n.fa-file-archive {\n color: #795548;\n}\n\n.fa-file-code {\n color: #388E3C;\n}\n\n/* ===========================\n Grid View\n =========================== */\n\n.file-grid-wrapper {\n flex: 1;\n overflow: auto;\n padding: 10px;\n border-bottom: 1px solid #ccc;\n}\n\n.file-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));\n gap: 12px;\n}\n\n.grid-item {\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 12px 8px;\n border: 1px solid #e8e8e8;\n border-radius: 4px;\n cursor: pointer;\n transition: all 0.15s;\n background: #fff;\n}\n\n.grid-item:hover {\n background-color: #f8f8f8;\n border-color: #ccc;\n}\n\n.grid-item.selected {\n background-color: #cce8ff;\n border-color: #007AFF;\n}\n\n.grid-icon {\n font-size: 48px;\n margin-bottom: 8px;\n color: #666;\n}\n\n.grid-icon i {\n display: block;\n}\n\n.grid-name {\n font-size: 12px;\n color: #333;\n text-align: center;\n word-break: break-word;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n max-width: 100%;\n}\n\n.grid-meta {\n font-size: 11px;\n color: #666;\n margin-top: 4px;\n}\n\n/* ===========================\n Bottom Toolbar\n =========================== */\n\n.bottom-toolbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 6px 10px;\n background-color: #f8f8f8;\n border-top: 1px solid #ccc;\n min-height: 36px;\n}\n\n.toolbar-left {\n display: flex;\n gap: 6px;\n}\n\n.toolbar-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n background: #fff;\n border: 1px solid #ccc;\n padding: 5px 12px;\n cursor: pointer;\n border-radius: 3px;\n font-size: 12px;\n color: #333;\n transition: background-color 0.15s;\n}\n\n.toolbar-btn:hover:not(:disabled) {\n background-color: #e8e8e8;\n}\n\n.toolbar-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.toolbar-btn i {\n font-size: 12px;\n}\n\n.toolbar-right {\n display: flex;\n align-items: center;\n gap: 12px;\n}\n\n.view-toggle {\n display: flex;\n border: 1px solid #ccc;\n border-radius: 3px;\n overflow: hidden;\n}\n\n.view-btn {\n background: #fff;\n border: none;\n border-right: 1px solid #ccc;\n padding: 4px 10px;\n cursor: pointer;\n font-size: 12px;\n color: #666;\n transition: all 0.15s;\n}\n\n.view-btn:last-child {\n border-right: none;\n}\n\n.view-btn:hover {\n background-color: #f0f0f0;\n color: #333;\n}\n\n.view-btn.active {\n background-color: #007AFF;\n color: #fff;\n}\n\n.item-count {\n font-size: 11px;\n color: #666;\n}\n\n/* ===========================\n Loading / Error / Empty States\n =========================== */\n\n.loading-state,\n.error-state,\n.empty-state {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 40px 20px;\n}\n\n.error-icon,\n.empty-icon {\n font-size: 48px;\n color: #999;\n margin-bottom: 16px;\n}\n\n.error-message,\n.empty-message {\n font-size: 14px;\n color: #666;\n margin-bottom: 16px;\n}\n\n.retry-btn,\n.upload-btn {\n display: flex;\n align-items: center;\n gap: 8px;\n background: #fff;\n border: 1px solid #ccc;\n padding: 8px 16px;\n cursor: pointer;\n border-radius: 3px;\n font-size: 13px;\n color: #333;\n transition: background-color 0.15s;\n}\n\n.retry-btn:hover,\n.upload-btn:hover {\n background-color: #f0f0f0;\n}\n\n/* ===========================\n Drag & Drop Overlay\n =========================== */\n\n.drag-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: rgba(255, 255, 255, 0.95);\n z-index: 1000;\n display: flex;\n align-items: center;\n justify-content: center;\n border: 3px dashed #007AFF;\n}\n\n.drag-content {\n text-align: center;\n}\n\n.drag-icon {\n font-size: 64px;\n color: #007AFF;\n margin-bottom: 16px;\n}\n\n.drag-title {\n font-size: 20px;\n font-weight: 600;\n color: #007AFF;\n margin: 0;\n}\n\n/* ===========================\n Upload Progress\n =========================== */\n\n.upload-progress {\n position: absolute;\n bottom: 40px;\n right: 20px;\n background: #fff;\n border: 1px solid #ccc;\n border-radius: 4px;\n padding: 12px 16px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\n min-width: 300px;\n z-index: 100;\n}\n\n.progress-content {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.progress-info {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 13px;\n color: #333;\n}\n\n.progress-bar-wrap {\n width: 100%;\n height: 6px;\n background-color: #e0e0e0;\n border-radius: 3px;\n overflow: hidden;\n}\n\n.progress-bar {\n height: 100%;\n background-color: #007AFF;\n transition: width 0.3s ease;\n}\n\n.progress-percent {\n font-size: 12px;\n color: #666;\n text-align: right;\n}\n\n/* ===========================\n Modal Dialogs\n =========================== */\n\n.modal-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: rgba(0, 0, 0, 0.5);\n z-index: 2000;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.modal-dialog {\n background: #fff;\n border: 1px solid #999;\n border-radius: 4px;\n min-width: 400px;\n max-width: 90%;\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);\n}\n\n.modal-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 16px;\n background-color: #f8f8f8;\n border-bottom: 1px solid #ccc;\n}\n\n.modal-header h3 {\n margin: 0;\n font-size: 14px;\n font-weight: 600;\n color: #333;\n}\n\n.modal-close {\n background: none;\n border: none;\n cursor: pointer;\n font-size: 16px;\n color: #666;\n padding: 4px;\n}\n\n.modal-close:hover {\n color: #333;\n}\n\n.modal-body {\n padding: 16px;\n}\n\n.modal-body label {\n display: block;\n margin-bottom: 6px;\n font-size: 12px;\n font-weight: 600;\n color: #333;\n}\n\n.modal-body input[type=\"text\"] {\n width: 100%;\n padding: 6px 10px;\n border: 1px solid #ccc;\n border-radius: 3px;\n font-size: 13px;\n font-family: inherit;\n}\n\n.modal-body input[type=\"text\"]:focus {\n outline: none;\n border-color: #007AFF;\n}\n\n.modal-body select,\n.modal-body .provider-select {\n width: 100%;\n padding: 6px 10px;\n border: 1px solid #ccc;\n border-radius: 3px;\n font-size: 13px;\n font-family: inherit;\n background-color: #fff;\n cursor: pointer;\n}\n\n.modal-body select:focus,\n.modal-body .provider-select:focus {\n outline: none;\n border-color: #007AFF;\n}\n\n.modal-body select:disabled,\n.modal-body .provider-select:disabled {\n background-color: #f5f5f5;\n cursor: not-allowed;\n}\n\n.modal-body p {\n margin: 0 0 12px 0;\n font-size: 13px;\n color: #333;\n}\n\n.modal-body .warning {\n color: #D32F2F;\n font-size: 12px;\n}\n\n/* Checkbox list for multi-account selection */\n.account-checkbox-list {\n max-height: 200px;\n overflow-y: auto;\n border: 1px solid #ccc;\n border-radius: 4px;\n padding: 8px;\n background: #fafafa;\n}\n\n.account-checkbox-item {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 4px;\n border-radius: 3px;\n cursor: pointer;\n}\n\n.account-checkbox-item:hover {\n background-color: #e8f4fd;\n}\n\n.account-checkbox-item input[type=\"checkbox\"] {\n width: 16px;\n height: 16px;\n cursor: pointer;\n accent-color: #007AFF;\n}\n\n.account-checkbox-item label {\n display: flex;\n align-items: center;\n gap: 8px;\n cursor: pointer;\n font-size: 13px;\n font-weight: normal;\n margin: 0;\n}\n\n.provider-badge {\n font-size: 11px;\n color: #666;\n background: #e0e0e0;\n padding: 2px 6px;\n border-radius: 10px;\n}\n\n.copy-progress {\n margin-top: 12px;\n padding: 8px 12px;\n background: #e8f4fd;\n border-radius: 4px;\n font-size: 12px;\n color: #0066cc;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.modal-footer {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n padding: 12px 16px;\n background-color: #f8f8f8;\n border-top: 1px solid #ccc;\n}\n\n.modal-footer button {\n padding: 6px 16px;\n border: 1px solid #ccc;\n background: #fff;\n cursor: pointer;\n border-radius: 3px;\n font-size: 12px;\n font-weight: 500;\n transition: background-color 0.15s;\n}\n\n.modal-footer button:hover:not(:disabled) {\n background-color: #f0f0f0;\n}\n\n.modal-footer button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.modal-footer button.danger {\n background-color: #D32F2F;\n color: #fff;\n border-color: #D32F2F;\n}\n\n.modal-footer button.danger:hover:not(:disabled) {\n background-color: #B71C1C;\n}\n\n/* ===========================\n Scrollbars\n =========================== */\n\n.file-table-wrapper::-webkit-scrollbar {\n width: 12px;\n height: 12px;\n}\n\n.file-table-wrapper::-webkit-scrollbar-track {\n background: #f0f0f0;\n}\n\n.file-table-wrapper::-webkit-scrollbar-thumb {\n background: #c0c0c0;\n border: 2px solid #f0f0f0;\n}\n\n.file-table-wrapper::-webkit-scrollbar-thumb:hover {\n background: #a0a0a0;\n}\n\n/* ===========================\n Responsive Design\n =========================== */\n\n@media screen and (max-width: 768px) {\n .file-table {\n font-size: 11px;\n }\n\n .file-table th,\n .file-table td {\n padding: 4px 6px;\n }\n\n .col-type,\n .col-size {\n display: none;\n }\n\n .col-name {\n width: 60%;\n }\n\n .col-modified {\n width: 40%;\n }\n\n .toolbar-btn span {\n display: none;\n }\n\n .modal-dialog {\n min-width: 90%;\n }\n}\n\n/* ===========================\n Multi-Provider Search\n =========================== */\n\n.multi-search-btn {\n background: #fff;\n border: 1px solid #ccc;\n padding: 4px 8px;\n cursor: pointer;\n border-radius: 3px;\n font-size: 12px;\n color: #666;\n transition: all 0.15s;\n}\n\n.multi-search-btn:hover {\n background-color: #e8e8e8;\n color: #333;\n}\n\n.multi-search-btn.active {\n background-color: #007AFF;\n color: #fff;\n border-color: #007AFF;\n}\n\n.multi-provider-search-panel {\n background: #fff;\n border-bottom: 1px solid #ccc;\n display: flex;\n flex-direction: column;\n max-height: 70vh;\n overflow: hidden;\n}\n\n.search-panel-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 16px;\n background-color: #f0f7ff;\n border-bottom: 1px solid #cce8ff;\n}\n\n.search-panel-header h3 {\n margin: 0;\n font-size: 14px;\n font-weight: 600;\n color: #007AFF;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.close-panel-btn {\n background: none;\n border: none;\n cursor: pointer;\n font-size: 16px;\n color: #666;\n padding: 4px;\n}\n\n.close-panel-btn:hover {\n color: #333;\n}\n\n.search-panel-body {\n padding: 16px;\n}\n\n.search-input-row {\n display: flex;\n gap: 8px;\n margin-bottom: 16px;\n}\n\n.multi-search-input {\n flex: 1;\n padding: 8px 12px;\n border: 1px solid #ccc;\n border-radius: 4px;\n font-size: 13px;\n font-family: inherit;\n}\n\n.multi-search-input:focus {\n outline: none;\n border-color: #007AFF;\n}\n\n.search-execute-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n background: #007AFF;\n color: #fff;\n border: none;\n padding: 8px 16px;\n cursor: pointer;\n border-radius: 4px;\n font-size: 13px;\n font-weight: 500;\n transition: background-color 0.15s;\n}\n\n.search-execute-btn:hover:not(:disabled) {\n background-color: #0056b3;\n}\n\n.search-execute-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.provider-selection {\n margin-bottom: 8px;\n}\n\n.provider-selection-label {\n display: block;\n font-size: 12px;\n font-weight: 600;\n color: #333;\n margin-bottom: 8px;\n}\n\n.provider-checkboxes {\n display: flex;\n flex-wrap: wrap;\n gap: 12px;\n}\n\n.provider-checkbox {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 13px;\n color: #333;\n cursor: pointer;\n padding: 4px 8px;\n border: 1px solid #e0e0e0;\n border-radius: 4px;\n background: #fafafa;\n transition: all 0.15s;\n}\n\n.provider-checkbox:hover:not(.disabled) {\n border-color: #007AFF;\n background: #f0f7ff;\n}\n\n.provider-checkbox.disabled {\n opacity: 0.6;\n cursor: not-allowed;\n background: #f5f5f5;\n}\n\n.provider-checkbox input[type=\"checkbox\"] {\n cursor: pointer;\n}\n\n.provider-checkbox .provider-name {\n font-weight: 500;\n}\n\n.no-search-badge {\n font-size: 10px;\n color: #999;\n font-style: italic;\n}\n\n/* Search Results */\n.search-results-container {\n flex: 1;\n overflow: auto;\n border-top: 1px solid #e0e0e0;\n}\n\n.search-results-summary {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 10px 16px;\n background: #f8f8f8;\n border-bottom: 1px solid #e0e0e0;\n}\n\n.search-results-summary .result-count {\n font-weight: 600;\n font-size: 13px;\n color: #333;\n}\n\n.search-results-summary .provider-stats {\n font-size: 12px;\n color: #666;\n}\n\n.search-results-summary .failed-count {\n color: #D32F2F;\n}\n\n.clear-results-btn {\n margin-left: auto;\n background: none;\n border: 1px solid #ccc;\n padding: 4px 10px;\n cursor: pointer;\n border-radius: 3px;\n font-size: 11px;\n color: #666;\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.clear-results-btn:hover {\n background: #f0f0f0;\n color: #333;\n}\n\n.provider-results-list {\n padding: 8px 0;\n}\n\n.provider-result-group {\n margin-bottom: 16px;\n}\n\n.provider-result-group:last-child {\n margin-bottom: 0;\n}\n\n.provider-result-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 16px;\n background: #f5f5f5;\n font-size: 13px;\n font-weight: 600;\n color: #333;\n}\n\n.provider-result-header i {\n color: #007AFF;\n}\n\n.provider-result-header .result-count {\n font-weight: normal;\n color: #666;\n}\n\n.provider-result-group.failed .provider-result-header {\n background: #fff5f5;\n}\n\n.provider-result-group.failed .provider-result-header i {\n color: #D32F2F;\n}\n\n.error-badge {\n color: #D32F2F;\n font-weight: normal;\n font-size: 12px;\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.provider-result-files {\n padding: 0;\n}\n\n.search-result-item {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 8px 16px 8px 40px;\n border-bottom: 1px solid #f0f0f0;\n cursor: pointer;\n transition: background-color 0.1s;\n}\n\n.search-result-item:hover {\n background-color: #f8f8f8;\n}\n\n.search-result-item:last-child {\n border-bottom: none;\n}\n\n.search-result-item .file-icon {\n font-size: 16px;\n color: #666;\n flex-shrink: 0;\n}\n\n.search-result-item .file-info {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.search-result-item .file-name {\n font-size: 13px;\n font-weight: 500;\n color: #333;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.search-result-item .file-path {\n font-size: 11px;\n color: #999;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.search-result-item .file-size {\n font-size: 12px;\n color: #666;\n flex-shrink: 0;\n min-width: 60px;\n text-align: right;\n}\n\n.search-result-item .file-date {\n font-size: 12px;\n color: #666;\n flex-shrink: 0;\n min-width: 140px;\n text-align: right;\n}\n\n.no-results-message {\n padding: 16px 40px;\n font-size: 12px;\n color: #999;\n font-style: italic;\n}\n\n.searching-state {\n padding: 40px 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n"] }]
|
|
2384
|
+
}], () => [], { account: [{
|
|
2385
|
+
type: Input
|
|
2386
|
+
}], folderPath: [{
|
|
2387
|
+
type: Input
|
|
2388
|
+
}], folderNavigate: [{
|
|
2389
|
+
type: Output
|
|
2390
|
+
}], folderStructureChanged: [{
|
|
2391
|
+
type: Output
|
|
2392
|
+
}] }); })();
|
|
2393
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(FileGridComponent, { className: "FileGridComponent", filePath: "src/lib/file-browser/file-grid.component.ts", lineNumber: 67 }); })();
|
|
2394
|
+
//# sourceMappingURL=file-grid.component.js.map
|