@api-client/ui 0.0.10 → 0.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (176) hide show
  1. package/.eslintrc +16 -1
  2. package/demo/elements/index.html +3 -0
  3. package/demo/elements/store/file-picker.html +15 -0
  4. package/demo/elements/store/file-picker.ts +134 -0
  5. package/demo/elements/store/index.html +19 -0
  6. package/demo/index.html +3 -0
  7. package/demo/layout/index.html +91 -0
  8. package/demo/layout/index.ts +182 -0
  9. package/demo/store/StorePlugin.js +1 -0
  10. package/dist/bindings/base/StoreBindings.d.ts +5 -0
  11. package/dist/bindings/base/StoreBindings.d.ts.map +1 -1
  12. package/dist/bindings/base/StoreBindings.js +15 -1
  13. package/dist/bindings/base/StoreBindings.js.map +1 -1
  14. package/dist/define/store/file-picker.d.ts +9 -0
  15. package/dist/define/store/file-picker.d.ts.map +1 -0
  16. package/dist/define/store/file-picker.js +10 -0
  17. package/dist/define/store/file-picker.js.map +1 -0
  18. package/dist/define/{files → store}/share-file.d.ts +1 -1
  19. package/dist/define/store/share-file.d.ts.map +1 -0
  20. package/dist/define/{files → store}/share-file.js +2 -2
  21. package/dist/define/store/share-file.js.map +1 -0
  22. package/dist/elements/layout/SplitItem.d.ts +1 -9
  23. package/dist/elements/layout/SplitItem.d.ts.map +1 -1
  24. package/dist/elements/layout/SplitItem.js +27 -20
  25. package/dist/elements/layout/SplitItem.js.map +1 -1
  26. package/dist/elements/layout/SplitLayout.d.ts +16 -14
  27. package/dist/elements/layout/SplitLayout.d.ts.map +1 -1
  28. package/dist/elements/layout/SplitLayout.js +47 -42
  29. package/dist/elements/layout/SplitLayout.js.map +1 -1
  30. package/dist/elements/layout/SplitPanel.d.ts +7 -2
  31. package/dist/elements/layout/SplitPanel.d.ts.map +1 -1
  32. package/dist/elements/layout/SplitPanel.js +130 -52
  33. package/dist/elements/layout/SplitPanel.js.map +1 -1
  34. package/dist/elements/layout/SplitView.d.ts.map +1 -1
  35. package/dist/elements/layout/SplitView.js +18 -14
  36. package/dist/elements/layout/SplitView.js.map +1 -1
  37. package/dist/elements/layout/type.d.ts +3 -3
  38. package/dist/elements/layout/type.d.ts.map +1 -1
  39. package/dist/elements/layout/type.js.map +1 -1
  40. package/dist/elements/store/FilePicker.element.d.ts +87 -0
  41. package/dist/elements/store/FilePicker.element.d.ts.map +1 -0
  42. package/dist/elements/store/FilePicker.element.js +263 -0
  43. package/dist/elements/store/FilePicker.element.js.map +1 -0
  44. package/dist/elements/store/FilePicker.styles.d.ts +3 -0
  45. package/dist/elements/store/FilePicker.styles.d.ts.map +1 -0
  46. package/dist/elements/store/FilePicker.styles.js +72 -0
  47. package/dist/elements/store/FilePicker.styles.js.map +1 -0
  48. package/dist/elements/store/FilesLib.d.ts +10 -0
  49. package/dist/elements/store/FilesLib.d.ts.map +1 -0
  50. package/dist/elements/store/FilesLib.js +38 -0
  51. package/dist/elements/store/FilesLib.js.map +1 -0
  52. package/dist/elements/{files/ShareFile.d.ts → store/ShareFile.element.d.ts} +1 -1
  53. package/dist/elements/store/ShareFile.element.d.ts.map +1 -0
  54. package/dist/elements/{files/ShareFile.js → store/ShareFile.element.js} +1 -1
  55. package/dist/elements/store/ShareFile.element.js.map +1 -0
  56. package/dist/elements/store/ShareFile.styles.d.ts.map +1 -0
  57. package/dist/elements/{files → store}/ShareFile.styles.js.map +1 -1
  58. package/dist/events/EventTypes.d.ts +1 -0
  59. package/dist/events/EventTypes.d.ts.map +1 -1
  60. package/dist/events/EventTypes.js +1 -0
  61. package/dist/events/EventTypes.js.map +1 -1
  62. package/dist/events/Events.d.ts +1 -0
  63. package/dist/events/Events.d.ts.map +1 -1
  64. package/dist/events/StoreEvents.d.ts +8 -1
  65. package/dist/events/StoreEvents.d.ts.map +1 -1
  66. package/dist/events/StoreEvents.js +19 -0
  67. package/dist/events/StoreEvents.js.map +1 -1
  68. package/dist/pages/ApplicationScreen.d.ts +1 -1
  69. package/dist/pages/ApplicationScreen.d.ts.map +1 -1
  70. package/dist/pages/ApplicationScreen.js +4 -2
  71. package/dist/pages/ApplicationScreen.js.map +1 -1
  72. package/dist/pages/api-client/ApiClient.screen.d.ts +0 -6
  73. package/dist/pages/api-client/ApiClient.screen.d.ts.map +1 -1
  74. package/dist/pages/api-client/ApiClient.screen.js +16 -29
  75. package/dist/pages/api-client/ApiClient.screen.js.map +1 -1
  76. package/dist/pages/api-client/Authenticate.screen.d.ts +1 -1
  77. package/dist/pages/api-client/Authenticate.screen.d.ts.map +1 -1
  78. package/dist/pages/api-client/Authenticate.screen.js +2 -2
  79. package/dist/pages/api-client/Authenticate.screen.js.map +1 -1
  80. package/dist/pages/api-client/pages/Files.page.d.ts +6 -35
  81. package/dist/pages/api-client/pages/Files.page.d.ts.map +1 -1
  82. package/dist/pages/api-client/pages/Files.page.js +32 -141
  83. package/dist/pages/api-client/pages/Files.page.js.map +1 -1
  84. package/dist/pages/api-client/pages/Shared.page.d.ts +1 -5
  85. package/dist/pages/api-client/pages/Shared.page.d.ts.map +1 -1
  86. package/dist/pages/api-client/pages/Shared.page.js +1 -40
  87. package/dist/pages/api-client/pages/Shared.page.js.map +1 -1
  88. package/dist/pages/demo/DemoPage.d.ts +7 -0
  89. package/dist/pages/demo/DemoPage.d.ts.map +1 -1
  90. package/dist/pages/demo/DemoPage.js +14 -0
  91. package/dist/pages/demo/DemoPage.js.map +1 -1
  92. package/dist/pages/http-project/HttpClientCommands.d.ts.map +1 -1
  93. package/dist/pages/http-project/HttpClientCommands.js +28 -12
  94. package/dist/pages/http-project/HttpClientCommands.js.map +1 -1
  95. package/dist/store/FileSystem.d.ts +90 -0
  96. package/dist/store/FileSystem.d.ts.map +1 -0
  97. package/dist/store/FileSystem.js +260 -0
  98. package/dist/store/FileSystem.js.map +1 -0
  99. package/dist/styles/global-styles.d.ts.map +1 -1
  100. package/dist/styles/global-styles.js +7 -0
  101. package/dist/styles/global-styles.js.map +1 -1
  102. package/dist/ui/icons/Icons.d.ts +2 -1
  103. package/dist/ui/icons/Icons.d.ts.map +1 -1
  104. package/dist/ui/icons/Icons.js +1 -0
  105. package/dist/ui/icons/Icons.js.map +1 -1
  106. package/dist/ui/list/UiDropdownList.d.ts +9 -1
  107. package/dist/ui/list/UiDropdownList.d.ts.map +1 -1
  108. package/dist/ui/list/UiDropdownList.js +39 -17
  109. package/dist/ui/list/UiDropdownList.js.map +1 -1
  110. package/dist/ui/list/UiList.d.ts +6 -1
  111. package/dist/ui/list/UiList.d.ts.map +1 -1
  112. package/dist/ui/list/UiList.js +24 -9
  113. package/dist/ui/list/UiList.js.map +1 -1
  114. package/dist/ui/table/DataTable.d.ts +4 -0
  115. package/dist/ui/table/DataTable.d.ts.map +1 -1
  116. package/dist/ui/table/DataTable.js +23 -1
  117. package/dist/ui/table/DataTable.js.map +1 -1
  118. package/package.json +2 -1
  119. package/src/bindings/base/StoreBindings.ts +16 -1
  120. package/src/define/store/file-picker.ts +12 -0
  121. package/src/define/{files → store}/share-file.ts +2 -2
  122. package/src/elements/layout/SplitItem.ts +29 -21
  123. package/src/elements/layout/SplitLayout.ts +53 -43
  124. package/src/elements/layout/SplitPanel.ts +140 -57
  125. package/src/elements/layout/SplitView.ts +18 -15
  126. package/src/elements/layout/type.ts +3 -4
  127. package/src/elements/store/FilePicker.element.ts +297 -0
  128. package/src/elements/store/FilePicker.styles.ts +72 -0
  129. package/src/elements/store/FilesLib.ts +32 -0
  130. package/src/events/EventTypes.ts +1 -0
  131. package/src/events/StoreEvents.ts +21 -1
  132. package/src/pages/ApplicationScreen.ts +5 -3
  133. package/src/pages/api-client/ApiClient.screen.ts +16 -31
  134. package/src/pages/api-client/Authenticate.screen.ts +2 -2
  135. package/src/pages/api-client/pages/Files.page.ts +37 -164
  136. package/src/pages/api-client/pages/Shared.page.ts +2 -40
  137. package/src/pages/demo/DemoPage.ts +17 -0
  138. package/src/pages/http-project/HttpClientCommands.ts +28 -12
  139. package/src/store/FileSystem.ts +325 -0
  140. package/src/styles/global-styles.ts +7 -0
  141. package/src/ui/icons/Icons.ts +2 -1
  142. package/src/ui/list/UiDropdownList.ts +44 -17
  143. package/src/ui/list/UiList.ts +26 -10
  144. package/src/ui/table/DataTable.ts +29 -3
  145. package/test/elements/layout/SplitItem.test.ts +76 -75
  146. package/test/elements/layout/SplitLayoutManager.test.ts +70 -69
  147. package/test/elements/layout/SplitPanel.test.ts +10 -7
  148. package/test/elements/store/FilePicker.test.ts +241 -0
  149. package/test/env.js +3 -0
  150. package/test/helpers/StoreHelper.ts +390 -0
  151. package/tsconfig.eslint.json +10 -0
  152. package/web-test-runner.config.mjs +51 -2
  153. package/dist/define/files/share-file.d.ts.map +0 -1
  154. package/dist/define/files/share-file.js.map +0 -1
  155. package/dist/define/layout/layout-panel.d.ts +0 -7
  156. package/dist/define/layout/layout-panel.d.ts.map +0 -1
  157. package/dist/define/layout/layout-panel.js +0 -3
  158. package/dist/define/layout/layout-panel.js.map +0 -1
  159. package/dist/elements/files/ShareFile.d.ts.map +0 -1
  160. package/dist/elements/files/ShareFile.js.map +0 -1
  161. package/dist/elements/files/ShareFile.styles.d.ts.map +0 -1
  162. package/dist/elements/layout/LayoutManager.d.ts +0 -327
  163. package/dist/elements/layout/LayoutManager.d.ts.map +0 -1
  164. package/dist/elements/layout/LayoutManager.js +0 -747
  165. package/dist/elements/layout/LayoutManager.js.map +0 -1
  166. package/dist/elements/layout/LayoutPanelElement.d.ts +0 -62
  167. package/dist/elements/layout/LayoutPanelElement.d.ts.map +0 -1
  168. package/dist/elements/layout/LayoutPanelElement.js +0 -628
  169. package/dist/elements/layout/LayoutPanelElement.js.map +0 -1
  170. package/src/define/layout/layout-panel.ts +0 -9
  171. package/src/elements/layout/LayoutManager.ts +0 -930
  172. package/src/elements/layout/LayoutPanelElement.ts +0 -651
  173. /package/dist/elements/{files → store}/ShareFile.styles.d.ts +0 -0
  174. /package/dist/elements/{files → store}/ShareFile.styles.js +0 -0
  175. /package/src/elements/{files/ShareFile.ts → store/ShareFile.element.ts} +0 -0
  176. /package/src/elements/{files → store}/ShareFile.styles.ts +0 -0
