Allow new value on QuickPick in a VSCode extension - visual-studio-code

I want to display to the user an input with some options, but the user can answer a new one.
Using showQuickPick I can show some options, but if the user answer a different option the return is undefined.
It's possible to do what I want?
I have already think about create a New option and then show an InputBox to the user, but I don't want that the user need to answer two questions.

I have used a solution where I inject the user input in the list of items.
It works pretty well:
const choices = ['a', 'b']
async function getUserSelectdValue() {
return new Promise((resolve) => {
const quickPick = window.createQuickPick();
quickPick.items = choices.map(choice => ({ label: choice }));
quickPick.title = 'Choose your favorite value:'
quickPick.onDidChangeValue(() => {
// INJECT user values into proposed values
if (!choices.includes(quickPick.value)) quickPick.items = [quickPick.value, ...choices].map(label => ({ label }))
})
quickPick.onDidAccept(() => {
const selection = quickPick.activeItems[0]
resolve(selection.label)
quickPick.hide()
})
quickPick.show();
})
}
Please let me know if you need further explanations

It sounds like you might be interested in the approach taken in this issue?
https://github.com/microsoft/vscode/issues/89601

Related

Showing multiple search results on leaflet map [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
https://www.website.ro/harta this is the map that uses leaflet.js and https://github.com/stefanocudini/leaflet-search, but i can search only single locations.
https://www.website.ro/public/ajax?q=electri
If i search for "electri" it has 3 locations, i want to show them when i hit enter, not to show "Not found".
Already searched on google, stackoverflow, didnt found similar answer/problem.
This can be done with careful use of the options that leaflet-search provides. First, let's create an array that will hold the potential results, and a featureLayer to render any results that show up:
const results = [];
var resultsLayer = L.featureGroup();
Now we can overwrite the buildTip option as a function which does pretty much what it does already by default, but pushes the results to an array as well:
var controlSearch = new L.Control.Search({
...options,
// hijack buildtip function, push results to array
buildTip: (text, loc) => {
results.push(loc); // <---- crucial line here
// the rest of this is lifted from the source code almost exactly
// so as to keep the same behavior when clicking on an option
const tip = L.DomUtil.create("div");
tip.innerHTML = text;
L.DomEvent.disableClickPropagation(tip)
.on(tip, "click", L.DomEvent.stop, controlSearch)
.on(
tip,
"click",
function (e) {
controlSearch._input.value = text;
controlSearch._handleAutoresize();
controlSearch._input.focus();
controlSearch._hideTooltip();
controlSearch._handleSubmit();
},
controlSearch
);
return tip;
},
// only move to the location if there are not multiple results
moveToLocation: results.length
? () => {}
: L.Control.Search._defaultMoveToLocation
});
Now we add an event listener to the input of the search, and if the user presses enter, and there are multiple results, the results that were pushed into the results array will be added to the resultsLayer as markers, and added to the map:
inputEl.addEventListener("keypress", function (e) {
if (e.key === "Enter" && results.length) {
markersLayer.remove();
results.forEach((result) => {
const marker = L.marker(result);
resultsLayer.addLayer(marker);
});
map.fitBounds(resultsLayer.getBounds());
}
});
Working codesandbox
Note this will likely require some cleanup work (i.e. emptying the array on new or empty searches), or readding the full data set if the search is empty, etc., but this should be enough to get you started.
Edit - Full item info
You asked in a comment how we can get the full details of an item and put that in a popup. Reading through leaflet-search's docs and source code, there doesn't seem to be any place that their code 'catches' the entire data object. The buildTip function really only needs 2 pieces of data from an item - the text to show in the tooltip, and the location it refers to. There's a bunch of TODOs regarding keeping the source data in a cache, but they're still todos.
What I would do is use the title and loc that is returned in a result to filter the original data and find its corresponding item in the original data:
const getFullItem = (title, loc) => {
return data.find((item) => item.title === title && loc.equals(item.loc));
};
We can also create a generic function to build the popup text for all the makers, and the results, so the popups are all consistent:
const buildPopupText = (item) => {
return `
<h4>Title: ${item.title}</h4>
<p>Phone: ${item.telefon}</p>
<p>more stuff from ${item.whatever}</p>
`;
};
When we hit enter and we map through the results, we'll use the result to get the original item:
inputEl.addEventListener("keypress", function (e) {
if (e.key === "Enter" && results.length) {
results.forEach((result) => {
const originalItem = getFullItem(result.text, result.loc);
const marker = L.marker(result.loc);
marker.bindPopup(buildPopupText(originalItem));
resultsLayer.addLayer(marker);
});
map.fitBounds(resultsLayer.getBounds());
}
});
So now the results popups build a popup from the originalItem, which has all the properties you'll need.
Working codesandbox

How to know what suggestion item is selected in vscode

I complete a vscode extension by vscode.languages.registerCompletionItemProvider(selector, new FuncCompletionProvider(),'.')
I want to listen which suggestion is selected. In the image below,when I click the current item I want to get the CompletionItem Info.
I tried to use the resolveCompletionItem function, but before the suggestion is selected resolveCompletionItem was triggered.
I tried to use the resolveCompletionItem function, but before the suggestion is selected resolveCompletionItem was triggered.
It appears this is intentional. Per their docs:
Note that this function is called when completion items are already showing in the UI or when an item has been selected for insertion
'selected' meaning selected in the list, not committed
The recommended way to gain insight on when a CompletionItem is inserted is using the CompletionProvider#command property:
An optional command that is executed after inserting this completion. Note that additional modifications to the current document should be described with the additionalTextEdits-property.
Example usage:
export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(
vscode.languages.registerCompletionItemProvider('html', new MyCompletionProvider),
vscode.commands.registerCommand("doTheThing", () => {
console.log('did the thing!!');
});
);
}
class MyCompletionProvider implements vscode.CompletionItemProvider {
provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext): vscode.ProviderResult<vscode.CompletionItem[] | vscode.CompletionList> {
const myHTMLCompletionItem: vscode.CompletionItem = new vscode.CompletionItem("myHTML");
myHTMLCompletionItem.command = {
title: '',
command: 'doTheThing'
};
return new vscode.CompletionList([myHTMLCompletionItem]);
}
}

