Skip to content

Commit 495043f

Browse files
committed
Again
1 parent faceaf2 commit 495043f

File tree

7 files changed

+224
-48
lines changed

7 files changed

+224
-48
lines changed

README.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,12 @@ can_delete | true | Set if current user can delete tags/pills
8989
can_add | true | Set if current users can add new tags
9090
remove_url | | Set URL where `POST` request will be sent on tag/pill removal.
9191
input_name | tags[] | Set name of the hidden `<input>` element to be added with every tag/pill. It should end with `[]` in order to support multiple values.
92+
double_hilight | #0B3549 | Color to highlight tag on double entry attempt.
9293
limit | 0 | Maximum amount of tags to add. 0 - no limits.
94+
only_suggestions | false | You can allow only add from suggestions and not add new tags/pills.
95+
suggestion_limit | 15 | Maximum number of suggestions for typeahead.
96+
suggestion_url | | Url to fetch suggestion. `POST` request will be send with `q` and `limit` parameters. `q` - search string and `limit` - how many suggestions maximum.
97+
suggestions | [] | Array of suggestion.
9398
templates | Object | Set HTML markup. This let you fully manage and style output as you want. No limitations. See [Templates](#templates) for more details.
9499

95100
## Templates
@@ -106,9 +111,9 @@ You can path template through `templates` option.
106111
}
107112
});
108113

109-
Template | Default | Descritpion
114+
Template | Default | Description
110115
---|---|---
111-
pill | `<span class="badge badge-info" data-tag-id="{1}">{0}</span>` | This is main HTML element of the pill. This is also what will be passed to `onRemove(pill)` method. After full pill creation it will include also hidden `<input>`, number if passed and remove icon. `{0}` is the tag text, `{1}` is the tag id. `data-tag-id` is required attribute.
116+
pill | `<span class="badge badge-info">{0}</span>` | This is main HTML element of the pill. This is also what will be passed to `onRemove(pill)` method. After full pill creation it will include also hidden `<input>`, number if passed and remove icon. `{0}` is the tag text.
112117
number | `<sup><small>{0}</small></sup>` | If `num` property exists in [feed](#feed-format) then number will be added. This is template how to format it.
113118
delete_icon | `<i class="icon-remove-sign"></i>` | This is delete icon. If you use FontAwesome or IcoMoon you can change it to display better icon.
114119

component.json

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
{
2-
"name": "bootstrap-tag-pills",
3-
"version": "0.0.1",
4-
"main": [
5-
"./css/bootstrap-tags.css",
6-
"./js/bootstrap-tags.js"
7-
],
8-
"ignore": [
9-
"**/.*",
10-
"node_modules",
11-
"components"
12-
],
13-
"dependencies": {
14-
"bootstrap": "~2.3.1",
15-
"jquery": "~2.0.0"
16-
}
2+
"name": "bootstrap-tag-pills",
3+
"version": "0.0.1",
4+
"main": [
5+
"./css/bootstrap-tags.css",
6+
"./js/bootstrap-tags.js"
7+
],
8+
"ignore": [
9+
"**/.*",
10+
"node_modules",
11+
"components"
12+
],
13+
"dependencies": {
14+
"bootstrap": "~2.3.1",
15+
"jquery": "~2.0.0",
16+
"jquery-color": "~2.1.2"
17+
}
1718
}

css/bootstrap-tags.css

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,6 @@
1-
1+
.add-pill {
2+
cursor: pointer;
3+
}
4+
.tag-input {
5+
outline: none;
6+
}

index.html

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,30 +22,44 @@ <h1>Bootstrap Tags Demo</h1>
2222
<div class="span8">
2323
<div id="bs-tags"></div>
2424
<style type="text/css">
25-
#bs-tags *[data-tag-id] {
25+
#bs-tags .badge-tag {
2626
margin-right: 5px;
27+
margin-bottom: 5px;
2728
font-weight: 100;
2829
font-size: 14px;
2930
line-height: 20px;
3031
}
31-
#bs-tags *[data-tag-id] a.tag-remove {
32+
#bs-tags .tag-icon {
3233
margin-left: 5px;
3334
margin-right: -3px;
3435
}
35-
#bs-tags *[data-tag-id] a.tag-link {
36+
#bs-tags .badge-tag a.tag-link {
3637
color: #ffffff;
3738
text-decoration: underline;
3839
}
40+
#bs-tags .tag-input {
41+
border: 0 solid;
42+
background-color: transparent;
43+
height: 18px;
44+
margin-top: -5px;
45+
font-weight: 100;
46+
color: #ffffff;
47+
}
3948
</style>
4049
</div>
4150
</div>
4251
</div>
4352