@@ -0,0 +1,72 @@
1
+ import { css } from "lit";
2
+ import typography from '../../styles/m3/typography.module.js';
3
+
4
+ export default [typography, css`
5
+ :host {
6
+ display: block;
7
+
8
+ width: 420px;
9
+ height: 400px;
10
+
11
+ background-color: var(--md-sys-color-background);
12
+ color: var(--md-sys-color-on-background);
13
+ }
14
+
15
+ .content {
16
+ height: 100%;
17
+ display: flex;
18
+ flex-direction: column;
19
+ }
20
+
21
+ .header {
22
+ height: 56px;
23
+ display: flex;
24
+ align-items: center;
25
+ position: relative;
26
+ margin: 0 16px;
27
+ }
28
+
29
+ .header.withIcon {
30
+ margin: 0 16px 0 4px;
31
+ }
32
+
33
+ .header .label {
34
+ display: block;
35
+ }
36
+
37
+ .progress {
38
+ position: absolute;
39
+ bottom: -10px; /* aligns with the divider */
40
+ width: 100%;
41
+ }
42
+
43
+ .list {
44
+ overflow-y: auto;
45
+ flex: 1;
46
+ margin: 0;
47
+ padding: 0;
48
+ }
49
+
50
+ .actions {
51
+ display: flex;
52
+ align-items: center;
53
+ justify-content: flex-end;
54
+ margin: 0 16px;
55
+ }
56
+
57
+ .file-icon {
58
+ margin-right: 12px;
59
+ }
60
+
61
+ .error {
62
+ background-color: var(--md-sys-color-error-container);
63
+ color: var(--md-sys-color-on-error-container);
64
+ display: block;
65
+ padding: 8px 12px;
66
+ border-radius: var(--md-sys-shape-corner-small);
67
+ }
68
+
69
+ .no-files {
70
+ flex: 1;
71
+ }
72
+ `];
@@ -0,0 +1,32 @@
1
+ import { CertificateFileKind, DataFileKind, FolderKind, IFile, ProjectKind } from "@api-client/core/build/browser.js";
2
+ import { IconType } from "../../ui/icons/Icons.js";
3
+
4
+ /**
5
+ * Sorts files on the list.
6
+ *
7
+ * Folders are always on top.
8
+ */
9
+ export function filesSortFunction(a: IFile, b: IFile): number {
10
+ if (a.kind !== b.kind) {
11
+ if (a.kind === FolderKind) {
12
+ return -1;
13
+ }
14
+ return 1;
15
+ }
16
+ if (a.kind === b.kind && a.kind === FolderKind) {
17
+ return (a.info.name || '').localeCompare(b.info.name || '');
18
+ }
19
+ return b.lastModified.time - a.lastModified.time;
20
+ }
21
+
22
+ export function fileIcon(item: IFile): IconType | undefined {
23
+ let icon: IconType | undefined;
24
+ switch (item.kind) {
25
+ case DataFileKind: icon = 'schema'; break;
26
+ case FolderKind: icon = 'folder'; break;
27
+ case ProjectKind: icon = 'collectionsBookmark'; break;
28
+ case CertificateFileKind: icon = 'certificate'; break;
29
+ default:
30
+ }
31
+ return icon;
32
+ }
@@ -21,6 +21,7 @@ export const EventTypes = Object.freeze({
21
21
  delete: 'storefilesdelete',
22
22
  patchUsers: 'storefilespatchusers',
23
23
  listUsers: 'storefileslistusers',
24
+ breadcrumbs: 'storefilesbreadcrumbs',
24
25
  }),