Ionic3 Action Sheet with Variable Inputs

I need to create an action sheet or some other kind of alert which displays options to a user and saves the option they clicked in a variable. However, the options displayed to the user need to be retrieved from Firestore and are different for each user (each user also may have a different number of options).
I’ve been able to display these options in an action sheet, but I’m struggling to get the value of the option they clicked.
Below is my code for the function so far. The list of options retrieved from Firestore is saved in an array called ‘options’.
categorizeProblem(){
this.action = this.actionCtrl.create({
title: "What sort of problem did this student have?",
})
this.options.forEach(option => {
var button = {
text: option,
value: //option name
handler: (data)=> {
//Need to get the value/name of the option clicked and save this in a variable
}
}
this.action.addButton(button);
})
this.action.present();
}
I would really appreciate if someone could help me with this or suggest an alternate way to do this.
Thanks in advance!
You can use the text property as your identifier. And then your handler should use the old function syntax so you will have the value of handler's this.
Might sound confusing but here's the code, that should demonstrate my point clearly.
presentActionSheet() {
var self = this;
let options = [
"Option1",
"Option2",
"Option3"
];
let actionSheet = this.actionSheetCtrl.create({
title: 'Categories',
});
options.forEach(option => {
actionSheet.addButton({
text: option,
handler: function() {
self.selectedOption = this.text;
}
})
});
actionSheet.present();
}
Here's the running code.

Dynamic Kendo Menu which uses Ajax to call actions

