Autocomplete InputField
My humble contribution:
(* Use this function to style list elements *)
listItemStyle[item_] := Mouseover[#, Style[#, Background -> LightBlue]] &@ MouseAppearance[Framed[item], "LinkHand"];
(* This filters the list of data and returns a clickable list *)
SetAttributes[autoComplete, HoldFirst];
autoComplete[s_, data_] := If[
StringLength[s] > 0,
EventHandler[#, {"MouseClicked" :> (s = #)}] & /@ Select[data, StringMatchQ[#, s ~~ __, IgnoreCase -> True] &],
{}
]
(* Responsible for final output *)
SetAttributes[inputWithAutoComplete, HoldFirst];
inputWithAutoComplete[s_, data_, max_: 5] := Column[{
InputField[Dynamic[s], String, ContinuousAction -> True],
Dynamic[listItemStyle /@ (Take[#, Min[Length[#], max]] &@autoComplete[s, data] /. {} -> "") // TableForm]
}, Left, 1]
Try it using:
data = CountryData[];
s = "";
inputWithAutoComplete[s, data, 10]
Version 2.0
I started all over and refactored the code to make it better. There are many advantages so this is definitely the one to use, but I'm not deleting what I posted earlier since the new version is also substantially different, and both may be of interest.
(* Returns expr with styles off or on depending on whether the expr \
is hovered. Additionally the cursor as the mouse if hovering the \
expression can be set. *)
MouseoverStyled[expr_, off_, on_, cursor_: "LinkHand"] := Deploy[
MouseAppearance[Mouseover[
Style[expr, off],
Style[expr, on]
], cursor]
]
(* Returns a list item with the given text *)
item[text_] := MouseoverStyled[Framed[text], {}, {Background -> LightBlue}]
(* Create a list of clickable items *)
items[labels_, func_] := EventHandler[
item[#], {
"MouseClicked" :> func[#]
}
] & /@ labels;
(* Picks out the n first elements that start with input *)
filter[input_, data_, n_: 10] := Take[#, Min[Length[#], n]] &@Select[data, StringMatchQ[#, input ~~ __, IgnoreCase -> True] &]
(* Final presentation *)
inputWithAutoomplete[data_] := DynamicModule[{s = ""},
Column[{
InputField[Dynamic[s], String, ContinuousAction -> True],
Dynamic@If[StringLength[s] > 0, Column[items[filter[s, data], (s = #) &]], ""]
}]]
Test it like this:
inputWithAutoomplete[Last /@ CountryData[]]
or with just CountryData[]
for more countries, but I noticed this can be quite slow (it didn't use to be - but since V10 with the EntityValue
thing.)
I also took a crack at this. I think I made it look pretty close to the jquery example you posted. Figuring out how to move the insertion point to the end of the word once a suggestion is selected was a bit of a struggle. As a result, there's a DynamicWrapper
in there that may be unstable.
Input is the list of possible values from which you'd like to draw suggestions.
Enjoy!
autocompleteInputField[possibilities:{_String..}]:=DynamicModule[{nb,ind=0,txt,suggestions,suggestedElements,returnKeyTrigger=False},
suggestions = Dynamic[
If[Length[suggestedElements] < 1 || StringLength[txt] <= 1,
"",
(* else *)
Column[
MapIndexed[
Button[
Function[panel,Mouseover[Style[panel,Background->If[ind===First[#2],LightBlue,White]],Style[panel,Background->LightBlue]]]@Pane[#1,ImageMargins->0],
(
txt=#1;
returnKeyTrigger=True
),
Appearance->"Frameless"
]&,
If[!(If[Length[suggestedElements]===1,First[suggestedElements]===txt,False]) && StringLength[txt]>1,
suggestedElements,
{}
]
],
Background->White,
Frame->True
]
]
];
Column[{
DynamicWrapper[
EventHandler[
InputField[
Dynamic[txt],
String,
ContinuousAction->True,
BaseStyle->{FontFamily->"Arial"}
],
{
"DownArrowKeyDown":>(ind=Min[ind+1,Length[suggestedElements]]),
"UpArrowKeyDown":>(ind=Max[0,ind-1]),
"ReturnKeyDown":>
If[Length[suggestedElements]>0 && ind>0,
txt=suggestedElements[[ind]];
ind=0;
returnKeyTrigger=True
]
}
],
If[returnKeyTrigger,
returnKeyTrigger=False;
SelectionMove[nb,Next,Word]
],
SynchronousUpdating->False
],
suggestions
}],
Initialization:>(
nb = EvaluationNotebook[];
txt="";
suggestedElements:=Pick[possibilities,StringMatchQ[possibilities,txt~~__]]
)
]
Maybe overkill but it was educational to try:
DynamicModule[{},
EventHandler[
Overlay[{
Dynamic@Framed[
Row[{Style[x, Transparent, 15, Bold], Style[rest, [email protected], 15, Bold]}],
ImageSize -> {280, 30}, Alignment -> Top, FrameMargins -> {{5, 0}, {0, 1}}],
InputField[Dynamic@x, String, BaseStyle -> {Bold, Black, 15}, Alignment -> Top,
ContinuousAction -> True, ImageSize -> {280, 30}, FrameMargins -> 0]
}, {2, 1}, 2]
, {"ReturnKeyDown" :> (x = x ~~ rest),
"UpArrowKeyDown" :> (names = RotateLeft@names),
"DownArrowKeyDown" :> (names = RotateRight@names)}
]
, Initialization :> (x = "";
names := Names["System`" ~~ x ~~ "*"];
rest := If[Length@names == 0 \[Or] x == "", "",
StringDrop[First[names],
Clip[StringLength@x, {0, StringLength@First[names]}]]]
)
]
Please forgive me but I have no time to make it more user friendly.
There is no menu, only auto filling which you accept with Enter or you can also switch between fillings with arrows.
I have a couple of ideas, I'm going to write them tomorrow.