4453
<script type="text/javascript" src="components/jquery/jquery.js"></script>
4554
<script type="text/javascript" src="components/bootstrap/docs/assets/js/bootstrap.js"></script>
55+
<script type="text/javascript" src="components/jquery-color/jquery.color.js"></script>
4656
<script type="text/javascript" src="js/bootstrap-tags.js"></script>
4757
<script type="text/javascript">
48-
$('#bs-tags').tags({values: ["Banana"], values_url: 'urls/defaults.json'});
58+
$('#bs-tags').tags({
59+
suggestions: ["Banana", "Durian", "Cocos"],
60+
suggestion_url: "urls/suggestions.json.php",
61+
values_url: 'urls/defaults.json'
62+
});
4963
</script>
5064
</body>
5165
</html>

js/bootstrap-tags.js

Lines changed: 167 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@
77
values_url: '',
88

99
templates: {
10-
pill: '<span class="badge badge-info" data-tag-id="{1}">{0}</span>',
11-
delete_icon: '<i class="icon-remove-sign"></i>',
12-
number: ' <sup><small>{0}</small></sup>'
10+
pill: '<span class="badge badge-info badge-tag">{0}</span>',
11+
add_pill: '<span class="badge badge-success badge-tag"></span>',
12+
number: ' <sup><small>{0}</small></sup>',
13+
plus_icon: '<i class="icon-plus-sign tag-icon"></i>',
14+
delete_icon: '<i class="icon-remove-sign tag-icon"></i>',
15+
ok_icon: '<i class="icon-ok-sign tag-icon"></i>'
1316
},
1417

1518
limit: 0,
@@ -19,13 +22,21 @@
1922
can_delete: true,
2023
can_add: true,
2124

25+
double_hilight: '#0B3549',
26+
2227
input_name: 'tags[]',
2328

2429
lang: {
2530
delete: "Delete",
2631
limit: "You have reached limit of only {0} tags to be added."
2732
},
2833

34+
suggestion_limit: 15,
35+
suggestion_url: '',
36+
suggestions: [],
37+
38+
only_suggestions: false,
39+
2940
remove_url: '',
3041

3142
onLoadDefaults: function(values) {
@@ -38,9 +49,12 @@
3849
},
3950
onBeforeAdd: function(pill, value) {
4051
return pill;
52+
},
53+
onLoadSuggestions: function(values) {
54+
return values;
4155
}
56+
}
4257

43-
};
4458

4559
function Tags(context, params) {
4660

@@ -59,36 +73,166 @@
5973
}
6074
$self.options.values = $self.options.onLoadDefaults($self.options.values);
6175

62-
var pills_list = $(document.createElement('div')).addClass('pills-list').appendTo(context);
76+
var pills_list = $(document.createElement('span')).addClass('pills-list').appendTo(context);
6377

78+
$self.options.values = $self._prepare($self.options.values);
6479
$.each($self.options.values, function(key, value) {
6580
$self.addTag(pills_list, value);
6681
});
6782

68-
$('[data-toggle="tooltip"]').tooltip();
83+
if($self.options.can_add) {
84+
85+
var labels = [], mapped = [];
86+
87+
var input = $(document.createElement('input'))
88+
.addClass('tag-input')
89+
.attr({"autocomplete": "off"})
90+
.css('outline', 'none')
91+
.typeahead({
92+
items: $self.options.suggestion_limit,
93+
source: function(query, process) {
94+
95+
var suggestions = $self.options.suggestions;
96+
labels = [];
97+
mapped = {};
98+
99+
if($self.options.suggestion_url) {
100+
$.ajax({
101+
dataType: 'json', type: 'post', async: false, url: $self.options.suggestion_url,
102+
data: {q: query, limit: $self.options.suggestion_limit}
103+
}).done(function(json) {
104+
if(typeof json == "object") {
105+
suggestions = $.merge(suggestions, json);
106+
}
107+
});
108+
}
109+
110+
suggestions = $self.options.onLoadSuggestions(suggestions);
111+
suggestions = $self._prepare(suggestions);
112+
113+
$.each(suggestions, function(i, item) {
114+
mapped[item.html] = item
115+
labels.push(item.html)
116+
});
117+
118+
return labels;
119+
},
120+
updater: function(item) {
121+
$self._addTag(pills_list, input, mapped[item]);
122+
}
123+
})
124+
.click(function(e){
125+
e.stopPropagation();
126+
});
127+
128+
if($self.options.only_suggestions == false) {
129+
input.keyup(function(e) {
130+
if(!$(this).val()) return;
131+
if(e.keyCode == 13) {
132+
$self._addTag(pills_list, $(this));
133+
}
134+
});
135+
}
136+
137+
var add = $($self.options.templates.add_pill)
138+
.append(input)
139+
.append($($self.options.templates.ok_icon)
140+
.css('cursor', 'pointer')
141+
.click(function(e) {
142+
e.stopPropagation();
143+
$self._addTag(pills_list, input);
144+
input.focus();
145+
})
146+
)
147+
.hide()
148+
.appendTo(context);
149+
150+
var wait = $($self.options.templates.add_pill)
151+
.addClass('add-pill')
152+
.css('cursor', 'pointer')
153+
.html('...')
154+
.append($(document.createElement('span'))
155+
.attr({})
156+
.addClass('tag-add')
157+
.append($self.options.templates.plus_icon)
158+
)
159+
.click(function() {
160+
add.show();
161+
input.focus();
162+
var $this = $(this);
163+
$this.hide();
164+
165+
setTimeout(function() {
166+
$('body').one('click', function() {
167+
add.hide();
168+
$this.show();
169+
});
170+
}, 200);
171+
172+
})
173+
.appendTo(context);
174+
}
69175
}
70176

