Skip to content

Commit

Permalink
Merge pull request #2386 from codefori/fix/file_selector
Browse files Browse the repository at this point in the history
Implement custom file picker functionality in CustomUI
  • Loading branch information
sebjulliand authored Dec 2, 2024
2 parents e0d14c0 + 06c30a9 commit 27160a2
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 91 deletions.
89 changes: 48 additions & 41 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2982,8 +2982,8 @@
"webpack-cli": "^4.5.0"
},
"dependencies": {
"@bendera/vscode-webview-elements": "^0.12.0",
"@ibm/ibmi-eventf-parser": "^1.0.2",
"@vscode-elements/elements": "^1.9.0",
"adm-zip": "^0.5.14",
"crc-32": "https://cdn.sheetjs.com/crc-32-latest/crc-32-latest.tgz",
"csv": "^6.2.1",
Expand Down
125 changes: 80 additions & 45 deletions src/api/CustomUI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import vscode from 'vscode';

//Webpack is returning this as a string
const vscodeweb = require(`@bendera/vscode-webview-elements/dist/bundled`);
const vscodeweb = require(`@vscode-elements/elements/dist/bundled`);

type PanelOptions = {
fullWidth?: boolean
Expand Down Expand Up @@ -36,6 +36,11 @@ export interface ComplexTab {
fields: Field[];
}

interface WebviewMessageRequest {
type: "submit"|"file";
data?: any;
}

export class Section {
readonly fields: Field[] = [];

Expand Down Expand Up @@ -139,13 +144,13 @@ export class CustomUI extends Section {
* @param callback
* @returns a Promise<Page<T>> if no callback is provided
*/
loadPage<T>(title: string, callback?: (page: Page<T>) => void): Promise<Page<T>> | undefined {
loadPage<T>(title: string): Promise<Page<T>> | undefined {
const webview = openedWebviews.get(title);
if (webview) {
webview.reveal();
}
else {
return this.createPage(title, callback);
return this.createPage(title);
}
}

Expand All @@ -154,7 +159,7 @@ export class CustomUI extends Section {
return this;
}

private createPage<T>(title: string, callback?: (page: Page<T>) => void): Promise<Page<T>> | undefined {
private createPage<T>(title: string): Promise<Page<T>> | undefined {
const panel = vscode.window.createWebviewPanel(
`custom`,
title,
Expand All @@ -172,39 +177,44 @@ export class CustomUI extends Section {

openedWebviews.set(title, panel);

if (callback) {
const page = new Promise<Page<T>>((resolve) => {
panel.webview.onDidReceiveMessage(
message => {
didSubmit = true;
callback({ panel, data: message });
(message: WebviewMessageRequest) => {
if (message.type && message.data) {
switch (message.type) {
case `submit`:
didSubmit = true;
resolve({ panel, data: message.data });
break;

case `file`:
const resultField = message.data.field;
if (resultField) {
vscode.window.showOpenDialog({
canSelectFiles: true,
canSelectMany: false,
canSelectFolders: false,
}).then(result => {
if (result) {
panel.webview.postMessage({ type: `update`, field: resultField, value: result[0].fsPath });
}
});
}
break;
}
}
}
);

panel.onDidDispose(() => {
openedWebviews.delete(title);
if (!didSubmit) {
callback({ panel });
}
});
} else {
const page = new Promise<Page<T>>((resolve) => {
panel.webview.onDidReceiveMessage(
message => {
didSubmit = true;
resolve({ panel, data: message });
}
);

panel.onDidDispose(() => {
openedWebviews.delete(title);
if (!didSubmit) {
resolve({ panel });
}
});
resolve({ panel });
}
});
});

return page;
}
return page;
}

private getHTML(panel: vscode.WebviewPanel, title: string) {
Expand Down Expand Up @@ -278,6 +288,26 @@ export class CustomUI extends Section {
// Fields that have value which can be returned
const submitfields = [${allFields.filter(field => !notInputFields.includes(field.type)).map(field => `'${field.id}'`).join(`,`)}];
window.addEventListener('message', event => {
const response = event.data;
if (response.type === 'update') {
const newValue = response.value;
const field = document.getElementById(response.field);
if (response.field && response.value) {
const field = document.getElementById(response.field);
if (field) {
field.value = newValue;
let innerInput = field.shadowRoot.querySelector("input");
if (innerInput) {
innerInput.value = newValue;
}
}
validateInputs(response.field);
}
}
});
const validateInputs = (optionalId) => {
const testFields = optionalId ? inputFields.filter(theField => theField.id === optionalId) : inputFields
Expand Down Expand Up @@ -342,9 +372,20 @@ export class CustomUI extends Section {
data[checkbox] = (data[checkbox] && data[checkbox].length >= 1);
}
vscode.postMessage(data);
vscode.postMessage({ type: 'submit', data });
};
const treeItemSubmit = (treeId, value) => {
vscode.postMessage({ type: 'submit', data: {treeId, value} });
}
const doFileRequest = (event, fieldId) => {
if (event)
event.preventDefault();
vscode.postMessage({ type: 'file', data: {field: fieldId} });
}
// Setup the input fields for validation
for (const field of inputFields) {
const fieldElement = document.getElementById(field.id);
Expand Down Expand Up @@ -395,18 +436,10 @@ export class CustomUI extends Section {
// This is used to read the file in order to get the real path.
for (const field of filefields) {
document.getElementById(field)
.addEventListener('vsc-change', (e) => {
const VirtualField = document.getElementById(e.target.id)
let input = VirtualField.shadowRoot.querySelector("input");
for (let file of Array.from(input.files)) {
let reader = new FileReader();
reader.addEventListener("load", () => {
document.getElementById(e.target.id).setAttribute("value", file.path)
});
reader.readAsText(file);
}
})
let fileButton = document.getElementById(field + '-file');
if (fileButton) {
fileButton.onclick = (event) => doFileRequest(event, field);
}
}
document.addEventListener('DOMContentLoaded', () => {
Expand All @@ -416,10 +449,10 @@ export class CustomUI extends Section {
return /*js*/`
currentTree = document.getElementById('${tree.id}');
currentTree.data = ${JSON.stringify(tree.treeList)};
currentTree.addEventListener('vsc-select', (event) => {
currentTree.addEventListener('vsc-tree-select', (event) => {
console.log(JSON.stringify(event.detail));
if (event.detail.itemType === 'leaf') {
vscode.postMessage({'${tree.id}': event.detail.value});
treeItemSubmit('${tree.id}', event.detail.value);
}
});
`
Expand Down Expand Up @@ -496,7 +529,7 @@ export class Field {
case `checkbox`:
return /* html */`
<vscode-form-group variant="settings-group">
<vscode-checkbox id="${this.id}" name="${this.id}" ${this.default === `checked` ? `checked` : ``}><vscode-label>${this.label}</vscode-label></vscode-checkbox>
<vscode-checkbox id="${this.id}" name="${this.id}" ${this.default === `checked` ? `checked` : ``} label="${this.label}"></vscode-checkbox>
${this.renderDescription()}
</vscode-form-group>`;

Expand Down Expand Up @@ -551,7 +584,9 @@ export class Field {
<vscode-form-group variant="settings-group">
${this.renderLabel()}
${this.renderDescription()}
<vscode-textfield type="file" id="${this.id}" name="${this.id}"></vscode-textfield>
<vscode-textfield type="input" id="${this.id}" name="${this.id}" ${this.default ? `value="${this.default}"` : ``} readonly></vscode-textfield>
<br /><br />
<vscode-button id="${this.id}-file" secondary>Select File</vscode-button>
</vscode-form-group>`;

case `password`:
Expand Down
6 changes: 3 additions & 3 deletions src/webviews/actions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import { Action, ActionEnvironment, ActionRefresh, ActionType } from "../../typi
import { getVariablesInfo } from "./varinfo";

type MainMenuPage = {
buttons: 'newAction' | 'duplicateAction'
actions: number
buttons?: 'newAction' | 'duplicateAction'
value: string
}

type ActionPage = {
Expand Down Expand Up @@ -76,7 +76,7 @@ export namespace ActionsUI {
duplicateAction();
break;
default:
workAction(page.data.actions);
workAction(Number(page.data.value));
break;
}
}
Expand Down
2 changes: 1 addition & 1 deletion webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ const config = {
},
{
test: /\.js$/,
include: path.resolve(__dirname, `node_modules/@bendera/vscode-webview-elements/dist`),
include: path.resolve(__dirname, `node_modules/@vscode-elements/elements/dist`),
type: `asset/source`
},
{
Expand Down

0 comments on commit 27160a2

Please sign in to comment.