summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVladislav Zarakovsky <vlad.zar@gmail.com>2016-03-12 14:25:05 +0300
committerVladislav Zarakovsky <vlad.zar@gmail.com>2016-03-12 14:25:05 +0300
commitd276bb2ee7fe68d020bd4ba0da3b5677f3e649e2 (patch)
tree8ac2ae23f70843630bb1301c781eacf6430413fa
parent2cd7fada2cf2fcd1c2048ab7ed862f349863a1bb (diff)
parent539fbf5c0b7adc73c22b6b10f9742a5e46170707 (diff)
downloadawesomplete-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.js48
-rw-r--r--index.html57
-rw-r--r--test/api/selectSpec.js4
-rw-r--r--test/init/listSpec.js35
-rw-r--r--test/init/optionsSpec.js4
-rw-r--r--test/static/dataSpec.js19
-rw-r--r--test/static/replaceSpec.js2
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],
diff --git a/index.html b/index.html
index b9ff341..0dc3917 100644
--- a/index.html
+++ b/index.html
@@ -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>&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">
@@ -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>&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>
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");
});
});