Here is code I have and the menu displays.
#(Html.Kendo().Menu()
.Name("MainMenu")
.BindTo(_userMenu.UserMenuItems, mappings =>
{
mappings.For<UserMenuItemModel>(binding => binding.ItemDataBound((item, UserMenuItemModel) =>
{
item.Text = UserMenuItemModel.Description;
item.ActionName = UserMenuItemModel.ActionName;
item.ControllerName = UserMenuItemModel.ControllerName;
item.HtmlAttributes = (new { data-ajax = "true", });
})
.Children(UserMenuItemModel => UserMenuItemModel.SubMenuItems));
mappings.For<UserMenuItemModel>(binding => binding.ItemDataBound((item, UserMenuItemModel) =>
{
item.Text = UserMenuItemModel.Description;
item.ActionName = UserMenuItemModel.ActionName;
item.ControllerName = UserMenuItemModel.ControllerName;
}));
})
)
However, clicking an item makes a non ajax call which won't work in our architecture.
Basically, with the kendo menu item I'm shooting to have links which function exactly like what this does.
<li>
#Ajax.ActionLink(item.Description, item.ActionName, item.ControllerName, ajaxMainMenuOptions)
</li>
I thought I was going to be able to set the HTMLAttributes for each item but that's read only.
Our dynamic menu is hiearchical with no limits on levels. I'm working another angle with a recursive partial page, but having a lot of trouble with CSS.
The kendo menu was extremely simple to bind to a our self referencing model, I just have to figure out how to get it to make an Ajax.ActionLink call.
Any pointers are greatly appreciated.
Thanks.
Just realized the Attributes have an add.
This appears to be what we were shooting for.
#(Html.Kendo().Menu()
.Name("MainMenu")
.BindTo(_userMenu.UserMenuItems, mappings =>
{
mappings.For<UserMenuItemModel>(binding => binding.ItemDataBound((item, UserMenuItemModel) =>
{
item.Text = UserMenuItemModel.Description;
item.ActionName = UserMenuItemModel.ActionName;
item.ControllerName = UserMenuItemModel.ControllerName;
item.LinkHtmlAttributes.Add("data-ajax", "true");
item.LinkHtmlAttributes.Add("data-ajax-method", "GET");
item.LinkHtmlAttributes.Add("data-ajax-mode", "replace");
item.LinkHtmlAttributes.Add("data-ajax-update", "#MainBody");
})
.Children(UserMenuItemModel => UserMenuItemModel.SubMenuItems));
mappings.For<UserMenuItemModel>(binding => binding.ItemDataBound((item, UserMenuItemModel) =>
{
item.Text = UserMenuItemModel.Description;
item.ActionName = UserMenuItemModel.ActionName;
item.ControllerName = UserMenuItemModel.ControllerName;
item.LinkHtmlAttributes.Add("data-ajax", "true");
item.LinkHtmlAttributes.Add("data-ajax-method", "GET");
item.LinkHtmlAttributes.Add("data-ajax-mode", "replace");
item.LinkHtmlAttributes.Add("data-ajax-update", "#MainBody");
}));
})
)
For anybody else, this is a fully dynamic menu built using a hierarchical, self-referencing model. UserMenuItemModel contains a List
Adding keywords because I really never found anything like this, Kendo Menu Ajax
ActionLink hierarchical self-referencing
Hope this helps others.

