diff options
author | Vladislav Zarakovsky <vlad.zar@gmail.com> | 2016-03-12 14:25:05 +0300 |
---|---|---|
committer | Vladislav Zarakovsky <vlad.zar@gmail.com> | 2016-03-12 14:25:05 +0300 |
commit | d276bb2ee7fe68d020bd4ba0da3b5677f3e649e2 (patch) | |
tree | 8ac2ae23f70843630bb1301c781eacf6430413fa | |
parent | 2cd7fada2cf2fcd1c2048ab7ed862f349863a1bb (diff) | |
parent | 539fbf5c0b7adc73c22b6b10f9742a5e46170707 (diff) | |
download | awesomplete-d276bb2ee7fe68d020bd4ba0da3b5677f3e649e2.zip awesomplete-d276bb2ee7fe68d020bd4ba0da3b5677f3e649e2.tar.gz awesomplete-d276bb2ee7fe68d020bd4ba0da3b5677f3e649e2.tar.bz2 |
Merge pull request #16866 from vlazar/feature/separate-label-value
Separate label/value
-rw-r--r-- | awesomplete.js | 48 | ||||
-rw-r--r-- | index.html | 57 | ||||
-rw-r--r-- | test/api/selectSpec.js | 4 | ||||
-rw-r--r-- | test/init/listSpec.js | 35 | ||||
-rw-r--r-- | test/init/optionsSpec.js | 4 | ||||
-rw-r--r-- | test/static/dataSpec.js | 19 | ||||
-rw-r--r-- | test/static/replaceSpec.js | 2 |
7 files changed, 143 insertions, 26 deletions
diff --git a/awesomplete.js b/awesomplete.js index 950a5ac..545086e 100644 --- a/awesomplete.js +++ b/awesomplete.js @@ -22,6 +22,7 @@ var _ = function (input, o) { minChars: 2, maxItems: 10, autoFirst: false, + data: _.DATA, filter: _.FILTER_CONTAINS, sort: _.SORT_BYLENGTH, item: _.ITEM, @@ -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; } } @@ -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"); } @@ -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); }) @@ -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], @@ -125,6 +125,29 @@ var awesomplete = new Awesomplete(input); 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><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"> @@ -233,6 +256,27 @@ awesomplete.list = ["Ada", "Java", "JavaScript", "Brainfuck", "LOLCODE", "Node.j 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> @@ -335,17 +379,10 @@ awesomplete.list = ["Ada", "Java", "JavaScript", "Brainfuck", "LOLCODE", "Node.j <pre class="language-markup"><code><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> diff --git a/test/api/selectSpec.js b/test/api/selectSpec.js index 84895b1..9a1214e 100644 --- a/test/api/selectSpec.js +++ b/test/api/selectSpec.js @@ -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] }) ); diff --git a/test/init/listSpec.js b/test/init/listSpec.js index 6f95ff0..eff78d6 100644 --- a/test/init/listSpec.js +++ b/test/init/listSpec.js @@ -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 () { @@ -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" } + ]); }); }); @@ -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" } + ]); }); }); }); diff --git a/test/init/optionsSpec.js b/test/init/optionsSpec.js index 40153c1..fc60997 100644 --- a/test/init/optionsSpec.js +++ b/test/init/optionsSpec.js @@ -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); }); diff --git a/test/static/dataSpec.js b/test/static/dataSpec.js new file mode 100644 index 0000000..311c6eb --- /dev/null +++ b/test/static/dataSpec.js @@ -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" ]); + }); +}); diff --git a/test/static/replaceSpec.js b/test/static/replaceSpec.js index 4339ff2..e2eddba 100644 --- a/test/static/replaceSpec.js +++ b/test/static/replaceSpec.js @@ -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"); }); }); |