177+
178+
Tags.prototype._prepare = function(values) {
179+
180+
$.each(values, function(key, value) {
181+
if(!value) {
182+
delete values[key];
183+
return true;
184+
}
185+
if(typeof value == "string") {
186+
values[key] = {id: value, text: value, html: value};
187+
}
188+
values[key].html = values[key].html || values[key].text;
189+
values[key].url = value.url || '';
190+
values[key].title = value.title || '';
191+
values[key].num = parseInt(value.num || '0');
192+
});
193+
return values;
194+
}
195+
Tags.prototype._addTag = function(pills_list, input, value) {
196+
197+
if(!value) {
198+
value = this._prepare([input.val()])[0];
199+
}
200+
201+
if(this.addTag(pills_list, value)) {
202+
input.val('').focus();
203+
}
204+
}
71205
Tags.prototype.addTag = function(pills_list, value) {
72206
var $self = this;
73207

208+
if(!value) return false;
209+
74210
if(parseInt($self.options.limit) > 0 && pills_list.children().length >= parseInt($self.options.limit)) {
75211
$self.options.onError(10, $self.options.lang.limit.format($self.options.limit));
76-
return;
77-
}
78-
79-
if(typeof value == "string") {
80-
value = {id: value, text: value, html: value};
212+
return false;
81213
}
82-
value.html = value.html || value.text;
83-
value.url = value.url || '';
84-
value.title = value.title || '';
85-
value.num = parseInt(value.num || '0');
86214

87215
if(!value.id || !value.text) {
88216
$self.options.onError(11, 'Not correct object format to create tag/pill');
89217
$.error('Not correct object format to create tag/pill');
90218
}
91219

220+
var unique = '';
221+
$.each(pills_list.children(), function(key, val) {
222+
if(value.id.toString().toLowerCase() == $(val).data('tag-id').toString().toLowerCase()) {
223+
unique = $(val);
224+
return false;
225+
}
226+
});
227+
228+
if(unique) {
229+
var color = $(pills_list.children()[0]).css('background-color');
230+
unique.stop().animate({"backgroundColor": $self.options.double_hilight}, 100, 'swing', function() {
231+
unique.stop().animate({"backgroundColor": color}, 100)
232+
});
233+
return false;
234+
}
235+
92236
if(value.url) {
93237
var title = value.title ? ' data-toggle="tooltip" title="' + value.title + '"' : '';
94238
value.text = '<a class="tag-link" ' + title + ' target="' + $self.options.tag_link_target + '" href="' + value.url + '">' + value.text + '</a>';
@@ -111,7 +255,8 @@
111255

112256
var num = value.num > 0 ? $self.options.templates.number.format(value.num) : '';
113257

114-
var tag = $($self.options.templates.pill.format(value.text, value.id))
258+
var tag = $($self.options.templates.pill.format(value.text))
259+
.attr('data-tag-id', value.id)
115260
.append(num, icon, $(document.createElement('input'))
116261
.attr({
117262
"data-tag-hidden": value.id,
@@ -124,6 +269,10 @@
124269
tag = $self.options.onBeforeAdd(tag, value);
125270

126271
pills_list.append(tag);
272+
273+
$('[data-toggle="tooltip"]').tooltip();
274+
275+
return true;
127276
}
128277

129278
Tags.prototype.removeTag = function(tag) {
@@ -154,4 +303,5 @@ if(!String.prototype.format) {
154303
return typeof args[number] != 'undefined' ? args[number] : match;
155304
});
156305
};
157-
}
306+
}
307+
;

urls/defaults.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@
22
{
33
"id": "2",
44
"text": "Apple",
5-
"html": "This is <b>Apple</b>!",
5+
"html": "You have found Apple",
66
"num": 5,
7-
"url": "http://tags/",
7+
"url": "http://www.apple.com",
88
"title": "Click here to see all &quot;records&quot; of only Apple's"
99
},
1010
{
1111
"id": "3",
1212
"text": "Mango",
13-
"html": "I like <b>Mango</b>!",
14-
"url": "/"
13+
"html": "You have found <b>Mango</b>!",
14+
"url": "http://www.mango.com/"
1515
},
1616
{
1717
"id": "5",

0 commit comments

Comments
 (0)