jQuery Combobox/select autocomplete? [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 7 years ago.
Improve this question
Does a jQuery plug-in exist for replacing select/combo box?
I tried SexyCombo, and it is as close to what I want, but it doesn't complete if you are writing from middle, only from beginning.
I have 2 levels of categories (20 top level categories, and with subcategories in total 120 categories), so when user is submitting an entry, he must find desired category as soon as possible.
So... 2 levels + autocomplete populate text even if you write middle letters.
Or any other solution?
Have a look at the following example of the jQueryUI Autocomplete, as it is keeping a select around and I think that is what you are looking for. Hope this helps.
http://jqueryui.com/demos/autocomplete/#combobox
[edit] The lovely chosen jQuery plugin has been bought to my attention, looks like a great alternative to me.
Or if you just want to use jQuery autocomplete, I've extended the combobox example to support defaults and remove the tooltips to give what I think is more expected behaviour. Try it out.
(function ($) {
$.widget("ui.combobox", {
_create: function () {
var input,
that = this,
wasOpen = false,
select = this.element.hide(),
selected = select.children(":selected"),
defaultValue = selected.text() || "",
wrapper = this.wrapper = $("<span>")
.addClass("ui-combobox")
.insertAfter(select);
function removeIfInvalid(element) {
var value = $(element).val(),
matcher = new RegExp("^" + $.ui.autocomplete.escapeRegex(value) + "$", "i"),
valid = false;
select.children("option").each(function () {
if ($(this).text().match(matcher)) {
this.selected = valid = true;
return false;
}
});
if (!valid) {
// remove invalid value, as it didn't match anything
$(element).val(defaultValue);
select.val(defaultValue);
input.data("ui-autocomplete").term = "";
}
}
input = $("<input>")
.appendTo(wrapper)
.val(defaultValue)
.attr("title", "")
.addClass("ui-state-default ui-combobox-input")
.width(select.width())
.autocomplete({
delay: 0,
minLength: 0,
autoFocus: true,
source: function (request, response) {
var matcher = new RegExp($.ui.autocomplete.escapeRegex(request.term), "i");
response(select.children("option").map(function () {
var text = $(this).text();
if (this.value && (!request.term || matcher.test(text)))
return {
label: text.replace(
new RegExp(
"(?![^&;]+;)(?!<[^<>]*)(" +
$.ui.autocomplete.escapeRegex(request.term) +
")(?![^<>]*>)(?![^&;]+;)", "gi"
), "<strong>$1</strong>"),
value: text,
option: this
};
}));
},
select: function (event, ui) {
ui.item.option.selected = true;
that._trigger("selected", event, {
item: ui.item.option
});
},
change: function (event, ui) {
if (!ui.item) {
removeIfInvalid(this);
}
}
})
.addClass("ui-widget ui-widget-content ui-corner-left");
input.data("ui-autocomplete")._renderItem = function (ul, item) {
return $("<li>")
.append("<a>" + item.label + "</a>")
.appendTo(ul);
};
$("<a>")
.attr("tabIndex", -1)
.appendTo(wrapper)
.button({
icons: {
primary: "ui-icon-triangle-1-s"
},
text: false
})
.removeClass("ui-corner-all")
.addClass("ui-corner-right ui-combobox-toggle")
.mousedown(function () {
wasOpen = input.autocomplete("widget").is(":visible");
})
.click(function () {
input.focus();
// close if already visible
if (wasOpen) {
return;
}
// pass empty string as value to search for, displaying all results
input.autocomplete("search", "");
});
},
_destroy: function () {
this.wrapper.remove();
this.element.show();
}
});
})(jQuery);
I know this has been said earlier, but jQuery Autocomplete will do exactly what you need. You should check out the docs as the autocomplete is very customizable. If you are familiar with javascript then you should be able to work this out. If not I can give you a few pointers, as I have done this once before, but beware I am not well versed in javascript myself either, so bear with me on this.
I think the first thing you should do is just get a simple autocomplete text field working on your page, and then you can customize it from there.
The autocomplete widget accepts JSON data as it's 'source:' option. So you should set-up your app to produce the 20 top level categories, and subcategories in JSON format.
The next thing to know is that when the user types into your textfield, the autocomplete widget will send the typed values in a parameter called "term".
So let's say you first set-up your site to deliver the JSON data from a URL like this:
/categories.json
Then your autocomplete source: option would be 'source: /categories.json'.
When a user types into the textfield, such as 'first-cata...' the autocomplete widget will start sending the value in the 'term' parameter like this:
/categories.json?term=first-cata
This will return JSON data back to the widget filtered by anything that matches 'first-cata', and this is displayed as an autocomplete suggestion.
I am not sure what you are programming in, but you can specify how the 'term' parameter finds a match. So you can customize this, so that the term finds a match in the middle of a word if you want. Example, if the user types 'or' you code could make a match on 'sports'.
Lastly, you made a comment that you want to be able to select a category name but have the autocomplete widget submit the category ID not the name.
This can easily be done with a hidden field. This is what is shown in the jQuery autocomplete docs.
When a user selects a category, your JavaScript should update a hidden field with the ID.
I know this answer is not very detailed, but that is mainly because I am not sure what you are programming in, but the above should point you in the right direction. The thing to know is that you can do practically any customizing you want with this widget, if you are willing to spend the time to learn it.
These are the broad strokes, but you can look here for some notes I made when I implemented something similar to what you want in a Rails app.
Hope this helped.
This works great for me and I'm doing more, writing less with jQuery's example modified.
I defined the select object on my page, just like the jQuery ex. I took the text and pushed it to an array. Then I use the array as my source to my input autocomplete. tadaa.
$(function() {
var mySource = [];
$("#mySelect").children("option").map(function() {
mySource.push($(this).text());
});
$("#myInput").autocomplete({
source: mySource,
minLength: 3
});
}
jQuery 1.8.1 has an example of this under autocomplete. It's very easy to implement.