@api-client/ui 0.0.11 → 0.0.13
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/.eslintrc +8 -0
- package/demo/elements/authorization/cc.ts +56 -27
- package/demo/elements/index.html +3 -0
- package/demo/elements/store/file-picker.html +15 -0
- package/demo/elements/store/file-picker.ts +134 -0
- package/demo/elements/store/index.html +19 -0
- package/demo/store/StorePlugin.js +1 -0
- package/dist/bindings/base/FileBindings.d.ts +4 -0
- package/dist/bindings/base/FileBindings.d.ts.map +1 -1
- package/dist/bindings/base/FileBindings.js +21 -1
- package/dist/bindings/base/FileBindings.js.map +1 -1
- package/dist/bindings/base/StoreBindings.d.ts +6 -17
- package/dist/bindings/base/StoreBindings.d.ts.map +1 -1
- package/dist/bindings/base/StoreBindings.js +15 -60
- package/dist/bindings/base/StoreBindings.js.map +1 -1
- package/dist/bindings/web/WebFileBindings.js +1 -1
- package/dist/bindings/web/WebFileBindings.js.map +1 -1
- package/dist/define/http/certificate-add.d.ts +9 -0
- package/dist/define/http/certificate-add.d.ts.map +1 -0
- package/dist/define/http/certificate-add.js +10 -0
- package/dist/define/http/certificate-add.js.map +1 -0
- package/dist/define/store/file-picker.d.ts +9 -0
- package/dist/define/store/file-picker.d.ts.map +1 -0
- package/dist/define/store/file-picker.js +10 -0
- package/dist/define/store/file-picker.js.map +1 -0
- package/dist/define/{files → store}/share-file.d.ts +1 -1
- package/dist/define/store/share-file.d.ts.map +1 -0
- package/dist/define/{files → store}/share-file.js +2 -2
- package/dist/define/store/share-file.js.map +1 -0
- package/dist/define/ui/ui-segmented-button-set.d.ts +1 -1
- package/dist/define/ui/ui-segmented-button-set.d.ts.map +1 -1
- package/dist/define/ui/ui-segmented-button-set.js.map +1 -1
- package/dist/elements/authorization/ui/CC.styles.d.ts.map +1 -1
- package/dist/elements/authorization/ui/CC.styles.js +4 -9
- package/dist/elements/authorization/ui/CC.styles.js.map +1 -1
- package/dist/elements/authorization/ui/CcAuthorization.d.ts +14 -29
- package/dist/elements/authorization/ui/CcAuthorization.d.ts.map +1 -1
- package/dist/elements/authorization/ui/CcAuthorization.js +67 -158
- package/dist/elements/authorization/ui/CcAuthorization.js.map +1 -1
- package/dist/elements/http/CertificateAdd.element.d.ts +91 -0
- package/dist/elements/http/CertificateAdd.element.d.ts.map +1 -0
- package/dist/elements/http/CertificateAdd.element.js +389 -0
- package/dist/elements/http/CertificateAdd.element.js.map +1 -0
- package/dist/elements/http/CertificateAdd.styles.d.ts +3 -0
- package/dist/elements/http/CertificateAdd.styles.d.ts.map +1 -0
- package/dist/elements/http/CertificateAdd.styles.js +61 -0
- package/dist/elements/http/CertificateAdd.styles.js.map +1 -0
- package/dist/elements/project/ProjectRunReport.d.ts +2 -1
- package/dist/elements/project/ProjectRunReport.d.ts.map +1 -1
- package/dist/elements/project/ProjectRunReport.js.map +1 -1
- package/dist/elements/store/FilePicker.element.d.ts +87 -0
- package/dist/elements/store/FilePicker.element.d.ts.map +1 -0
- package/dist/elements/store/FilePicker.element.js +263 -0
- package/dist/elements/store/FilePicker.element.js.map +1 -0
- package/dist/elements/store/FilePicker.styles.d.ts +3 -0
- package/dist/elements/store/FilePicker.styles.d.ts.map +1 -0
- package/dist/elements/store/FilePicker.styles.js +73 -0
- package/dist/elements/store/FilePicker.styles.js.map +1 -0
- package/dist/elements/store/FilesLib.d.ts +10 -0
- package/dist/elements/store/FilesLib.d.ts.map +1 -0
- package/dist/elements/store/FilesLib.js +38 -0
- package/dist/elements/store/FilesLib.js.map +1 -0
- package/dist/elements/{files/ShareFile.d.ts → store/ShareFile.element.d.ts} +1 -1
- package/dist/elements/store/ShareFile.element.d.ts.map +1 -0
- package/dist/elements/{files/ShareFile.js → store/ShareFile.element.js} +1 -1
- package/dist/elements/store/ShareFile.element.js.map +1 -0
- package/dist/elements/store/ShareFile.styles.d.ts.map +1 -0
- package/dist/elements/{files → store}/ShareFile.styles.js.map +1 -1
- package/dist/events/EventTypes.d.ts +7 -7
- package/dist/events/EventTypes.d.ts.map +1 -1
- package/dist/events/EventTypes.js +8 -7
- package/dist/events/EventTypes.js.map +1 -1
- package/dist/events/Events.d.ts +7 -1
- package/dist/events/Events.d.ts.map +1 -1
- package/dist/events/Events.js +2 -0
- package/dist/events/Events.js.map +1 -1
- package/dist/events/FilesystemEvents.d.ts +8 -0
- package/dist/events/FilesystemEvents.d.ts.map +1 -0
- package/dist/events/FilesystemEvents.js +59 -0
- package/dist/events/FilesystemEvents.js.map +1 -0
- package/dist/events/HttpClientEvents.d.ts +0 -2
- package/dist/events/HttpClientEvents.d.ts.map +1 -1
- package/dist/events/HttpClientEvents.js +0 -2
- package/dist/events/HttpClientEvents.js.map +1 -1
- package/dist/events/StoreEvents.d.ts +8 -1
- package/dist/events/StoreEvents.d.ts.map +1 -1
- package/dist/events/StoreEvents.js +19 -0
- package/dist/events/StoreEvents.js.map +1 -1
- package/dist/http-client/idb/Arc18DataUpgrade.d.ts +0 -8
- package/dist/http-client/idb/Arc18DataUpgrade.d.ts.map +1 -1
- package/dist/http-client/idb/Arc18DataUpgrade.js +11 -206
- package/dist/http-client/idb/Arc18DataUpgrade.js.map +1 -1
- package/dist/http-client/store/StoreBroadcast.d.ts +0 -5
- package/dist/http-client/store/StoreBroadcast.d.ts.map +1 -1
- package/dist/http-client/store/StoreBroadcast.js +0 -7
- package/dist/http-client/store/StoreBroadcast.js.map +1 -1
- package/dist/lib/files/FileUtils.d.ts +9 -0
- package/dist/lib/files/FileUtils.d.ts.map +1 -0
- package/dist/lib/files/FileUtils.js +13 -0
- package/dist/lib/files/FileUtils.js.map +1 -0
- package/dist/mixins/RouteMixin.d.ts +4 -0
- package/dist/mixins/RouteMixin.d.ts.map +1 -1
- package/dist/mixins/RouteMixin.js +1 -0
- package/dist/mixins/RouteMixin.js.map +1 -1
- package/dist/pages/ApplicationScreen.d.ts +1 -1
- package/dist/pages/ApplicationScreen.d.ts.map +1 -1
- package/dist/pages/ApplicationScreen.js +4 -2
- package/dist/pages/ApplicationScreen.js.map +1 -1
- package/dist/pages/api-client/ApiClient.screen.d.ts +4 -6
- package/dist/pages/api-client/ApiClient.screen.d.ts.map +1 -1
- package/dist/pages/api-client/ApiClient.screen.js +39 -31
- package/dist/pages/api-client/ApiClient.screen.js.map +1 -1
- package/dist/pages/api-client/ApiClient.styles.d.ts.map +1 -1
- package/dist/pages/api-client/ApiClient.styles.js +0 -12
- package/dist/pages/api-client/ApiClient.styles.js.map +1 -1
- package/dist/pages/api-client/Authenticate.screen.d.ts +1 -1
- package/dist/pages/api-client/Authenticate.screen.d.ts.map +1 -1
- package/dist/pages/api-client/Authenticate.screen.js +2 -2
- package/dist/pages/api-client/Authenticate.screen.js.map +1 -1
- package/dist/pages/api-client/pages/Files.page.d.ts +6 -35
- package/dist/pages/api-client/pages/Files.page.d.ts.map +1 -1
- package/dist/pages/api-client/pages/Files.page.js +45 -141
- package/dist/pages/api-client/pages/Files.page.js.map +1 -1
- package/dist/pages/api-client/pages/Shared.page.d.ts +1 -5
- package/dist/pages/api-client/pages/Shared.page.d.ts.map +1 -1
- package/dist/pages/api-client/pages/Shared.page.js +1 -40
- package/dist/pages/api-client/pages/Shared.page.js.map +1 -1
- package/dist/pages/demo/DemoPage.d.ts +7 -0
- package/dist/pages/demo/DemoPage.d.ts.map +1 -1
- package/dist/pages/demo/DemoPage.js +14 -0
- package/dist/pages/demo/DemoPage.js.map +1 -1
- package/dist/store/FileSystem.d.ts +90 -0
- package/dist/store/FileSystem.d.ts.map +1 -0
- package/dist/store/FileSystem.js +260 -0
- package/dist/store/FileSystem.js.map +1 -0
- package/dist/styles/global-styles.d.ts.map +1 -1
- package/dist/styles/global-styles.js +7 -0
- package/dist/styles/global-styles.js.map +1 -1
- package/dist/ui/button/SegmentedButtonsSet.d.ts +14 -0
- package/dist/ui/button/SegmentedButtonsSet.d.ts.map +1 -1
- package/dist/ui/button/SegmentedButtonsSet.js.map +1 -1
- package/dist/ui/icons/Icons.d.ts +2 -1
- package/dist/ui/icons/Icons.d.ts.map +1 -1
- package/dist/ui/icons/Icons.js +1 -0
- package/dist/ui/icons/Icons.js.map +1 -1
- package/dist/ui/list/UiDropdownList.d.ts +9 -1
- package/dist/ui/list/UiDropdownList.d.ts.map +1 -1
- package/dist/ui/list/UiDropdownList.js +39 -17
- package/dist/ui/list/UiDropdownList.js.map +1 -1
- package/dist/ui/list/UiList.d.ts +6 -1
- package/dist/ui/list/UiList.d.ts.map +1 -1
- package/dist/ui/list/UiList.js +24 -9
- package/dist/ui/list/UiList.js.map +1 -1
- package/dist/ui/notification/SnackNotifications.d.ts +1 -0
- package/dist/ui/notification/SnackNotifications.d.ts.map +1 -1
- package/dist/ui/notification/SnackNotifications.js +7 -0
- package/dist/ui/notification/SnackNotifications.js.map +1 -1
- package/dist/ui/table/DataTable.d.ts +4 -0
- package/dist/ui/table/DataTable.d.ts.map +1 -1
- package/dist/ui/table/DataTable.js +23 -1
- package/dist/ui/table/DataTable.js.map +1 -1
- package/package.json +1 -1
- package/src/bindings/base/FileBindings.ts +25 -1
- package/src/bindings/base/StoreBindings.ts +16 -73
- package/src/bindings/web/WebFileBindings.ts +1 -1
- package/src/define/http/certificate-add.ts +12 -0
- package/src/define/store/file-picker.ts +12 -0
- package/src/define/{files → store}/share-file.ts +2 -2
- package/src/define/ui/ui-segmented-button-set.ts +1 -1
- package/src/elements/authorization/ui/CC.styles.ts +4 -9
- package/src/elements/authorization/ui/CcAuthorization.ts +67 -167
- package/src/elements/http/CertificateAdd.element.ts +443 -0
- package/src/elements/http/CertificateAdd.styles.ts +61 -0
- package/src/elements/project/ProjectRunReport.ts +2 -1
- package/src/elements/store/FilePicker.element.ts +297 -0
- package/src/elements/store/FilePicker.styles.ts +73 -0
- package/src/elements/store/FilesLib.ts +32 -0
- package/src/events/EventTypes.ts +8 -7
- package/src/events/Events.ts +2 -0
- package/src/events/FilesystemEvents.ts +63 -0
- package/src/events/HttpClientEvents.ts +0 -2
- package/src/events/StoreEvents.ts +21 -1
- package/src/http-client/idb/Arc18DataUpgrade.ts +84 -84
- package/src/http-client/store/StoreBroadcast.ts +0 -8
- package/src/lib/files/FileUtils.ts +12 -0
- package/src/mixins/RouteMixin.ts +8 -1
- package/src/pages/ApplicationScreen.ts +5 -3
- package/src/pages/api-client/ApiClient.screen.ts +42 -33
- package/src/pages/api-client/ApiClient.styles.ts +0 -12
- package/src/pages/api-client/Authenticate.screen.ts +2 -2
- package/src/pages/api-client/pages/Files.page.ts +48 -164
- package/src/pages/api-client/pages/Shared.page.ts +2 -40
- package/src/pages/demo/DemoPage.ts +17 -0
- package/src/store/FileSystem.ts +325 -0
- package/src/styles/global-styles.ts +7 -0
- package/src/ui/button/SegmentedButtonsSet.ts +16 -1
- package/src/ui/icons/Icons.ts +2 -1
- package/src/ui/list/UiDropdownList.ts +44 -17
- package/src/ui/list/UiList.ts +26 -10
- package/src/ui/notification/SnackNotifications.ts +8 -0
- package/src/ui/table/DataTable.ts +29 -3
- package/test/elements/http/BodyFormdataEditorElement.test.ts +458 -454
- package/test/elements/http/BodyMultipartEditorElement.test.ts +609 -605
- package/test/elements/http/BodyRawEditorElement.test.ts +60 -56
- package/test/elements/http/CertificateAdd.test.ts +430 -0
- package/test/elements/store/FilePicker.test.ts +241 -0
- package/test/env.js +3 -0
- package/test/events/EventTypes.test.ts +0 -22
- package/test/helpers/StoreHelper.ts +390 -0
- package/test/helpers/UiMock.ts +19 -2
- package/tsconfig.eslint.json +3 -1
- package/web-test-runner.config.mjs +50 -3
- package/dist/define/files/share-file.d.ts.map +0 -1
- package/dist/define/files/share-file.js.map +0 -1
- package/dist/elements/files/ShareFile.d.ts.map +0 -1
- package/dist/elements/files/ShareFile.js.map +0 -1
- package/dist/elements/files/ShareFile.styles.d.ts.map +0 -1
- package/dist/events/http-client/models/CertificatesEvents.d.ts +0 -12
- package/dist/events/http-client/models/CertificatesEvents.d.ts.map +0 -1
- package/dist/events/http-client/models/CertificatesEvents.js +0 -18
- package/dist/events/http-client/models/CertificatesEvents.js.map +0 -1
- package/dist/http-client/idb/AuthDataModel.d.ts +0 -60
- package/dist/http-client/idb/AuthDataModel.d.ts.map +0 -1
- package/dist/http-client/idb/AuthDataModel.js +0 -150
- package/dist/http-client/idb/AuthDataModel.js.map +0 -1
- package/dist/http-client/idb/HostsModel.d.ts +0 -25
- package/dist/http-client/idb/HostsModel.d.ts.map +0 -1
- package/dist/http-client/idb/HostsModel.js +0 -82
- package/dist/http-client/idb/HostsModel.js.map +0 -1
- package/dist/http-client/idb/LegacyMockedStore.d.ts +0 -214
- package/dist/http-client/idb/LegacyMockedStore.d.ts.map +0 -1
- package/dist/http-client/idb/LegacyMockedStore.js +0 -486
- package/dist/http-client/idb/LegacyMockedStore.js.map +0 -1
- package/src/events/http-client/models/CertificatesEvents.ts +0 -23
- package/src/http-client/idb/AuthDataModel.ts +0 -175
- package/src/http-client/idb/HostsModel.ts +0 -125
- package/src/http-client/idb/LegacyMockedStore.ts +0 -544
- package/test/apic-ui.test.ts +0 -31
- /package/dist/elements/{files → store}/ShareFile.styles.d.ts +0 -0
- /package/dist/elements/{files → store}/ShareFile.styles.js +0 -0
- /package/src/elements/{files/ShareFile.ts → store/ShareFile.element.ts} +0 -0
- /package/src/elements/{files → store}/ShareFile.styles.ts +0 -0
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BroadcastEvent,
|
|
3
|
+
BroadcastFileData,
|
|
4
|
+
ContextListResult,
|
|
5
|
+
ContextSpaceListOptions,
|
|
6
|
+
DeletedBroadcastEvent,
|
|
7
|
+
FileAccessBroadcastEvent,
|
|
8
|
+
FileBreadcrumb,
|
|
9
|
+
FileMetaCreatedBroadcastEvent,
|
|
10
|
+
FilePatchBroadcastEvent,
|
|
11
|
+
IFile,
|
|
12
|
+
ListFileKind,
|
|
13
|
+
} from "@api-client/core/build/browser.js";
|
|
14
|
+
import { Patch } from "@api-client/json";
|
|
15
|
+
import { filesSortFunction } from "../elements/store/FilesLib.js";
|
|
16
|
+
import { Events } from "../events/Events.js";
|
|
17
|
+
import { StoreBroadcast } from "../http-client/store/StoreBroadcast.js";
|
|
18
|
+
|
|
19
|
+
type FilesSource = 'own' | 'shared';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Contains a logic to read files from the store.
|
|
23
|
+
*
|
|
24
|
+
* It uses app events to query for the data.
|
|
25
|
+
*/
|
|
26
|
+
export class FileSystem extends EventTarget {
|
|
27
|
+
source: FilesSource = 'own';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The list of files to render.
|
|
31
|
+
* Do not overwrite this property or when you must then update the DataTable's `items` as well.
|
|
32
|
+
*/
|
|
33
|
+
files: IFile[] = [];
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* The pagination cursor for files.
|
|
37
|
+
*/
|
|
38
|
+
cursor?: string;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* This is set when the last files query did not yeld results.
|
|
42
|
+
*/
|
|
43
|
+
queryEnded?: boolean;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The currently rendered breadcrumbs.
|
|
47
|
+
*/
|
|
48
|
+
breadcrumbs?: FileBreadcrumb[];
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Whether the UI is reading files
|
|
52
|
+
*/
|
|
53
|
+
reading?: boolean;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* The list of file kinds to list.
|
|
57
|
+
* Folders are always included.
|
|
58
|
+
*/
|
|
59
|
+
kinds?: ListFileKind[];
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* The timeout for the query debouncer.
|
|
63
|
+
* When any property change this is the time the element will wait
|
|
64
|
+
* until the actual query is made.
|
|
65
|
+
*/
|
|
66
|
+
debounceTimeout = 100;
|
|
67
|
+
|
|
68
|
+
debouncerValue?: number;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* The variable parent folder key.
|
|
72
|
+
* This is set when interacting with the UI or when the `folder` attribute change.
|
|
73
|
+
*/
|
|
74
|
+
parent?: string;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* The page limit. Defaults to the store defaults.
|
|
78
|
+
* @attribute
|
|
79
|
+
*/
|
|
80
|
+
limit?: number;
|
|
81
|
+
|
|
82
|
+
filesChannel = new BroadcastChannel(StoreBroadcast.files);
|
|
83
|
+
|
|
84
|
+
eventsTarget: EventTarget = document.body;
|
|
85
|
+
|
|
86
|
+
constructor() {
|
|
87
|
+
super();
|
|
88
|
+
this.fileMetaMessageHandler = this.fileMetaMessageHandler.bind(this);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
observe(): void {
|
|
92
|
+
this.filesChannel.addEventListener('message', this.fileMetaMessageHandler);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
unobserve(): void {
|
|
96
|
+
this.filesChannel.removeEventListener('message', this.fileMetaMessageHandler);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
fileMetaMessageHandler(e: MessageEvent): void {
|
|
100
|
+
const event = e.data as BroadcastEvent & BroadcastFileData;
|
|
101
|
+
if (event.parent !== this.parent) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (event.alt !== 'meta') {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
switch (event.operation) {
|
|
108
|
+
case 'created': this.handleFileCreated((event as FileMetaCreatedBroadcastEvent).data); break;
|
|
109
|
+
case 'deleted': this.handleFileDeleted(event as DeletedBroadcastEvent); break;
|
|
110
|
+
case 'patch': this.handleFilePatch(event as FilePatchBroadcastEvent); break;
|
|
111
|
+
case 'access-granted': this.handleFileAccessGranted(event as FileAccessBroadcastEvent); break;
|
|
112
|
+
case 'access-removed': this.handleFileAccessRemoved(event as FileAccessBroadcastEvent); break;
|
|
113
|
+
default:
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
handleFileCreated(file: IFile): void {
|
|
118
|
+
this.files.push(file);
|
|
119
|
+
// We don't sort here so files that appear at the end which makes sense because the list is scrolling.
|
|
120
|
+
// this.files.sort(filesSortFunction);
|
|
121
|
+
this.notifyUpdate();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
handleFileDeleted(event: DeletedBroadcastEvent): void {
|
|
125
|
+
this.removeFileFromList(event.key);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async handleFileAccessGranted(event: FileAccessBroadcastEvent): Promise<void> {
|
|
129
|
+
if (this.source !== 'shared') {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
const { key } = event;
|
|
133
|
+
try {
|
|
134
|
+
const file = await Events.Store.File.read(key, false, this.eventsTarget);
|
|
135
|
+
const index = this.files.findIndex(i => i.key === key);
|
|
136
|
+
if (index >= 0) {
|
|
137
|
+
this.files.splice(index, 1);
|
|
138
|
+
}
|
|
139
|
+
this.handleFileCreated(file);
|
|
140
|
+
} catch (e) {
|
|
141
|
+
// eslint-disable-next-line no-console
|
|
142
|
+
console.warn(e);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
handleFileAccessRemoved(event: FileAccessBroadcastEvent): void {
|
|
147
|
+
if (this.source !== 'shared') {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const { key } = event;
|
|
151
|
+
this.removeFileFromList(key);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
removeFileFromList(key: string): void {
|
|
155
|
+
const index = this.files.findIndex(i => i.key === key);
|
|
156
|
+
if (index === -1) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
this.files.splice(index, 1);
|
|
160
|
+
this.notifyUpdate();
|
|
161
|
+
this.dispatchEvent(new CustomEvent('delete', { detail: key }));
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
handleFilePatch(event: FilePatchBroadcastEvent): void {
|
|
165
|
+
const { data, key } = event;
|
|
166
|
+
const index = this.files.findIndex(i => i.key === key);
|
|
167
|
+
if (index === -1) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
const file = this.files[index];
|
|
171
|
+
const result = Patch.apply(file, data.patch);
|
|
172
|
+
this.files[index] = result.doc as IFile;
|
|
173
|
+
this.notifyUpdate();
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
debounceQuery(): void {
|
|
177
|
+
if (this.debouncerValue) {
|
|
178
|
+
clearTimeout(this.debouncerValue);
|
|
179
|
+
}
|
|
180
|
+
this.debouncerValue = window.setTimeout(() => {
|
|
181
|
+
this.queryPage();
|
|
182
|
+
this.queryBreadcrumbs();
|
|
183
|
+
}, this.debounceTimeout);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
resetList(): void {
|
|
187
|
+
this.files = [];
|
|
188
|
+
this.cursor = undefined;
|
|
189
|
+
this.breadcrumbs = undefined;
|
|
190
|
+
this.queryEnded = false;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async queryPage(): Promise<void> {
|
|
194
|
+
if (this.reading || this.queryEnded) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
this.reading = true;
|
|
198
|
+
this.notifyUpdate();
|
|
199
|
+
try {
|
|
200
|
+
const result = await this.listFiles();
|
|
201
|
+
if (!result) {
|
|
202
|
+
throw new Error(`Files event not handled.`);
|
|
203
|
+
}
|
|
204
|
+
const { items, cursor } = result;
|
|
205
|
+
if (!items.length) {
|
|
206
|
+
this.queryEnded = true;
|
|
207
|
+
} else {
|
|
208
|
+
if (!this.files.length) {
|
|
209
|
+
// we only sort the first page as later the user scrolls and sorting would change the order.
|
|
210
|
+
items.sort(filesSortFunction);
|
|
211
|
+
}
|
|
212
|
+
this.files = this.files.concat(items);
|
|
213
|
+
}
|
|
214
|
+
if (cursor) {
|
|
215
|
+
this.cursor = cursor;
|
|
216
|
+
}
|
|
217
|
+
} catch(e) {
|
|
218
|
+
const err = e as Error;
|
|
219
|
+
this.notifyError(`Unable to load files list. ${err.message}`);
|
|
220
|
+
// eslint-disable-next-line no-console
|
|
221
|
+
console.error(e);
|
|
222
|
+
} finally {
|
|
223
|
+
this.reading = false;
|
|
224
|
+
}
|
|
225
|
+
this.dispatchEvent(new Event('querycomplete'));
|
|
226
|
+
this.notifyUpdate();
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
async queryBreadcrumbs(): Promise<void> {
|
|
230
|
+
if (this.parent) {
|
|
231
|
+
const br = await this.listBreadcrumbs(this.parent);
|
|
232
|
+
this.breadcrumbs = br.items;
|
|
233
|
+
} else {
|
|
234
|
+
this.breadcrumbs = undefined;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
getPageQueryOptions(): ContextSpaceListOptions {
|
|
239
|
+
const { cursor: filesCursor, parent, limit } = this;
|
|
240
|
+
const opts: ContextSpaceListOptions = {
|
|
241
|
+
space: '', // this is filled by the bindings.
|
|
242
|
+
descending: true,
|
|
243
|
+
};
|
|
244
|
+
if (filesCursor) {
|
|
245
|
+
opts.cursor = filesCursor;
|
|
246
|
+
}
|
|
247
|
+
if (parent) {
|
|
248
|
+
opts.parent = parent;
|
|
249
|
+
}
|
|
250
|
+
if (typeof limit === 'number' && limit) {
|
|
251
|
+
opts.limit = limit;
|
|
252
|
+
}
|
|
253
|
+
return opts;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
async listFiles(): Promise<ContextListResult<IFile>> {
|
|
257
|
+
const { kinds } = this;
|
|
258
|
+
const opts = this.getPageQueryOptions();
|
|
259
|
+
if (this.source === 'shared') {
|
|
260
|
+
return Events.Store.File.listShared(kinds, opts, this.eventsTarget);
|
|
261
|
+
}
|
|
262
|
+
return Events.Store.File.list(kinds, opts, this.eventsTarget);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
async listBreadcrumbs(folder: string): Promise<ContextListResult<FileBreadcrumb>> {
|
|
266
|
+
return Events.Store.File.breadcrumbs(folder, this.eventsTarget);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
notifyError(error: string): void {
|
|
270
|
+
this.dispatchEvent(new CustomEvent<string>('error', {
|
|
271
|
+
detail: error,
|
|
272
|
+
}));
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
notifyUpdate(): void {
|
|
276
|
+
this.dispatchEvent(new Event('change'));
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Sets a parent and makes the query.
|
|
281
|
+
* @param key The key of the parent to query. Empty for root folder.
|
|
282
|
+
*/
|
|
283
|
+
selectFolder(key?: string): void {
|
|
284
|
+
this.parent = key;
|
|
285
|
+
this.resetList();
|
|
286
|
+
this.debounceQuery();
|
|
287
|
+
// this.queryPage();
|
|
288
|
+
this.queryBreadcrumbs();
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Moves to a parent folder, if any.
|
|
293
|
+
*/
|
|
294
|
+
async parentUp(): Promise<void> {
|
|
295
|
+
const { breadcrumbs = [] } = this;
|
|
296
|
+
const [, parent] = breadcrumbs;
|
|
297
|
+
if (!parent) {
|
|
298
|
+
if (!this.parent) {
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
this.parent = undefined;
|
|
302
|
+
} else {
|
|
303
|
+
if (this.parent === parent.key) {
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
this.parent = parent.key;
|
|
307
|
+
}
|
|
308
|
+
this.resetList();
|
|
309
|
+
await Promise.all([
|
|
310
|
+
this.queryPage(),
|
|
311
|
+
this.queryBreadcrumbs(),
|
|
312
|
+
]);
|
|
313
|
+
this.notifyUpdate();
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* @param target The HTML element that is a scrollable list if files.
|
|
318
|
+
* @returns True when the scroll position indicates that the user scrolled near the end of the list.
|
|
319
|
+
*/
|
|
320
|
+
isListEnd(target: HTMLElement): boolean {
|
|
321
|
+
const { scrollTop, offsetHeight, scrollHeight } = target;
|
|
322
|
+
// 20 is the offset which qualifies as the end. An arbitrary number.
|
|
323
|
+
return scrollTop + offsetHeight >= scrollHeight - 20;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
@@ -65,6 +65,13 @@ body {
|
|
|
65
65
|
justify-content: center;
|
|
66
66
|
position: absolute;
|
|
67
67
|
inset: 0;
|
|
68
|
+
background-color: var(--md-sys-color-surface);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.message,
|
|
72
|
+
.sub-message,
|
|
73
|
+
.auth-required-screen {
|
|
74
|
+
color: var(--md-sys-color-on-surface);
|
|
68
75
|
}
|
|
69
76
|
|
|
70
77
|
.full-error {
|
|
@@ -3,6 +3,21 @@ import { property, queryAssignedElements } from "lit/decorators.js";
|
|
|
3
3
|
import { UiElement } from "../UiElement.js";
|
|
4
4
|
import SegmentedButton from "./SegmentedButton.js";
|
|
5
5
|
|
|
6
|
+
export interface ISegmentedButtonSelectionDetail {
|
|
7
|
+
/**
|
|
8
|
+
* A reference to the selected button.
|
|
9
|
+
*/
|
|
10
|
+
button: SegmentedButton;
|
|
11
|
+
/**
|
|
12
|
+
* Whether the button is selected.
|
|
13
|
+
*/
|
|
14
|
+
selected: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* The index of the selected button.
|
|
17
|
+
*/
|
|
18
|
+
index: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
6
21
|
/**
|
|
7
22
|
* @fires select - When button's selection change.
|
|
8
23
|
*/
|
|
@@ -80,7 +95,7 @@ export default class SegmentedButtonsSet extends UiElement {
|
|
|
80
95
|
}
|
|
81
96
|
|
|
82
97
|
private dispatchSelected(index: number): void {
|
|
83
|
-
this.dispatchEvent(new CustomEvent('select', {
|
|
98
|
+
this.dispatchEvent(new CustomEvent<ISegmentedButtonSelectionDetail>('select', {
|
|
84
99
|
detail: {
|
|
85
100
|
button: this.buttons[index],
|
|
86
101
|
selected: this.buttons[index].selected,
|
package/src/ui/icons/Icons.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { svg, SVGTemplateResult } from 'lit';
|
|
|
8
8
|
export const iconWrapper = (tpl: SVGTemplateResult, width = 24, height = 24, top = 0, left = 0): SVGTemplateResult => svg`<svg viewBox="${top} ${left} ${width} ${height}" preserveAspectRatio="xMidYMid meet" focusable="false" style="pointer-events: none; display: block; width: 100%; height: 100%;">${tpl}</svg>`;
|
|
9
9
|
|
|
10
10
|
export type IconType =
|
|
11
|
-
'add' | 'api' | 'apps' | 'arrowBack' | 'arrowDropDown' | 'cancel' | 'cancelFilled' | 'check' | 'checkBox' | 'checkBoxBlank' | 'checkIndeterminate' | 'chevronLeft' | 'chevronRight' | 'close' | 'cloud' | 'cloudFilled' | 'collectionsBookmark' | 'deleteFile' | 'deleteOutline' | 'edit' | 'environment' | 'expandMore' |
|
|
11
|
+
'add' | 'api' | 'apps' | 'arrowBack' | 'arrowDropDown' | 'cancel' | 'cancelFilled' | 'certificate' | 'check' | 'checkBox' | 'checkBoxBlank' | 'checkIndeterminate' | 'chevronLeft' | 'chevronRight' | 'close' | 'cloud' | 'cloudFilled' | 'collectionsBookmark' | 'deleteFile' | 'deleteOutline' | 'edit' | 'environment' | 'expandMore' |
|
|
12
12
|
'fileDownload' | 'folder' | 'folderFilled' | 'folderShared' | 'help' | 'history' | 'info' | 'key' | 'menu' | 'leaderBoard' |
|
|
13
13
|
'moreVert' | 'openInNew' | 'personAdd' | 'playArrow' | 'project' | 'remove' | 'rename' | 'request' | 'restoreFromTrash' | 'save' |
|
|
14
14
|
'schema' | 'schemaEntity' | 'schemaModel' | 'schemaNamespace' | 'search' | 'send' | 'settings' | 'space' | 'taskAlt' | 'timeline' | 'tune' | 'viewGrid' | 'viewList' | 'visibility' | 'visibilityOff' | 'warning';
|
|
@@ -20,6 +20,7 @@ export const arrowBack = iconWrapper(svg`<path d="M20 11H7.83l5.59-5.59L12 4l-8
|
|
|
20
20
|
export const arrowDropDown = iconWrapper(svg`<path d="M7 10l5 5 5-5z"/>`);
|
|
21
21
|
export const cancel = iconWrapper(svg`<path d="M0 0h24v24H0V0z" fill="none" opacity=".87"/><path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm3.59-13L12 10.59 8.41 7 7 8.41 10.59 12 7 15.59 8.41 17 12 13.41 15.59 17 17 15.59 13.41 12 17 8.41z"/>`);
|
|
22
22
|
export const cancelFilled = iconWrapper(svg`<path d="M0 0h24v24H0z" fill="none"/><path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17 12 13.41 8.41 17 7 15.59 10.59 12 7 8.41 8.41 7 12 10.59 15.59 7 17 8.41 13.41 12 17 15.59z"/>`);
|
|
23
|
+
export const certificate = iconWrapper(svg`<path d="M4 3c-1.11 0-2 .89-2 2v10a2 2 0 0 0 2 2h8v5l3-3l3 3v-5h2a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2H4m8 2l3 2l3-2v3.5l3 1.5l-3 1.5V15l-3-2l-3 2v-3.5L9 10l3-1.5V5M4 5h5v2H4V5m0 4h3v2H4V9m0 4h5v2H4v-2Z"/>`);
|
|
23
24
|
export const check = iconWrapper(svg`<path d="m9.55 18-5.7-5.7 1.425-1.425L9.55 15.15l9.175-9.175L20.15 7.4Z"/>`);
|
|
24
25
|
export const checkBox = iconWrapper(svg`<path d="m10.6 16.2 7.05-7.05-1.4-1.4-5.65 5.65-2.85-2.85-1.4 1.4ZM5 21q-.825 0-1.413-.587Q3 19.825 3 19V5q0-.825.587-1.413Q4.175 3 5 3h14q.825 0 1.413.587Q21 4.175 21 5v14q0 .825-.587 1.413Q19.825 21 19 21Zm0-2h14V5H5v14ZM5 5v14V5Z"/>`);
|
|
25
26
|
export const checkBoxBlank = iconWrapper(svg`<path d="M5 21q-.825 0-1.413-.587Q3 19.825 3 19V5q0-.825.587-1.413Q4.175 3 5 3h14q.825 0 1.413.587Q21 4.175 21 5v14q0 .825-.587 1.413Q19.825 21 19 21Zm0-2h14V5H5v14Z"/>`);
|
|
@@ -27,7 +27,7 @@ export interface UiDropdownListSelection {
|
|
|
27
27
|
*
|
|
28
28
|
* @slot - The default slot for the dropdown trigger (button)
|
|
29
29
|
* @slot dropdown - The slot for the list.
|
|
30
|
-
* @fires select - Custom event with the selected item on the `detail.item` when the user selected an item.
|
|
30
|
+
* @fires select - Custom event with the selected item on the `detail.item` when the user selected an item. When the event is cancelled then there's no side effects (closing the dropdown)
|
|
31
31
|
* @fires dropdownopen - An event informing other dropdowns that this one was opened and other should close.
|
|
32
32
|
* @fires open - An event dispatched when the open state change through a user interaction
|
|
33
33
|
*/
|
|
@@ -91,6 +91,13 @@ export default class UiDropdownList extends LitElement {
|
|
|
91
91
|
*/
|
|
92
92
|
@property({ type: Boolean }) matchTriggerWidth?: boolean;
|
|
93
93
|
|
|
94
|
+
/**
|
|
95
|
+
* When set it closes the drop-down when `tab` button is pressed.
|
|
96
|
+
* This is not a default behavior since the drop-down content can have its own logic
|
|
97
|
+
* related to tab index.
|
|
98
|
+
*/
|
|
99
|
+
@property({ type: Boolean }) closeOnTab?: boolean;
|
|
100
|
+
|
|
94
101
|
/**
|
|
95
102
|
* The first element located in the default slot.
|
|
96
103
|
*/
|
|
@@ -145,9 +152,7 @@ export default class UiDropdownList extends LitElement {
|
|
|
145
152
|
if (e.composedPath()[0] === this) {
|
|
146
153
|
return;
|
|
147
154
|
}
|
|
148
|
-
this.
|
|
149
|
-
this.updateExpanded();
|
|
150
|
-
this.notifyOpen();
|
|
155
|
+
this.close();
|
|
151
156
|
}
|
|
152
157
|
|
|
153
158
|
protected updateExpanded(): void {
|
|
@@ -212,10 +217,15 @@ export default class UiDropdownList extends LitElement {
|
|
|
212
217
|
}
|
|
213
218
|
|
|
214
219
|
protected contentKeyDownHandler(e: KeyboardEvent): void {
|
|
215
|
-
if (e.
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
220
|
+
if (e.defaultPrevented) {
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
if (e.code === 'Escape') {
|
|
224
|
+
this.close();
|
|
225
|
+
} else if (e.code === 'Tab') {
|
|
226
|
+
if (this.closeOnTab) {
|
|
227
|
+
this.close();
|
|
228
|
+
}
|
|
219
229
|
} else if (['Enter', 'Space'].includes(e.code)) {
|
|
220
230
|
this.activate(e);
|
|
221
231
|
}
|
|
@@ -225,6 +235,17 @@ export default class UiDropdownList extends LitElement {
|
|
|
225
235
|
this.activate(e);
|
|
226
236
|
}
|
|
227
237
|
|
|
238
|
+
close(): void {
|
|
239
|
+
this.open = false;
|
|
240
|
+
this.updateExpanded();
|
|
241
|
+
this.notifyOpen();
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
protected contentCloseHandler(e: Event): void {
|
|
245
|
+
e.stopPropagation();
|
|
246
|
+
this.close();
|
|
247
|
+
}
|
|
248
|
+
|
|
228
249
|
protected override willUpdate(cp: PropertyValues<this>): void {
|
|
229
250
|
super.willUpdate(cp);
|
|
230
251
|
if ((cp.has('noOverlap') || cp.has('verticalAlign') || cp.has('horizontalAlign') || cp.has('open')) && this.open) {
|
|
@@ -272,9 +293,7 @@ export default class UiDropdownList extends LitElement {
|
|
|
272
293
|
if (inside) {
|
|
273
294
|
return;
|
|
274
295
|
}
|
|
275
|
-
this.
|
|
276
|
-
this.updateExpanded();
|
|
277
|
-
this.notifyOpen();
|
|
296
|
+
this.close();
|
|
278
297
|
}
|
|
279
298
|
|
|
280
299
|
protected toggleOpened(): void {
|
|
@@ -311,6 +330,9 @@ export default class UiDropdownList extends LitElement {
|
|
|
311
330
|
}
|
|
312
331
|
|
|
313
332
|
protected activate(e: Event): void {
|
|
333
|
+
if (e.defaultPrevented) {
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
314
336
|
const path = e.composedPath();
|
|
315
337
|
let item: HTMLElement | undefined;
|
|
316
338
|
while (!item) {
|
|
@@ -328,14 +350,18 @@ export default class UiDropdownList extends LitElement {
|
|
|
328
350
|
if (!item) {
|
|
329
351
|
return;
|
|
330
352
|
}
|
|
331
|
-
|
|
353
|
+
const event = new CustomEvent<UiDropdownListSelection>('select', {
|
|
354
|
+
cancelable: true,
|
|
355
|
+
composed: true,
|
|
332
356
|
detail: {
|
|
333
357
|
item,
|
|
334
|
-
}
|
|
335
|
-
})
|
|
336
|
-
this.
|
|
337
|
-
|
|
338
|
-
|
|
358
|
+
},
|
|
359
|
+
})
|
|
360
|
+
this.dispatchEvent(event);
|
|
361
|
+
if (event.defaultPrevented) {
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
this.close();
|
|
339
365
|
}
|
|
340
366
|
|
|
341
367
|
protected notifyOpen(): void {
|
|
@@ -385,6 +411,7 @@ export default class UiDropdownList extends LitElement {
|
|
|
385
411
|
@slotchange="${this.dropdownChanged}"
|
|
386
412
|
@keydown="${this.contentKeyDownHandler}"
|
|
387
413
|
@click="${this.contentClickHandler}"
|
|
414
|
+
@close="${this.contentCloseHandler}"
|
|
388
415
|
></slot>
|
|
389
416
|
</div>
|
|
390
417
|
`;
|
package/src/ui/list/UiList.ts
CHANGED
|
@@ -58,6 +58,15 @@ export default class UiList extends LitElement {
|
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
override focus(options?: FocusOptions): void {
|
|
62
|
+
const { activeListItem } = this;
|
|
63
|
+
if (activeListItem) {
|
|
64
|
+
activeListItem.focus(options);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
this.activateFirstItem();
|
|
68
|
+
}
|
|
69
|
+
|
|
61
70
|
override firstUpdated(changedProperties: PropertyValues): void {
|
|
62
71
|
super.firstUpdated(changedProperties);
|
|
63
72
|
|
|
@@ -66,7 +75,7 @@ export default class UiList extends LitElement {
|
|
|
66
75
|
|
|
67
76
|
activateFirstItem(): void {
|
|
68
77
|
this.activeListItem = this.getFirstItem();
|
|
69
|
-
this.activeListItem
|
|
78
|
+
this.activeListItem?.activate();
|
|
70
79
|
}
|
|
71
80
|
|
|
72
81
|
activateLastItem(): void {
|
|
@@ -318,30 +327,37 @@ export default class UiList extends LitElement {
|
|
|
318
327
|
this.manageSelection(item);
|
|
319
328
|
item.activate();
|
|
320
329
|
this.activeListItem = item;
|
|
321
|
-
this.notifySelect(item)
|
|
330
|
+
if (this.notifySelect(item)) {
|
|
331
|
+
e.preventDefault();
|
|
332
|
+
}
|
|
322
333
|
}
|
|
323
334
|
|
|
324
|
-
|
|
335
|
+
/**
|
|
336
|
+
* @param item The UiListItem that is selected.
|
|
337
|
+
* @returns True when the event was canceled.
|
|
338
|
+
*/
|
|
339
|
+
notifySelect(item: UiListItem): boolean {
|
|
325
340
|
const index = this.items.indexOf(item);
|
|
326
341
|
if (index === -1) {
|
|
327
|
-
return;
|
|
342
|
+
return false;
|
|
328
343
|
}
|
|
329
|
-
|
|
344
|
+
const event = new CustomEvent<UiListSelection>('select', {
|
|
345
|
+
cancelable: true,
|
|
330
346
|
detail: {
|
|
331
347
|
item,
|
|
332
348
|
index,
|
|
333
349
|
}
|
|
334
|
-
})
|
|
350
|
+
});
|
|
351
|
+
this.dispatchEvent(event);
|
|
352
|
+
return event.defaultPrevented;
|
|
335
353
|
}
|
|
336
354
|
|
|
337
355
|
protected manageSelection(item: UiListItem): void {
|
|
338
356
|
if (!this.selectActive) {
|
|
339
357
|
return;
|
|
340
358
|
}
|
|
341
|
-
const {
|
|
342
|
-
|
|
343
|
-
activeListItem.classList.remove('select');
|
|
344
|
-
}
|
|
359
|
+
const { items } = this;
|
|
360
|
+
items.forEach((current) => current.classList.remove('select'));
|
|
345
361
|
item.classList.add('select');
|
|
346
362
|
}
|
|
347
363
|
|
|
@@ -199,4 +199,12 @@ export class SnackNotifications {
|
|
|
199
199
|
}
|
|
200
200
|
}
|
|
201
201
|
}
|
|
202
|
+
|
|
203
|
+
static clear(): void {
|
|
204
|
+
this.stack = [];
|
|
205
|
+
if (this.current) {
|
|
206
|
+
this.current.element.parentElement?.removeChild(this.current.element);
|
|
207
|
+
this.current = undefined;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
202
210
|
}
|
|
@@ -27,14 +27,15 @@ export interface IDataTableOptions {
|
|
|
27
27
|
|
|
28
28
|
export type RenderContent = string | TemplateResult | TemplateResult[];
|
|
29
29
|
|
|
30
|
-
type DataEvents = 'header' | 'item' | 'empty' | 'activate' | 'select' | 'render';
|
|
30
|
+
type DataEvents = 'header' | 'item' | 'empty' | 'activate' | 'select' | 'render' | 'scrollend';
|
|
31
31
|
|
|
32
32
|
type CellCallback<T> = (item: T) => TemplateResult[];
|
|
33
33
|
type ActivateCallback<T> = (item: T) => void;
|
|
34
34
|
type SelectCallback = (item: string | string[]) => void;
|
|
35
|
+
type Callback = () => void;
|
|
35
36
|
|
|
36
37
|
type DataEventsMap<T> = {
|
|
37
|
-
[key in DataEvents]?: SelectCallback | CellCallback<T> | CellCallback<T[]> | ActivateCallback<T> | ActivateCallback<T[]> | {(): TemplateResult | TemplateResult[]};
|
|
38
|
+
[key in DataEvents]?: Callback | SelectCallback | CellCallback<T> | CellCallback<T[]> | ActivateCallback<T> | ActivateCallback<T[]> | {(): TemplateResult | TemplateResult[]};
|
|
38
39
|
};
|
|
39
40
|
|
|
40
41
|
export interface ICellOptions {
|
|
@@ -227,6 +228,7 @@ export class DataTable<T> {
|
|
|
227
228
|
this.handleClick = this.handleClick.bind(this);
|
|
228
229
|
this.handleKeyDown = this.handleKeyDown.bind(this);
|
|
229
230
|
this.handleFocus = this.handleFocus.bind(this);
|
|
231
|
+
this.handleListScroll = this.handleListScroll.bind(this);
|
|
230
232
|
}
|
|
231
233
|
|
|
232
234
|
addEventListener(type: 'header', listener: () => TemplateResult[]): void;
|
|
@@ -241,6 +243,8 @@ export class DataTable<T> {
|
|
|
241
243
|
|
|
242
244
|
addEventListener(type: 'select', listener: SelectCallback): void;
|
|
243
245
|
|
|
246
|
+
addEventListener(type: 'scrollend', listener: Callback): void;
|
|
247
|
+
|
|
244
248
|
addEventListener(type: DataEvents, listener: SelectCallback | CellCallback<T> | CellCallback<T[]> | ActivateCallback<T> | ActivateCallback<T[]> | {(): TemplateResult | TemplateResult[]}): void {
|
|
245
249
|
this.#listeners[type] = listener;
|
|
246
250
|
}
|
|
@@ -585,6 +589,15 @@ export class DataTable<T> {
|
|
|
585
589
|
}
|
|
586
590
|
}
|
|
587
591
|
|
|
592
|
+
protected handleListScroll(e: Event): void {
|
|
593
|
+
const list = e.target as HTMLElement;
|
|
594
|
+
const { scrollTop, offsetHeight, scrollHeight } = list;
|
|
595
|
+
const bottom = scrollTop + offsetHeight >= scrollHeight - 20; // 20 is the offset which qualifies as the end. An arbitrary number.
|
|
596
|
+
if (bottom) {
|
|
597
|
+
this.dispatchScrollEnd();
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
588
601
|
protected renderHeader(): TemplateResult {
|
|
589
602
|
const cells = this.dispatchHeader();
|
|
590
603
|
return html`
|
|
@@ -602,7 +615,7 @@ export class DataTable<T> {
|
|
|
602
615
|
const contents = !!items && !!items.length ? items.map(item => this.renderItem(item)) : this.renderEmpty();
|
|
603
616
|
const dbListener = active ? this.dblclickHandler : undefined;
|
|
604
617
|
return html`
|
|
605
|
-
<div class="data-table-body" role="rowgroup" @dblclick="${dbListener}">
|
|
618
|
+
<div class="data-table-body" role="rowgroup" @dblclick="${dbListener}" @scroll="${{ handleEvent: this.handleListScroll, passive: true }}">
|
|
606
619
|
${contents}
|
|
607
620
|
</div>`;
|
|
608
621
|
}
|
|
@@ -715,4 +728,17 @@ export class DataTable<T> {
|
|
|
715
728
|
console.warn(e);
|
|
716
729
|
}
|
|
717
730
|
}
|
|
731
|
+
|
|
732
|
+
protected dispatchScrollEnd(): void {
|
|
733
|
+
const callback = this.#listeners.scrollend as Callback;
|
|
734
|
+
if (!callback) {
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
try {
|
|
738
|
+
callback();
|
|
739
|
+
} catch (e) {
|
|
740
|
+
// eslint-disable-next-line no-console
|
|
741
|
+
console.warn(e);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
718
744
|
}
|