25
26
  User: Object.freeze({
26
27
  me: 'storeuserme',
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  IBackendInfo, IFile, IFileCreateOptions,
4
4
  ListFileKind, AccessOperation, IUser, IPatchRevision, IApplication, IHttpHistory, IHttpHistoryBulkAdd, HistoryListOptions,
5
- ContextChangeRecord, ContextDeleteEvent, ContextDeleteBulkEvent, ContextListOptions, ContextListResult, IProjectExecution, IProjectExecutionListOptions, IBulkOperationResult,
5
+ ContextChangeRecord, ContextDeleteEvent, ContextDeleteBulkEvent, ContextListOptions, ContextListResult, IProjectExecution, IProjectExecutionListOptions, IBulkOperationResult, FileBreadcrumb,
6
6
  } from '@api-client/core/build/browser.js';
7
7
  import { Patch } from '@api-client/json';
8
8
  import { IConfigEnvironment, IConfigInit } from '../lib/config/Config.js';
@@ -371,6 +371,26 @@ export const StoreEvents = Object.freeze({
371
371
  target.dispatchEvent(e);
372
372
  return ((e.detail.result as unknown) as Promise<ContextListResult<IUser>>);
373
373
  },
374
+
375
+ /**
376
+ * Lists breadcrumbs to the file.
377
+ *
378
+ * @param fileOrFolder The if of the file or folder to query for.
379
+ * @param options Optional query options.
380
+ */
381
+ breadcrumbs: async (fileOrFolder: string, target: EventTarget = document.body): Promise<ContextListResult<FileBreadcrumb>> => {
382
+ const e = new CustomEvent(EventTypes.Store.File.breadcrumbs, {
383
+ bubbles: true,
384
+ cancelable: true,
385
+ composed: true,
386
+ detail: {
387
+ key: fileOrFolder,
388
+ result: undefined,
389
+ },
390
+ });
391
+ target.dispatchEvent(e);
392
+ return ((e.detail.result as unknown) as Promise<ContextListResult<FileBreadcrumb>>);
393
+ },
374
394
  }),
