@eclipse-lyra/extension-dataviewer 0.0.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/api.d.ts +13 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +5 -0
- package/dist/api.js.map +1 -0
- package/dist/dataview-part.d.ts +23 -0
- package/dist/dataview-part.d.ts.map +1 -0
- package/dist/dataviewer-extension.d.ts +1 -0
- package/dist/dataviewer-extension.d.ts.map +1 -0
- package/dist/dataviewer-service.d.ts +13 -0
- package/dist/dataviewer-service.d.ts.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +294 -0
- package/dist/index.js.map +1 -0
- package/dist/parse-csv.d.ts +6 -0
- package/dist/parse-csv.d.ts.map +1 -0
- package/package.json +34 -0
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface TabularData {
|
|
2
|
+
columns: string[];
|
|
3
|
+
rows: unknown[][];
|
|
4
|
+
}
|
|
5
|
+
export interface DataView {
|
|
6
|
+
id?: string;
|
|
7
|
+
title: string;
|
|
8
|
+
data: TabularData;
|
|
9
|
+
source?: string;
|
|
10
|
+
createdAt?: number;
|
|
11
|
+
}
|
|
12
|
+
export declare const TOPIC_DATAVIEW_PUBLISH = "dataview/publish";
|
|
13
|
+
//# sourceMappingURL=api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,IAAI,EAAE,OAAO,EAAE,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,QAAQ;IACvB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,WAAW,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,sBAAsB,qBAAqB,CAAC"}
|
package/dist/api.js
ADDED
package/dist/api.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sources":["../src/api.ts"],"sourcesContent":["export interface TabularData {\n columns: string[];\n rows: unknown[][];\n}\n\nexport interface DataView {\n id?: string;\n title: string;\n data: TabularData;\n source?: string;\n createdAt?: number;\n}\n\nexport const TOPIC_DATAVIEW_PUBLISH = 'dataview/publish';\n"],"names":[],"mappings":"AAaO,MAAM,yBAAyB;"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { LyraPart } from '@eclipse-lyra/core';
|
|
2
|
+
import { DataView } from './api';
|
|
3
|
+
export declare class DataViewPart extends LyraPart {
|
|
4
|
+
dataview: DataView | null;
|
|
5
|
+
private persistedList;
|
|
6
|
+
private selectedStorageKey;
|
|
7
|
+
private selectedFromDropdown;
|
|
8
|
+
private loadingList;
|
|
9
|
+
private get displayed();
|
|
10
|
+
protected doInitUI(): Promise<void>;
|
|
11
|
+
private refreshPersistedList;
|
|
12
|
+
private onDropdownChange;
|
|
13
|
+
protected renderToolbar(): import('lit-html').TemplateResult<1>;
|
|
14
|
+
private renderTable;
|
|
15
|
+
render(): import('lit-html').TemplateResult<1>;
|
|
16
|
+
static styles: import('lit').CSSResult;
|
|
17
|
+
}
|
|
18
|
+
declare global {
|
|
19
|
+
interface HTMLElementTagNameMap {
|
|
20
|
+
'lyra-dataview': DataViewPart;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=dataview-part.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dataview-part.d.ts","sourceRoot":"","sources":["../src/dataview-part.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAG9C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAGtC,qBACa,YAAa,SAAQ,QAAQ;IAExC,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAQ;IAGjC,OAAO,CAAC,aAAa,CAAgD;IAGrE,OAAO,CAAC,kBAAkB,CAAM;IAGhC,OAAO,CAAC,oBAAoB,CAAyB;IAGrD,OAAO,CAAC,WAAW,CAAQ;IAE3B,OAAO,KAAK,SAAS,GAEpB;cAEe,QAAQ;YAKV,oBAAoB;YAcpB,gBAAgB;IAmB9B,SAAS,CAAC,aAAa;IAmBvB,OAAO,CAAC,WAAW;IA2BnB,MAAM;IAMN,MAAM,CAAC,MAAM,0BA2CX;CACH;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,eAAe,EAAE,YAAY,CAAC;KAC/B;CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=dataviewer-extension.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dataviewer-extension.d.ts","sourceRoot":"","sources":["../src/dataviewer-extension.ts"],"names":[],"mappings":"AAUA,OAAO,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { DataView } from './api';
|
|
2
|
+
export declare class DataviewerService {
|
|
3
|
+
private subscriptionToken?;
|
|
4
|
+
init(): void;
|
|
5
|
+
private handlePublish;
|
|
6
|
+
listViews(): Promise<Array<DataView & {
|
|
7
|
+
storageKey: string;
|
|
8
|
+
}>>;
|
|
9
|
+
getView(storageKey: string): Promise<DataView | null>;
|
|
10
|
+
deleteView(storageKey: string): Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
export declare const dataviewerService: DataviewerService;
|
|
13
|
+
//# sourceMappingURL=dataviewer-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dataviewer-service.d.ts","sourceRoot":"","sources":["../src/dataviewer-service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAMtC,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,iBAAiB,CAAC,CAAS;IAEnC,IAAI,IAAI,IAAI;YAOE,aAAa;IAkBrB,SAAS,IAAI,OAAO,CAAC,KAAK,CAAC,QAAQ,GAAG;QAAE,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAW9D,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAKrD,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAMpD;AAED,eAAO,MAAM,iBAAiB,mBAA0B,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,wBAAwB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import { css, html } from "lit";
|
|
2
|
+
import { subscribe, persistenceService, LyraPart, toastError, rootContext, contributionRegistry, PANEL_BOTTOM, editorRegistry, File } from "@eclipse-lyra/core";
|
|
3
|
+
import { v4 } from "@eclipse-lyra/core/externals/third-party";
|
|
4
|
+
import { TOPIC_DATAVIEW_PUBLISH } from "./api.js";
|
|
5
|
+
import { property, state, customElement } from "lit/decorators.js";
|
|
6
|
+
import Papa from "papaparse";
|
|
7
|
+
const KEY_PREFIX = "dataview/";
|
|
8
|
+
const KEY_INDEX = KEY_PREFIX + "index";
|
|
9
|
+
class DataviewerService {
|
|
10
|
+
init() {
|
|
11
|
+
if (this.subscriptionToken !== void 0) return;
|
|
12
|
+
this.subscriptionToken = subscribe(TOPIC_DATAVIEW_PUBLISH, (payload) => {
|
|
13
|
+
void this.handlePublish(payload);
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
async handlePublish(payload) {
|
|
17
|
+
const storageKey = v4();
|
|
18
|
+
const entry = {
|
|
19
|
+
id: payload.id ?? storageKey,
|
|
20
|
+
title: payload.title,
|
|
21
|
+
data: payload.data,
|
|
22
|
+
source: payload.source,
|
|
23
|
+
createdAt: Date.now()
|
|
24
|
+
};
|
|
25
|
+
await persistenceService.persistObject(KEY_PREFIX + storageKey, entry);
|
|
26
|
+
const index = await persistenceService.getObject(KEY_INDEX);
|
|
27
|
+
const list = Array.isArray(index) ? index : [];
|
|
28
|
+
if (!list.includes(storageKey)) {
|
|
29
|
+
list.push(storageKey);
|
|
30
|
+
await persistenceService.persistObject(KEY_INDEX, list);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async listViews() {
|
|
34
|
+
const index = await persistenceService.getObject(KEY_INDEX);
|
|
35
|
+
const keys = Array.isArray(index) ? index : [];
|
|
36
|
+
const entries = [];
|
|
37
|
+
for (const storageKey of keys) {
|
|
38
|
+
const entry = await persistenceService.getObject(KEY_PREFIX + storageKey);
|
|
39
|
+
if (entry != null) entries.push({ ...entry, storageKey });
|
|
40
|
+
}
|
|
41
|
+
return entries.sort((a, b) => (a.createdAt ?? 0) - (b.createdAt ?? 0));
|
|
42
|
+
}
|
|
43
|
+
async getView(storageKey) {
|
|
44
|
+
const entry = await persistenceService.getObject(KEY_PREFIX + storageKey);
|
|
45
|
+
return entry ?? null;
|
|
46
|
+
}
|
|
47
|
+
async deleteView(storageKey) {
|
|
48
|
+
const index = await persistenceService.getObject(KEY_INDEX);
|
|
49
|
+
const list = Array.isArray(index) ? index.filter((x) => x !== storageKey) : [];
|
|
50
|
+
await persistenceService.persistObject(KEY_INDEX, list);
|
|
51
|
+
await persistenceService.persistObject(KEY_PREFIX + storageKey, null);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const dataviewerService = new DataviewerService();
|
|
55
|
+
var __defProp = Object.defineProperty;
|
|
56
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
57
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
58
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
59
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
60
|
+
if (decorator = decorators[i])
|
|
61
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
62
|
+
if (kind && result) __defProp(target, key, result);
|
|
63
|
+
return result;
|
|
64
|
+
};
|
|
65
|
+
let DataViewPart = class extends LyraPart {
|
|
66
|
+
constructor() {
|
|
67
|
+
super(...arguments);
|
|
68
|
+
this.dataview = null;
|
|
69
|
+
this.persistedList = [];
|
|
70
|
+
this.selectedStorageKey = "";
|
|
71
|
+
this.selectedFromDropdown = null;
|
|
72
|
+
this.loadingList = true;
|
|
73
|
+
}
|
|
74
|
+
get displayed() {
|
|
75
|
+
return this.selectedFromDropdown ?? this.dataview;
|
|
76
|
+
}
|
|
77
|
+
async doInitUI() {
|
|
78
|
+
this.subscribe(TOPIC_DATAVIEW_PUBLISH, () => this.refreshPersistedList());
|
|
79
|
+
await this.refreshPersistedList();
|
|
80
|
+
}
|
|
81
|
+
async refreshPersistedList() {
|
|
82
|
+
this.loadingList = true;
|
|
83
|
+
this.requestUpdate();
|
|
84
|
+
try {
|
|
85
|
+
this.persistedList = await dataviewerService.listViews();
|
|
86
|
+
} catch (e) {
|
|
87
|
+
toastError(e instanceof Error ? e.message : String(e));
|
|
88
|
+
this.persistedList = [];
|
|
89
|
+
} finally {
|
|
90
|
+
this.loadingList = false;
|
|
91
|
+
this.requestUpdate();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
async onDropdownChange(e) {
|
|
95
|
+
const select = e.target;
|
|
96
|
+
const storageKey = select?.value ?? "";
|
|
97
|
+
this.selectedStorageKey = storageKey;
|
|
98
|
+
if (!storageKey) {
|
|
99
|
+
this.selectedFromDropdown = null;
|
|
100
|
+
this.requestUpdate();
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const entry = await dataviewerService.getView(storageKey);
|
|
104
|
+
if (!entry) {
|
|
105
|
+
this.selectedFromDropdown = null;
|
|
106
|
+
this.requestUpdate();
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
this.selectedFromDropdown = entry;
|
|
110
|
+
this.requestUpdate();
|
|
111
|
+
}
|
|
112
|
+
renderToolbar() {
|
|
113
|
+
return html`
|
|
114
|
+
<wa-select
|
|
115
|
+
class="dataview-select"
|
|
116
|
+
size="small"
|
|
117
|
+
.value=${this.selectedStorageKey}
|
|
118
|
+
title="Persisted data views"
|
|
119
|
+
?disabled=${this.loadingList}
|
|
120
|
+
@change=${(e) => this.onDropdownChange(e)}
|
|
121
|
+
>
|
|
122
|
+
<wa-option value="">No data</wa-option>
|
|
123
|
+
${this.persistedList.map(
|
|
124
|
+
(entry) => html`<wa-option value=${entry.storageKey}>${entry.title}</wa-option>`
|
|
125
|
+
)}
|
|
126
|
+
</wa-select>
|
|
127
|
+
`;
|
|
128
|
+
}
|
|
129
|
+
renderTable(dv) {
|
|
130
|
+
const { columns, rows } = dv.data;
|
|
131
|
+
if (columns.length === 0 && rows.length === 0) {
|
|
132
|
+
return html`<div class="result-empty">No data.</div>`;
|
|
133
|
+
}
|
|
134
|
+
return html`
|
|
135
|
+
<div class="result-table-wrap">
|
|
136
|
+
<table class="result-table">
|
|
137
|
+
<thead>
|
|
138
|
+
<tr>
|
|
139
|
+
${columns.map((col) => html`<th>${col}</th>`)}
|
|
140
|
+
</tr>
|
|
141
|
+
</thead>
|
|
142
|
+
<tbody>
|
|
143
|
+
${rows.map(
|
|
144
|
+
(row) => html`
|
|
145
|
+
<tr>
|
|
146
|
+
${row.map((cell) => html`<td>${String(cell ?? "")}</td>`)}
|
|
147
|
+
</tr>
|
|
148
|
+
`
|
|
149
|
+
)}
|
|
150
|
+
</tbody>
|
|
151
|
+
</table>
|
|
152
|
+
</div>
|
|
153
|
+
`;
|
|
154
|
+
}
|
|
155
|
+
render() {
|
|
156
|
+
const dv = this.displayed;
|
|
157
|
+
if (dv != null) return this.renderTable(dv);
|
|
158
|
+
return html`<div class="result-empty">No data.</div>`;
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
DataViewPart.styles = css`
|
|
162
|
+
:host {
|
|
163
|
+
display: flex;
|
|
164
|
+
flex-direction: column;
|
|
165
|
+
height: 100%;
|
|
166
|
+
overflow: hidden;
|
|
167
|
+
}
|
|
168
|
+
.dataview-select {
|
|
169
|
+
min-width: 12rem;
|
|
170
|
+
}
|
|
171
|
+
.result-table-wrap {
|
|
172
|
+
overflow: auto;
|
|
173
|
+
padding: 0.75rem;
|
|
174
|
+
height: 100%;
|
|
175
|
+
box-sizing: border-box;
|
|
176
|
+
}
|
|
177
|
+
.result-table {
|
|
178
|
+
width: 100%;
|
|
179
|
+
border-collapse: collapse;
|
|
180
|
+
font-size: 0.8125rem;
|
|
181
|
+
}
|
|
182
|
+
.result-table th,
|
|
183
|
+
.result-table td {
|
|
184
|
+
padding: 0.35rem 0.75rem;
|
|
185
|
+
text-align: left;
|
|
186
|
+
border-bottom: 1px solid var(--wa-color-neutral-200, #e5e7eb);
|
|
187
|
+
}
|
|
188
|
+
.result-table th {
|
|
189
|
+
font-weight: 600;
|
|
190
|
+
background: var(--wa-color-neutral-100, #f3f4f6);
|
|
191
|
+
}
|
|
192
|
+
.result-table tbody tr:hover {
|
|
193
|
+
background: var(--wa-color-neutral-50, #f9fafb);
|
|
194
|
+
}
|
|
195
|
+
.result-empty {
|
|
196
|
+
flex: 1;
|
|
197
|
+
display: flex;
|
|
198
|
+
align-items: center;
|
|
199
|
+
justify-content: center;
|
|
200
|
+
padding: 1rem;
|
|
201
|
+
color: var(--wa-color-neutral-500, #6b7280);
|
|
202
|
+
font-size: 0.875rem;
|
|
203
|
+
}
|
|
204
|
+
`;
|
|
205
|
+
__decorateClass([
|
|
206
|
+
property({ attribute: false })
|
|
207
|
+
], DataViewPart.prototype, "dataview", 2);
|
|
208
|
+
__decorateClass([
|
|
209
|
+
state()
|
|
210
|
+
], DataViewPart.prototype, "persistedList", 2);
|
|
211
|
+
__decorateClass([
|
|
212
|
+
state()
|
|
213
|
+
], DataViewPart.prototype, "selectedStorageKey", 2);
|
|
214
|
+
__decorateClass([
|
|
215
|
+
state()
|
|
216
|
+
], DataViewPart.prototype, "selectedFromDropdown", 2);
|
|
217
|
+
__decorateClass([
|
|
218
|
+
state()
|
|
219
|
+
], DataViewPart.prototype, "loadingList", 2);
|
|
220
|
+
DataViewPart = __decorateClass([
|
|
221
|
+
customElement("lyra-dataview")
|
|
222
|
+
], DataViewPart);
|
|
223
|
+
function parseCsv(text) {
|
|
224
|
+
const result = Papa.parse(text, {
|
|
225
|
+
header: true,
|
|
226
|
+
skipEmptyLines: true
|
|
227
|
+
});
|
|
228
|
+
const columns = result.meta.fields ?? [];
|
|
229
|
+
const rows = result.data.map(
|
|
230
|
+
(row) => columns.map((col) => row[col])
|
|
231
|
+
);
|
|
232
|
+
return { columns, rows };
|
|
233
|
+
}
|
|
234
|
+
const DATAVIEW_KEY_PREFIX = ".dataview/";
|
|
235
|
+
dataviewerService.init();
|
|
236
|
+
rootContext.put("dataviewerService", dataviewerService);
|
|
237
|
+
contributionRegistry.registerContribution(PANEL_BOTTOM, {
|
|
238
|
+
name: "view.dataviewer",
|
|
239
|
+
label: "Data Views",
|
|
240
|
+
icon: "table",
|
|
241
|
+
component: (id) => html`<lyra-dataview id="${id}"></lyra-dataview>`
|
|
242
|
+
});
|
|
243
|
+
editorRegistry.registerEditorInputHandler({
|
|
244
|
+
editorId: "system.dataviewer",
|
|
245
|
+
label: "Data View",
|
|
246
|
+
icon: "table",
|
|
247
|
+
ranking: 900,
|
|
248
|
+
canHandle: (input) => typeof input?.key === "string" && input.key.startsWith(DATAVIEW_KEY_PREFIX),
|
|
249
|
+
handle: async (input) => {
|
|
250
|
+
const storageKey = input.data?.storageKey ?? input.key?.replace(DATAVIEW_KEY_PREFIX, "");
|
|
251
|
+
const entry = await dataviewerService.getView(storageKey);
|
|
252
|
+
if (!entry) {
|
|
253
|
+
return Promise.reject(new Error("Data view not found"));
|
|
254
|
+
}
|
|
255
|
+
const title = entry.title || `Data: ${entry.id}`;
|
|
256
|
+
return {
|
|
257
|
+
key: input.key,
|
|
258
|
+
title,
|
|
259
|
+
data: entry,
|
|
260
|
+
icon: "table",
|
|
261
|
+
noOverflow: false,
|
|
262
|
+
state: {},
|
|
263
|
+
component: () => html`<lyra-dataview .dataview=${entry}></lyra-dataview>`
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
editorRegistry.registerEditorInputHandler({
|
|
268
|
+
editorId: "system.dataviewer-table",
|
|
269
|
+
label: "Table",
|
|
270
|
+
icon: "table",
|
|
271
|
+
ranking: 800,
|
|
272
|
+
canHandle: (input) => {
|
|
273
|
+
if (!(input instanceof File)) return false;
|
|
274
|
+
const lower = input.getName().toLowerCase();
|
|
275
|
+
return lower.endsWith(".csv") || lower.endsWith(".tsv");
|
|
276
|
+
},
|
|
277
|
+
handle: async (input) => {
|
|
278
|
+
const name = input.getName();
|
|
279
|
+
const text = await input.getContents();
|
|
280
|
+
const { columns, rows } = parseCsv(text ?? "");
|
|
281
|
+
const dataView = { title: name, data: { columns, rows } };
|
|
282
|
+
const editorInput = {
|
|
283
|
+
title: name,
|
|
284
|
+
data: dataView,
|
|
285
|
+
key: name,
|
|
286
|
+
icon: "table",
|
|
287
|
+
noOverflow: false,
|
|
288
|
+
state: {},
|
|
289
|
+
component: () => html`<lyra-dataview .dataview=${dataView}></lyra-dataview>`
|
|
290
|
+
};
|
|
291
|
+
return editorInput;
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/dataviewer-service.ts","../src/dataview-part.ts","../src/parse-csv.ts","../src/dataviewer-extension.ts"],"sourcesContent":["import { persistenceService, subscribe } from '@eclipse-lyra/core';\nimport { v4 } from '@eclipse-lyra/core/externals/third-party';\nimport type { DataView } from './api';\nimport { TOPIC_DATAVIEW_PUBLISH } from './api';\n\nconst KEY_PREFIX = 'dataview/';\nconst KEY_INDEX = KEY_PREFIX + 'index';\n\nexport class DataviewerService {\n private subscriptionToken?: string;\n\n init(): void {\n if (this.subscriptionToken !== undefined) return;\n this.subscriptionToken = subscribe(TOPIC_DATAVIEW_PUBLISH, (payload: DataView) => {\n void this.handlePublish(payload);\n });\n }\n\n private async handlePublish(payload: DataView): Promise<void> {\n const storageKey = v4();\n const entry: DataView = {\n id: payload.id ?? storageKey,\n title: payload.title,\n data: payload.data,\n source: payload.source,\n createdAt: Date.now(),\n };\n await persistenceService.persistObject(KEY_PREFIX + storageKey, entry);\n const index = (await persistenceService.getObject(KEY_INDEX)) as string[] | undefined;\n const list = Array.isArray(index) ? index : [];\n if (!list.includes(storageKey)) {\n list.push(storageKey);\n await persistenceService.persistObject(KEY_INDEX, list);\n }\n }\n\n async listViews(): Promise<Array<DataView & { storageKey: string }>> {\n const index = (await persistenceService.getObject(KEY_INDEX)) as string[] | undefined;\n const keys = Array.isArray(index) ? index : [];\n const entries: Array<DataView & { storageKey: string }> = [];\n for (const storageKey of keys) {\n const entry = await persistenceService.getObject(KEY_PREFIX + storageKey);\n if (entry != null) entries.push({ ...entry, storageKey });\n }\n return entries.sort((a, b) => (a.createdAt ?? 0) - (b.createdAt ?? 0));\n }\n\n async getView(storageKey: string): Promise<DataView | null> {\n const entry = await persistenceService.getObject(KEY_PREFIX + storageKey);\n return entry ?? null;\n }\n\n async deleteView(storageKey: string): Promise<void> {\n const index = (await persistenceService.getObject(KEY_INDEX)) as string[] | undefined;\n const list = Array.isArray(index) ? index.filter((x) => x !== storageKey) : [];\n await persistenceService.persistObject(KEY_INDEX, list);\n await persistenceService.persistObject(KEY_PREFIX + storageKey, null);\n }\n}\n\nexport const dataviewerService = new DataviewerService();\n","import { customElement, property, state } from 'lit/decorators.js';\nimport { css, html } from 'lit';\nimport { LyraPart } from '@eclipse-lyra/core';\nimport { toastError } from '@eclipse-lyra/core';\nimport { dataviewerService } from './dataviewer-service';\nimport type { DataView } from './api';\nimport { TOPIC_DATAVIEW_PUBLISH } from './api';\n\n@customElement('lyra-dataview')\nexport class DataViewPart extends LyraPart {\n @property({ attribute: false })\n dataview: DataView | null = null;\n\n @state()\n private persistedList: Array<DataView & { storageKey: string }> = [];\n\n @state()\n private selectedStorageKey = '';\n\n @state()\n private selectedFromDropdown: DataView | null = null;\n\n @state()\n private loadingList = true;\n\n private get displayed(): DataView | null {\n return this.selectedFromDropdown ?? this.dataview;\n }\n\n protected async doInitUI() {\n this.subscribe(TOPIC_DATAVIEW_PUBLISH, () => this.refreshPersistedList());\n await this.refreshPersistedList();\n }\n\n private async refreshPersistedList(): Promise<void> {\n this.loadingList = true;\n this.requestUpdate();\n try {\n this.persistedList = await dataviewerService.listViews();\n } catch (e) {\n toastError(e instanceof Error ? e.message : String(e));\n this.persistedList = [];\n } finally {\n this.loadingList = false;\n this.requestUpdate();\n }\n }\n\n private async onDropdownChange(e: Event): Promise<void> {\n const select = e.target as HTMLSelectElement & { value: string };\n const storageKey = select?.value ?? '';\n this.selectedStorageKey = storageKey;\n if (!storageKey) {\n this.selectedFromDropdown = null;\n this.requestUpdate();\n return;\n }\n const entry = await dataviewerService.getView(storageKey);\n if (!entry) {\n this.selectedFromDropdown = null;\n this.requestUpdate();\n return;\n }\n this.selectedFromDropdown = entry;\n this.requestUpdate();\n }\n\n protected renderToolbar() {\n return html`\n <wa-select\n class=\"dataview-select\"\n size=\"small\"\n .value=${this.selectedStorageKey}\n title=\"Persisted data views\"\n ?disabled=${this.loadingList}\n @change=${(e: Event) => this.onDropdownChange(e)}\n >\n <wa-option value=\"\">No data</wa-option>\n ${this.persistedList.map(\n (entry) =>\n html`<wa-option value=${entry.storageKey}>${entry.title}</wa-option>`\n )}\n </wa-select>\n `;\n }\n\n private renderTable(dv: DataView) {\n const { columns, rows } = dv.data;\n if (columns.length === 0 && rows.length === 0) {\n return html`<div class=\"result-empty\">No data.</div>`;\n }\n return html`\n <div class=\"result-table-wrap\">\n <table class=\"result-table\">\n <thead>\n <tr>\n ${columns.map((col) => html`<th>${col}</th>`)}\n </tr>\n </thead>\n <tbody>\n ${rows.map(\n (row) => html`\n <tr>\n ${row.map((cell) => html`<td>${String(cell ?? '')}</td>`)}\n </tr>\n `\n )}\n </tbody>\n </table>\n </div>\n `;\n }\n\n render() {\n const dv = this.displayed;\n if (dv != null) return this.renderTable(dv);\n return html`<div class=\"result-empty\">No data.</div>`;\n }\n\n static styles = css`\n :host {\n display: flex;\n flex-direction: column;\n height: 100%;\n overflow: hidden;\n }\n .dataview-select {\n min-width: 12rem;\n }\n .result-table-wrap {\n overflow: auto;\n padding: 0.75rem;\n height: 100%;\n box-sizing: border-box;\n }\n .result-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 0.8125rem;\n }\n .result-table th,\n .result-table td {\n padding: 0.35rem 0.75rem;\n text-align: left;\n border-bottom: 1px solid var(--wa-color-neutral-200, #e5e7eb);\n }\n .result-table th {\n font-weight: 600;\n background: var(--wa-color-neutral-100, #f3f4f6);\n }\n .result-table tbody tr:hover {\n background: var(--wa-color-neutral-50, #f9fafb);\n }\n .result-empty {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 1rem;\n color: var(--wa-color-neutral-500, #6b7280);\n font-size: 0.875rem;\n }\n `;\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'lyra-dataview': DataViewPart;\n }\n}\n","import Papa from 'papaparse';\n\n/** Parses CSV-like (comma-, tab-, or other delimited) text; delimiter is auto-detected. */\nexport function parseCsv(text: string): { columns: string[]; rows: unknown[][] } {\n const result = Papa.parse<Record<string, unknown>>(text, {\n header: true,\n skipEmptyLines: true,\n });\n const columns = result.meta.fields ?? [];\n const rows = result.data.map((row: Record<string, unknown>) =>\n columns.map((col: string) => row[col])\n );\n return { columns, rows };\n}\n","import { html } from 'lit';\nimport {\n rootContext,\n editorRegistry,\n contributionRegistry,\n File,\n type EditorInput,\n} from '@eclipse-lyra/core';\nimport { PANEL_BOTTOM } from '@eclipse-lyra/core';\nimport { dataviewerService } from './dataviewer-service';\nimport './dataview-part';\nimport type { DataView } from './api';\nimport { parseCsv } from './parse-csv';\n\nconst DATAVIEW_KEY_PREFIX = '.dataview/';\n\ndataviewerService.init();\nrootContext.put('dataviewerService', dataviewerService);\n\ncontributionRegistry.registerContribution(PANEL_BOTTOM, {\n name: 'view.dataviewer',\n label: 'Data Views',\n icon: 'table',\n component: (id: string) => html`<lyra-dataview id=\"${id}\"></lyra-dataview>`,\n});\n\neditorRegistry.registerEditorInputHandler({\n editorId: 'system.dataviewer',\n label: 'Data View',\n icon: 'table',\n ranking: 900,\n canHandle: (input: unknown) =>\n typeof (input as EditorInput)?.key === 'string' &&\n (input as EditorInput).key.startsWith(DATAVIEW_KEY_PREFIX),\n handle: async (input: EditorInput) => {\n const storageKey = (input.data?.storageKey as string) ?? (input.key?.replace(DATAVIEW_KEY_PREFIX, '') as string);\n const entry = await dataviewerService.getView(storageKey);\n if (!entry) {\n return Promise.reject(new Error('Data view not found'));\n }\n const title = entry.title || `Data: ${entry.id}`;\n return {\n key: input.key,\n title,\n data: entry,\n icon: 'table',\n noOverflow: false,\n state: {},\n component: () =>\n html`<lyra-dataview .dataview=${entry}></lyra-dataview>`,\n } as EditorInput;\n },\n});\n\neditorRegistry.registerEditorInputHandler({\n editorId: 'system.dataviewer-table',\n label: 'Table',\n icon: 'table',\n ranking: 800,\n canHandle: (input: unknown) => {\n if (!(input instanceof File)) return false;\n const lower = input.getName().toLowerCase();\n return lower.endsWith('.csv') || lower.endsWith('.tsv');\n },\n handle: async (input: File) => {\n const name = input.getName();\n const text = (await input.getContents()) as string;\n const { columns, rows } = parseCsv(text ?? '');\n const dataView: DataView = { title: name, data: { columns, rows } };\n const editorInput: EditorInput = {\n title: name,\n data: dataView,\n key: name,\n icon: 'table',\n noOverflow: false,\n state: {},\n component: () => html`<lyra-dataview .dataview=${dataView}></lyra-dataview>`,\n };\n return editorInput;\n },\n});\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse-csv.d.ts","sourceRoot":"","sources":["../src/parse-csv.ts"],"names":[],"mappings":"AAEA,2FAA2F;AAC3F,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,IAAI,EAAE,OAAO,EAAE,EAAE,CAAA;CAAE,CAU/E"}
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@eclipse-lyra/extension-dataviewer",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./api": {
|
|
14
|
+
"types": "./dist/api.d.ts",
|
|
15
|
+
"import": "./dist/api.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "vite build"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@eclipse-lyra/core": "*",
|
|
26
|
+
"papaparse": "^5.5.3"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/papaparse": "^5.5.2",
|
|
30
|
+
"typescript": "^5.9.3",
|
|
31
|
+
"vite": "^7.1.12",
|
|
32
|
+
"vite-plugin-dts": "^4.5.4"
|
|
33
|
+
}
|
|
34
|
+
}
|