Skip to content

Commit

Permalink
Merge pull request #16866 from vlazar/feature/separate-label-value
Browse files Browse the repository at this point in the history
Separate label/value
  • Loading branch information
vlazar committed Mar 12, 2016
2 parents 2cd7fad + 539fbf5 commit d276bb2
Show file tree
Hide file tree
Showing 7 changed files with 143 additions and 26 deletions.
48 changes: 41 additions & 7 deletions awesomplete.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ var _ = function (input, o) {
minChars: 2,
maxItems: 10,
autoFirst: false,
data: _.DATA,
filter: _.FILTER_CONTAINS,
sort: _.SORT_BYLENGTH,
item: _.ITEM,
Expand Down Expand Up @@ -117,9 +118,18 @@ _.prototype = {
list = $(list);

if (list && list.children) {
this._list = slice.apply(list.children).map(function (el) {
return el.textContent.trim();
var items = [];
slice.apply(list.children).forEach(function (el) {
if (!el.disabled) {
var text = el.textContent.trim();
var value = el.value || text;
var label = el.label || text;
if (value !== "") {
items.push({ label: label, value: value });
}
}
});
this._list = items;
}
}

Expand Down Expand Up @@ -184,17 +194,21 @@ _.prototype = {
},

select: function (selected, origin) {
selected = selected || this.ul.children[this.index];
if (selected) {
this.index = $.siblingIndex(selected);
} else {
selected = this.ul.children[this.index];
}

if (selected) {
var allowed = $.fire(this.input, "awesomplete-select", {
text: selected.textContent,
data: this.suggestions[$.siblingIndex(selected)],
text: this.suggestions[this.index],
data: this.suggestions[this.index],
origin: origin || selected
});

if (allowed) {
this.replace(selected.textContent);
this.replace(this.suggestions[this.index]);
this.close();
$.fire(this.input, "awesomplete-selectcomplete");
}
Expand All @@ -211,6 +225,9 @@ _.prototype = {
this.ul.innerHTML = "";

this.suggestions = this._list
.map(function(item) {
return new Suggestion(me.data(item, value));
})
.filter(function(item) {
return me.filter(item, value);
})
Expand Down Expand Up @@ -262,11 +279,28 @@ _.ITEM = function (text, input) {
};

_.REPLACE = function (text) {
this.input.value = text;
this.input.value = text.value;
};

_.DATA = function (item/*, input*/) { return item; };

// Private functions

function Suggestion(data) {
var o = Array.isArray(data)
? { label: data[0], value: data[1] }
: typeof data === "object" && "label" in data && "value" in data ? data : { label: data, value: data };

this.label = o.label || o.value;
this.value = o.value;
}
Object.defineProperty(Suggestion.prototype = Object.create(String.prototype), "length", {
get: function() { return this.label.length; }
});
Suggestion.prototype.toString = Suggestion.prototype.valueOf = function () {
return "" + this.label;
};

function configure(instance, properties, o) {
for (var i in properties) {
var initial = properties[i],
Expand Down
57 changes: 47 additions & 10 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,29 @@ <h1>Basic usage</h1>

awesomplete.list = ["Ada", "Java", "JavaScript", "Brainfuck", "LOLCODE", "Node.js", "Ruby on Rails"];</code></pre>

<p>Suggestions with different <strong>label</strong> and <strong>value</strong> are supported too. The label will be shown in autocompleter and the value will be inserted into the input.</p>

<pre class="language-markup"><code>&lt;input id="myinput" /></code></pre>
<pre class="language-javascript"><code>var input = document.getElementById("myinput");

// Show label but insert value into the input:
new Awesomplete(input, {
list: [
{ label: "Belarus", value: "BY" },
{ label: "China", value: "CN" },
{ label: "United States", value: "US" }
]
});

// Same with arrays:
new Awesomplete(input, {
list: [
[ "Belarus", "BY" ],
[ "China", "CN" ],
[ "United States", "US" ]
]
});</code></pre>

</section>

<section id="customization">
Expand Down Expand Up @@ -233,6 +256,27 @@ <h1>Extend</h1>
this.input.value = text;
}</code></pre></td>
</tr>
<tr>
<td><code>data</code></td>
<td>Controls suggestions' <code>label</code> and <code>value</code>. This is useful if you have list items in custom format, or want to change list items based on user's input.</td>
<td>Function that takes two parameters, the first one being the original list item and the second a string with the user’s input and returns a list item in one of supported by default formats:
<ul>
<li><code>"JavaScript"</code></li>
<li><code>{ label: "JavaScript", value: "JS" }</code></li>
<li><code>[ "JavaScript", "JS" ]</code></li>
</ul>
To <strong>use objects without <code>label</code> or <code>value</code> properties</strong>, e.g. <code>name</code> and <code>id</code> instead, you can do this:
<pre class="language-javascript"><code>data: function (item, input) {
return { label: item.name, value: item.id };
}</code></pre>
You can <strong>use any object for <code>label</code> and <code>value</code></strong> and it will be converted to String where necessary:
<pre class="language-javascript"><code>list: [ new Date("2015-01-01"), ... ] </code></pre>
Original list items as Date objects will be accessible in <code>filter</code>, <code>sort</code>, <code>item</code> and <code>replace</code> functions, but by default we'll just see Date objects converted to strings in autocompleter and the same value will be inserted to the input.
<br />
We can also <strong>generate list items based on user's input</strong>. See E-mail autocomplete example in <a href="#advanced-examples">Advanced Examples</a> section.
</td>
<td><code class="language-javascript">Awesomplete.DATA</code>: Identity function which just returns the original list item.</td>
</tr>
</tbody>
</table>
</section>
Expand Down Expand Up @@ -335,17 +379,10 @@ <h2>E-mail autocomplete</h2>
<pre class="language-markup"><code>&lt;input type="email" /></code></pre>
<pre class="language-javascript"><code><script>new Awesomplete($('input[type="email"]'), {
list: ["@aol.com", "@att.net", "@comcast.net", "@facebook.com", "@gmail.com", "@gmx.com", "@googlemail.com", "@google.com", "@hotmail.com", "@hotmail.co.uk", "@mac.com", "@me.com", "@mail.com", "@msn.com", "@live.com", "@sbcglobal.net", "@verizon.net", "@yahoo.com", "@yahoo.co.uk"],
item: function(text, input) {
var newText = input.slice(0, input.indexOf("@")) + text;

return Awesomplete.$.create("li", {
innerHTML: newText.replace(RegExp(input.trim(), "gi"), "<mark>$&</mark>"),
"aria-selected": "false"
});
data: function (text, input) {
return input.slice(0, input.indexOf("@")) + text;
},
filter: function(text, input) {
return RegExp("^" + Awesomplete.$.regExpEscape(input.replace(/^.+?(?=@)/, ''), "i")).test(text);
}
filter: Awesomplete.FILTER_STARTSWITH
});</script></code></pre>
</section>

Expand Down
4 changes: 2 additions & 2 deletions test/api/selectSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ describe("awesomplete.select", function () {

expect(handler).toHaveBeenCalledWith(
jasmine.objectContaining({
text: expectedTxt,
data: expectedTxt,
text: jasmine.objectContaining({ label: expectedTxt, value: expectedTxt }),
data: jasmine.objectContaining({ label: expectedTxt, value: expectedTxt }),
origin: this.selectArgument || this.subject.ul.children[0]
})
);
Expand Down
35 changes: 29 additions & 6 deletions test/init/listSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,20 @@ describe("Awesomplete list", function () {

it("assigns from element specified by selector", function () {
this.subject.list = "#data-list";
expect(this.subject._list).toEqual([ "With", "Data", "List" ]);
expect(this.subject._list).toEqual([
{ label: "With", value: "With" },
{ label: "Data", value: "Data" },
{ label: "List", value: "List" }
]);
});

it("assigns from element", function () {
this.subject.list = $("#data-list");
expect(this.subject._list).toEqual([ "With", "Data", "List" ]);
expect(this.subject._list).toEqual([
{ label: "With", value: "With" },
{ label: "Data", value: "Data" },
{ label: "List", value: "List" }
]);
});

it("does not assigns from not found list", function () {
Expand Down Expand Up @@ -68,12 +76,20 @@ describe("Awesomplete list", function () {

it("assigns from element specified by selector", function () {
this.options = { list: "#data-list" };
expect(this.subject._list).toEqual([ "With", "Data", "List" ]);
expect(this.subject._list).toEqual([
{ label: "With", value: "With" },
{ label: "Data", value: "Data" },
{ label: "List", value: "List" }
]);
});

it("assigns from list specified by element", function () {
this.options = { list: $("#data-list") };
expect(this.subject._list).toEqual([ "With", "Data", "List" ]);
expect(this.subject._list).toEqual([
{ label: "With", value: "With" },
{ label: "Data", value: "Data" },
{ label: "List", value: "List" }
]);
});
});

Expand All @@ -85,14 +101,21 @@ describe("Awesomplete list", function () {

it("assigns from element referenced by selector", function () {
this.element = "#with-data-list";
expect(this.subject._list).toEqual(["With", "Data", "List"]);
expect(this.subject._list).toEqual([
{ label: "With", value: "With" },
{ label: "Data", value: "Data" },
{ label: "List", value: "List" }
]);
});
});

describe("list html attribute", function () {
it("assigns from element referenced by id", function () {
this.element = "#with-list";
expect(this.subject._list).toEqual(["With", "List"]);
expect(this.subject._list).toEqual([
{ label: "With", value: "With" },
{ label: "List", value: "List" }
]);
});
});
});
4 changes: 4 additions & 0 deletions test/init/optionsSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ describe("Constructor options", function () {
expect(this.subject.autoFirst).toBe(false);
});

it("modifies list item with DATA", function () {
expect(this.subject.data).toBe(Awesomplete.DATA);
});

it("filters with FILTER_CONTAINS", function () {
expect(this.subject.filter).toBe(Awesomplete.FILTER_CONTAINS);
});
Expand Down
19 changes: 19 additions & 0 deletions test/static/dataSpec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
describe("Awesomplete.DATA", function () {

subject(function () { return Awesomplete.DATA(this.item) });

it("returns original String", function () {
this.item = "JavaScript";
expect(this.subject).toEqual("JavaScript");
});

it("returns original Object", function () {
this.item = { label: "JavaScript", value: "JS" };
expect(this.subject).toEqual({ label: "JavaScript", value: "JS" });
});

it("returns original Array", function () {
this.item = [ "JavaScript", "JS" ];
expect(this.subject).toEqual([ "JavaScript", "JS" ]);
});
});
2 changes: 1 addition & 1 deletion test/static/replaceSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ describe("Awesomplete.REPLACE", function () {
def("instance", function() { return { input: { value: "initial" } } });

it("replaces input value", function () {
this.subject.call(this.instance, "JavaScript");
this.subject.call(this.instance, { value: "JavaScript" });
expect(this.instance.input.value).toBe("JavaScript");
});
});

0 comments on commit d276bb2

Please sign in to comment.