375
395
  /**
376
396
  * Reads the current user.
@@ -144,7 +144,9 @@ export abstract class ApplicationScreen extends RouteMixin(RenderableMixin(Event
144
144
  window.onunhandledrejection = this.unhandledRejectionHandler.bind(this);
145
145
  this.initMediaQueries();
146
146
  this.initStoreChange();
147
- this.configBroadcast.addEventListener('message', this.handleConfigBroadcast.bind(this))
147
+ this.configBroadcast.addEventListener('message', this.handleConfigBroadcast.bind(this));
148
+ // this will use the system-default theme until config is read.
149
+ this.themeActiveCallback();
148
150
  }
149
151
 
150
152
  /**
@@ -325,7 +327,7 @@ export abstract class ApplicationScreen extends RouteMixin(RenderableMixin(Event
325
327
  * Called from the initialize store init flow when the user is not authenticated
326
328
  * and the auth screen is rendered.
327
329
  */
328
- protected async _authHandler(): Promise<void> {
330
+ protected async handleAuthenticate(): Promise<void> {
329
331
  const { store } = this.bindings;
330
332
  await store.auth.authenticate({ updateEnvironment: true });
331
333
  this.authenticated = true;
@@ -509,7 +511,7 @@ export abstract class ApplicationScreen extends RouteMixin(RenderableMixin(Event
509
511
  <div class="auth-required-screen">
510
512
  <h1 class="display-large">Authentication required</h1>
511
513
  <p class="body-large">You are not authenticated. To continue, please, authenticate your account.</p>
512
- <ui-button type="tonal" @click="${this._authHandler}">Authenticate</ui-button>
514
+ <ui-button type="tonal" @click="${this.handleAuthenticate}">Authenticate</ui-button>
513
515
  </div>
514
516
  `;
515
517
  }
@@ -99,8 +99,6 @@ export default class ApiClientScreen extends ApplicationScreen {
99
99
 
100
100
  protected settings = new SettingsPage(this);
101
101
 
102
- filesChannel = new BroadcastChannel(StoreBroadcast.files);
103
-
104
102
  spacesChannel = new BroadcastChannel(StoreBroadcast.spaces);
105
103
 
106
104
  trashChannel = new BroadcastChannel(StoreBroadcast.trash);
@@ -136,7 +134,6 @@ export default class ApiClientScreen extends ApplicationScreen {
136
134
  await this.bindings.store.observeTrash();
137
135
  }
138
136
  await this.loadUser();
139
- this.filesChannel.addEventListener('message', this.fileMetaMessageHandler.bind(this));
140
137
  this.spacesChannel.addEventListener('message', this.spacesMessageHandler.bind(this));
141
138
  this.trashChannel.addEventListener('message', this.trashMessageHandler.bind(this));
142
139
  this.initializeRouting();
@@ -146,8 +143,8 @@ export default class ApiClientScreen extends ApplicationScreen {
146
143
  reset(): void {
147
144
  this.spaces = [];
148
145
  this.spacesCursor = undefined;
149
- this.files.resetList();
150
- this.shared.resetList();
146
+ this.files.fs.resetList();
147
+ this.shared.fs.resetList();
151
148
  this.trash.resetList();
152
149
  }
153
150
 
@@ -249,16 +246,16 @@ export default class ApiClientScreen extends ApplicationScreen {
249
246
  this.bindings.store.space = this.selectedSpace;
250
247
 
251
248
  this.render();
252
- this.files.resetList();
253
- this.shared.resetList();
249
+ this.files.fs.resetList();
250
+ this.shared.fs.resetList();
254
251
  this.trash.resetList();
255
252
  if (this.page === 'files') {
256
- this.files.readFiles();
253
+ this.files.fs.debounceQuery();
257
254
  navigate('files'); // remove any folder info
258
255
  } else if (this.page === 'trash') {
259
256
  this.trash.readFiles();
260
257
  } else if (this.page === 'shared') {
261
- this.shared.readFiles();
258
+ this.shared.fs.debounceQuery();
262
259
  }
263
260
  this.storeLastSpaceKey(this.selectedSpace);
264
261
  }
@@ -279,18 +276,6 @@ export default class ApiClientScreen extends ApplicationScreen {
279
276
  this.spacesCursor = cursor;
280
277
  }
281
278
 
282
- /**
283
- * A handler for the broadcast channel message which informs about changes to files meta.
284
- * The function distributes changes between pages
285
- */
286
- protected fileMetaMessageHandler(e: MessageEvent): void {
287
- const event = e.data as BroadcastEvent;
288
- if (event.space === this.selectedSpace) {
289
- this.files.handleFileEvent(event);
290
- }
291
- this.shared.handleFileEvent(event);
292
- }
293
-
294
279
  protected trashMessageHandler(e: MessageEvent): void {
295
280
  const event = e.data as BroadcastEvent;
296
281
  if (event.space !== this.selectedSpace) {
@@ -343,13 +328,13 @@ export default class ApiClientScreen extends ApplicationScreen {
343
328
  this.resetRoute();
344
329
  this.page = 'files';
345
330
  const folder = info.params && info.params.key as string | undefined;
346
- const folderChanged = this.files.folder !== folder;
331
+ const folderChanged = this.files.fs.parent !== folder;
347
332
  if (folderChanged) {
348
- this.files.folder = folder;
349
- this.files.resetList();
333
+ this.files.fs.parent = folder;
334
+ this.files.fs.resetList();
350
335
  }
351
- if (!this.files.files.length || folderChanged) {
352
- this.files.readFiles();
336
+ if (!this.files.fs.files.length || folderChanged) {
337
+ this.files.fs.debounceQuery();
353
338
  }
354
339
  }
355
340
 
@@ -357,13 +342,13 @@ export default class ApiClientScreen extends ApplicationScreen {
357
342
  this.resetRoute();
358
343
  this.page = 'shared';
359
344
  const folder = info.params && info.params.key as string | undefined;
360
- const folderChanged = this.shared.folder !== folder;
345
+ const folderChanged = this.shared.fs.parent !== folder;
361
346
  if (folderChanged) {
362
- this.shared.folder = folder;
363
- this.shared.resetList();
347
+ this.shared.fs.parent = folder;
348
+ this.shared.fs.resetList();
364
349
  }
365
- if (!this.shared.files.length || folderChanged) {
366
- this.shared.readFiles();
350
+ if (!this.shared.fs.files.length || folderChanged) {
351
+ this.shared.fs.debounceQuery();
367
352
  }
368
353
  }
369
354
 
@@ -66,7 +66,7 @@ export default class AuthenticateScreen extends ApplicationScreen {
66
66
  return env;
67
67
  }
68
68
 
69
- protected handleAuthenticate(): void {
69
+ protected async handleAuth(): Promise<void> {
70
70
  const { environment } = this;
71
71
  if (!environment) {
72
72
  this.environmentError = 'Environment information not found. Go back to the previous page.';
@@ -146,7 +146,7 @@ export default class AuthenticateScreen extends ApplicationScreen {
146
146
  <div class="action">
147
147
  <ui-button
148
148
  type="filled"
149
- @click="${this.handleAuthenticate}"
149
+ @click="${this.handleAuth}"
150
150
  >Authenticate</ui-button>
151
151
  </div>
152
152
  `;
@@ -1,19 +1,13 @@
1
1
  import {
2
2
  IFile, HttpProject, Project, Folder, ProjectKind, FolderKind,
3
3
  EventUtils, DataNamespace, DataFile, DataFileKind,
4
- FileAddOptions, FileBreadcrumb, ContextSpaceListOptions,
5
- BroadcastEvent, BroadcastFileData,
6
- FileMetaCreatedBroadcastEvent,
7
- DeletedBroadcastEvent,
8
- FilePatchBroadcastEvent,
9
- ContextListResult,
4
+ FileAddOptions, FileBreadcrumb,
10
5
  ICapabilities,
11
6
  } from "@api-client/core/build/browser.js";
12
7
  import { html, nothing, TemplateResult } from "lit";
13
8
  import { join } from "lit/directives/join.js";
14
9
  import { map } from "lit/directives/map.js";
15
10
  import { until } from 'lit/directives/until.js';
16
- import { Patch } from "@api-client/json";
17
11
  import type ApiClientScreen from "../ApiClient.screen.js";
18
12
  import { DataTable } from "../../../ui/table/DataTable.js";
19
13
  import { NavigationEvents } from "../../../events/NavigationEvents.js";
@@ -22,9 +16,10 @@ import { DataImportProcessor } from "../../../http-client/store/DataImportProces
22
16
  import { SnackNotifications } from "../../../ui/notification/SnackNotifications.js";
23
17
  import { UiDialogClosingReason } from "../../../ui/dialog/UiDialog.js";
24
18
  import { FileBindings } from "../../../bindings/base/FileBindings.js";
25
- import { IconType } from "../../../ui/icons/Icons.js";
26
19
  import UserCache from '../../../store/UserCache.js';
27
20
  import AppInfo from '../AppInfo.js';
21
+ import { fileIcon } from "../../../elements/store/FilesLib.js";
22
+ import { FileSystem } from "../../../store/FileSystem.js";
28
23
  import '@github/relative-time-element';
29
24
  import '../../../define/ui/ui-list.js';
30
25
  import '../../../define/ui/ui-list-item.js';
@@ -35,7 +30,7 @@ import '../../../define/ui/ui-button.js';
35
30
  import '../../../define/ui/ui-divider.js';
36
31
  import '../../../define/ui/ui-text-field.js';
37
32
  import '../../../define/dialog/rename-dialog.js';
38
- import '../../../define/files/share-file.js';
33
+ import '../../../define/store/share-file.js';
39
34
 
40
35
  enum ImportState {
41
36
  Analyzing,
@@ -49,41 +44,20 @@ enum ImportState {
49
44
  * A class that specializes in rendering the list of files in HTTP Client.
50
45
  */
51
46
  export class FilesPage {
47
+ fs = new FileSystem();
52
48
 
53
- /**
54
- * The list of files to render.
55
- * Do not overwrite this property or when you must then update the DataTable's `items` as well.
56
- */
57
- files: IFile[] = [];
58
-
59
- /**
60
- * The pagination cursor for files.
61
- * As long there's a cursor, there are more files to request.
62
- */
63
- cursor?: string;
64
-
65
- /**
66
- * The key of the currently rendered folder.
67
- */
68
- folder?: string;
69
-
70
- breadcrumbs?: FileBreadcrumb[];
71
-
72
- dt = new DataTable(() => this.files, { ariaLabel: 'Your files', active: true, selection: 'single' });
49
+ dt = new DataTable(() => this.fs.files, { ariaLabel: 'Your files', active: true, selection: 'single' });
73
50
 
74
51
  importProcessor?: DataImportProcessor;
75
52
 
76
53
  importState?: ImportState;
77
54
 
78
- /**
79
- * Whether the UI is reading files
80
- */
81
- readingFiles?: boolean;
82
-
83
55
  /**
84
56
  * A selected in the table item.
85
57
  */
86
58
  selectedItem?: string;
59
+
60
+ errorMessage?: string;
87
61
 
88
62
  constructor(protected screen: ApiClientScreen) {
89
63
  this.createProjectHandler = this.createProjectHandler.bind(this);
@@ -105,122 +79,24 @@ export class FilesPage {
105
79
  this.dt.addEventListener('activate', this.itemActivateHandler.bind(this));
106
80
  this.dt.addEventListener('render', this.handleTableRender.bind(this));
107
81
  this.dt.addEventListener('select', this.handleTableSelect.bind(this));
108
- }
82
+ this.dt.addEventListener('scrollend', this.handleTableScrollEnd.bind(this));
109
83
 
110
- async refresh(): Promise<void> {
111
- this.resetList();
112
- this.screen.render();
113
- await this.readFiles();
114
- }
115
-
116
- resetList(): void {
117
- this.files = [];
118
- this.cursor = undefined;
119
- this.breadcrumbs = undefined;
120
- }
121
-
122
- async readFiles(): Promise<void> {
123
- const { selectedSpace } = this.screen;
124
- if (!selectedSpace) {
125
- return;
126
- }
127
- if (this.readingFiles) {
128
- return;
129
- }
130
- this.readingFiles = true;
131
- const { cursor: filesCursor, folder } = this;
132
- const opts: ContextSpaceListOptions = {
133
- space: selectedSpace,
134
- descending: true,
135
- };
136
- if (filesCursor) {
137
- opts.cursor = filesCursor;
138
- }
139
- if (folder) {
140
- opts.parent = folder;
141
- }
142
- try {
143
- const result = await this.listFiles(opts);
144
- const { items, cursor } = result;
145
- this.files = this.files.concat(items).sort(this.filesSortFunction.bind(this));
146
- this.cursor = cursor;
147
- if (folder) {
148
- const br = await this.screen.bindings.store.files.breadcrumbs(folder);
149
- this.breadcrumbs = br.items.reverse();
150
- }
151
- } finally {
152
- this.readingFiles = false;
153
- }
154
- this.screen.render();
155
- }
156
-
157
- protected async listFiles(opts: ContextSpaceListOptions): Promise<ContextListResult<IFile>> {
158
- return this.screen.bindings.store.files.list(opts);
159
- }
160
-
161
- /**
162
- * Sorts files on the list.
163
- *
164
- * Folders are always on top.
165
- */
166
- filesSortFunction(a: IFile, b:IFile): number {
167
- if (a.kind !== b.kind) {
168
- if (a.kind === FolderKind) {
169
- return -1;
170
- }
171
- return 1;
172
- }
173
- if (a.kind === b.kind && a.kind === FolderKind) {
174
- return (a.info.name || '').localeCompare(b.info.name || '');
175
- }
176
- return b.lastModified.time - a.lastModified.time;
177
- }
178
-
179
- handleFileEvent(event: BroadcastEvent): void {
180
- const typed = event as BroadcastEvent & BroadcastFileData;
181
- if (typed.parent && typed.parent !== this.folder) {
182
- return;
183
- }
184
- if (typed.alt !== 'meta') {
185
- return;
186
- }
187
- switch (event.operation) {
188
- case 'created': this.handleFileCreated((typed as FileMetaCreatedBroadcastEvent).data); break;
189
- case 'deleted': this.handleFileDeleted(typed as DeletedBroadcastEvent); break;
190
- case 'patch': this.handleFilePatch(typed as FilePatchBroadcastEvent); break;
191
- default:
192
- }
193
- }
194
-
195
- handleFileCreated(file: IFile): void {
196
- this.files.push(file);
197
- this.files.sort(this.filesSortFunction.bind(this));
198
- this.screen.render();
199
- }
200
-
201
- handleFileDeleted(event: DeletedBroadcastEvent): void {
202
- this.removeFileFromList(event.key);
203
- }
204
-
205
- removeFileFromList(key: string): void {
206
- const index = this.files.findIndex(i => i.key === key);
207
- if (index === -1) {
208
- return;
209
- }
210
- this.files.splice(index, 1);
211
- this.screen.render();
84
+ this.fs.observe();
85
+ this.fs.addEventListener('change', () => this.screen.render());
86
+ this.fs.addEventListener('error', (e: Event) => {
87
+ const event = e as CustomEvent<string>;
88
+ this.errorMessage = event.detail;
89
+ });
90
+ this.fs.addEventListener('querycomplete', () => {
91
+ document.body.dispatchEvent(new Event('querycomplete', { bubbles: true }));
92
+ });
212
93
  }
213
94
 
214
- handleFilePatch(event: FilePatchBroadcastEvent): void {
215
- const { data, key } = event;
216
- const index = this.files.findIndex(i => i.key === key);
217
- if (index === -1) {
218
- return;
219
- }
220
- const file = this.files[index];
221
- const result = Patch.apply(file, data.patch);
222
- this.files[index] = result.doc as IFile;
95
+ async refresh(): Promise<void> {
96
+ this.fs.resetList();
223
97
  this.screen.render();
98
+ await this.fs.queryPage();
99
+ await this.fs.queryBreadcrumbs();
224
100
  }
225
101
 
226
102
  protected cancelCreateProjectDialog(): void {
@@ -331,8 +207,8 @@ export class FilesPage {
331
207
  throw new Error('You need to select a space first');
332
208
  }
333
209
  const opts: FileAddOptions = {};
334
- if (this.folder) {
335
- opts.parent = this.folder;
210
+ if (this.fs.parent) {
211
+ opts.parent = this.fs.parent;
336
212
  }
337
213
  const result = await this.screen.bindings.store.files.add(file, opts);
338
214
  if (media) {
@@ -422,7 +298,7 @@ export class FilesPage {
422
298
  this.importState = ImportState.Importing;
423
299
  this.screen.render();
424
300
  try {
425
- await importProcessor.importProject(this.screen.bindings.store, selectedSpace, this.folder);
301
+ await importProcessor.importProject(this.screen.bindings.store, selectedSpace, this.fs.parent);
426
302
  this.importState = ImportState.Done;
427
303
  this.screen.render();
428
304
  } catch (e) {
@@ -465,8 +341,12 @@ export class FilesPage {
465
341
  this.screen.render();
466
342
  }
467
343
 
344
+ protected handleTableScrollEnd(): void {
345
+ this.fs.debounceQuery();
346
+ }
347
+
468
348
  openProject(key: string): void {
469
- const file = this.files.find(i => i.key === key);
349
+ const file = this.fs.files.find(i => i.key === key);
470
350
  if (!file) {
471
351
  throw new Error(`Invalid state: file not found`);
472
352
  }
@@ -550,7 +430,7 @@ export class FilesPage {
550
430
  }
551
431
 
552
432
  protected renameFile(key: string): void {
553
- const file = this.files.find(i => i.key === key);
433
+ const file = this.fs.files.find(i => i.key === key);
554
434
  if (!file) {
555
435
  return;
556
436
  }
@@ -571,7 +451,7 @@ export class FilesPage {
571
451
  }
572
452
 
573
453
  protected shareFile(key: string): void {
574
- const file = this.files.find(i => i.key === key);
454
+ const file = this.fs.files.find(i => i.key === key);
575
455
  if (!file) {
576
456
  return;
577
457
  }
@@ -582,7 +462,7 @@ export class FilesPage {
582
462
  dialog.modal = true;
583
463
  document.body.appendChild(dialog);
584
464
  dialog.open = true;
585
- dialog.addEventListener('closed', () => {
465
+ dialog.addEventListener('close', () => {
586
466
  document.body.removeChild(dialog);
587
467
  });
588
468
  }
@@ -626,7 +506,7 @@ export class FilesPage {
626
506
  if (!selectedItem) {
627
507
  return nothing
628
508
  }
629
- const file = this.files.find(i => i.key === selectedItem);
509
+ const file = this.fs.files.find(i => i.key === selectedItem);
630
510
  if (!file) {
631
511
  return nothing;
632
512
  }
@@ -726,7 +606,7 @@ export class FilesPage {
726
606
  }
727
607
 
728
608
  protected renderBreadcrumbs(): TemplateResult {
729
- const { breadcrumbs=[] } = this;
609
+ const { breadcrumbs = [] } = this.fs;
730
610
  if (!breadcrumbs.length) {
731
611
  return html``;
732
612
  }
@@ -868,17 +748,10 @@ export class FilesPage {
868
748
  }
869
749
 
870
750
  protected nameCell(item: IFile): TemplateResult {
871
- let icon: IconType | undefined;
872
- switch (item.kind) {
873
- case DataFileKind: icon = 'schema'; break;
874
- case FolderKind: icon = 'folder'; break;
875
- case ProjectKind: icon = 'collectionsBookmark'; break;
876
- default:
877
- }
878
-
751
+ const icon = fileIcon(item);
879
752
  return this.dt.cell(html`
880
753
  ${icon ? html`<ui-icon icon="${icon}" class="cell-icon"></ui-icon>` : nothing}
881
- ${item.info.name || 'Unnamed project'}
754
+ ${item.info.name || 'Unnamed file'}
882
755
  `, { class: 'name-cell', data: { kind: item.kind } });
883
756
  }
884
757
 
@@ -1,4 +1,4 @@
1
- import { BroadcastEvent, BroadcastFileData, ContextListResult, ContextSpaceListOptions, FileAccessBroadcastEvent, IFile } from "@api-client/core/build/browser.js";
1
+ import { IFile } from "@api-client/core/build/browser.js";
2
2
  import { html, TemplateResult } from "lit";
3
3
  import type ApiClientScreen from "../ApiClient.screen.js";
4
4
  import { FilesPage } from "./Files.page.js";
@@ -7,45 +7,7 @@ export class SharedPage extends FilesPage {
7
7
  constructor(screen: ApiClientScreen) {
8
8
  super(screen);
9
9
  this.dt.options.ariaLabel = 'Shared with you';
10
- }
11
-
12
- protected override async listFiles(opts: ContextSpaceListOptions): Promise<ContextListResult<IFile>> {
13
- return this.screen.bindings.store.shared.list(opts);
14
- }
15
-
16
- override handleFileEvent(event: BroadcastEvent): void {
17
- const typed = event as BroadcastEvent & BroadcastFileData;
18
- if (typed.parent && typed.parent !== this.folder) {
19
- return;
20
- }
21
- // if (typed.alt !== 'meta') {
22
- // return;
23
- // }
24
- switch (event.operation) {
25
- case 'access-granted': this.handleFileAccessGranted(typed as FileAccessBroadcastEvent); break;
26
- case 'access-removed': this.handleFileAccessRemoved(typed as FileAccessBroadcastEvent); break;
27
- default: super.handleFileEvent(event);
28
- }
29
- }
30
-
31
- async handleFileAccessGranted(event: FileAccessBroadcastEvent): Promise<void> {
32
- const { key } = event;
33
- try {
34
- const file = await this.screen.bindings.store.files.read(key);
35
- const index = this.files.findIndex(i => i.key === key);
36
- if (index >= 0) {
37
- this.files.splice(index, 1);
38
- }
39
- this.handleFileCreated(file);
40
- } catch (e) {
41
- // eslint-disable-next-line no-console
42
- console.warn(e);
43
- }
44
- }
45
-
46
- handleFileAccessRemoved(event: FileAccessBroadcastEvent): void {
47
- const { key } = event;
48
- this.removeFileFromList(key);
10
+ this.fs.source = 'shared';
49
11
  }
50
12
 
51
13
  override render(): TemplateResult {