Skip to content

Commit 5538cbe

Browse files
Lev KanterLev Kanter
authored andcommitted
multidropdown
1 parent 23b200b commit 5538cbe

File tree

3 files changed

+211
-27
lines changed

3 files changed

+211
-27
lines changed

examples/field.html

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@
4444
cursor: pointer;
4545
padding: 5px 10px;
4646
border: 2px solid #999999;
47+
overflow: hidden;
48+
white-space: nowrap;
4749
}
4850
.tc-dropdown .display:hover {
4951
background-color: #f9f9f9;
@@ -84,22 +86,30 @@
8486
background-color: #000000;
8587
color: #ffffff;
8688
}
89+
90+
.tc-dropdown .tc-dropdown-item input[type="checkbox"] {
91+
margin: 0px 10px 0px 0px;
92+
position: relative;
93+
top: -2px;
94+
}
95+
8796
</style>
8897

8998
</head>
9099

91100
<body>
92101

93-
<div id="my-dropdown" class="tc-field tc-dropdown">
102+
<div id="my-dropdown" class="tc-field tc-dropdown tc-multiple">
94103
<div class="display"></div>
95104
<div class="menu">
96105
<div data-value="-1" class="tc-dropdown-item state-empty">Select a Nut</div>
106+
97107
<div data-value="1" class="tc-dropdown-item">Almonds</div>
98108
<div data-value="2" class="tc-dropdown-item">Cashews</div>
99-
<div data-value="3" class="tc-dropdown-item">Pistachios</div>
100-
<div data-value="4" class="tc-dropdown-item">Walnuts</div>
101-
<div data-value="5" class="tc-dropdown-item">Pecans</div>
102-
<div data-value="6" class="tc-dropdown-item">Hazlenuts</div>
109+
<div data-value="3" class="tc-dropdown-item"><input type="checkbox">Pistachios</div>
110+
<div data-value="4" class="tc-dropdown-item"><input type="checkbox">Walnuts</div>
111+
<div data-value="5" class="tc-dropdown-item"><input type="checkbox">Pecans</div>
112+
<div data-value="6" class="tc-dropdown-item"><input type="checkbox">Hazlenuts</div>
103113
</div>
104114
</div>
105115

@@ -115,7 +125,9 @@
115125
</div>
116126
</select>
117127

118-
<input id="my-input" type="text">
128+
<div class="tc-field">
129+
<input id="my-input" type="text">
130+
</div>
119131

120132
<input id="my-password" type="password">
121133

@@ -188,7 +200,9 @@
188200
options: {
189201
handlers: {
190202
change: function(e, d) {
191-
console.info("change from "+ d.from +" to "+ d.to);
203+
if (d) {
204+
console.info("change from "+ d.from +" to "+ d.to);
205+
}
192206
}
193207
}
194208
}

lib/field/tc.field.dropdown.js

Lines changed: 177 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
name: "Dropdown",
3535

3636
props: {
37-
container_class: "tc-dropdown"
37+
CONTAINER_CLASS: "tc-dropdown"
3838
},
3939

4040
proto: {
@@ -59,7 +59,16 @@
5959

6060
on_open: function(instance) {},
6161
on_close: function(instance) {},
62-
on_keydown: function(e) {}
62+
on_keydown: function(e) {},
63+
64+
fx: {
65+
open: function($menu) {
66+
$menu.slideDown(100);
67+
},
68+
close: function($menu) {
69+
$menu.slideUp(100);
70+
}
71+
}
6372
},
6473

6574
_init: function() {
@@ -144,8 +153,8 @@
144153
generate_from_dom: function(options) {
145154
var me, $items;
146155
me = this;
147-
this._base.generate_from_dom.apply(this, arguments);
148-
this.$e.addClass(field[this._name].container_class);
156+
field._Base.prototype.generate_from_dom.apply(this, arguments);
157+
this.$e.addClass(field[this._name].CONTAINER_CLASS);
149158
this._init_elements();
150159
$items = this.get_items(true);
151160
if (typeof(this.internal.o.item_handlers) === "object") {
@@ -170,7 +179,7 @@
170179

171180
generate_from_scratch: function(options) {
172181
this.$e = $("<div></div>")
173-
.addClass(field.co.BASE_CLASS+ " " +field[this._name].container_class)
182+
.addClass(field.co.BASE_CLASS+ " " +field[this._name].CONTAINER_CLASS)
174183
.html("<div class='"+ this.internal.o.display_class +"'></div>"+
175184
"<div class='"+ this.internal.o.menu_class + "'></div>");
176185
this._init_elements();
@@ -271,12 +280,22 @@
271280
_set_val: function(val) {
272281
var me, $items;
273282
me = this;
283+
284+
if (val === this.internal.o.empty_val) {
285+
this.clear();
286+
return;
287+
}
288+
274289
$items = this.get_items(true);
275290

276291
$items.each(function(i) {
277292
var $item = $(this);
278293
if ($item.attr(field.co.VALUE) == val) {
279-
$items.removeClass(field.co.ACTIVE_CLASS);
294+
if (!me.internal.multi) {
295+
$items.removeClass(field.co.ACTIVE_CLASS);
296+
} else {
297+
$item.children("input").attr("checked", true);
298+
}
280299
$item.addClass(field.co.ACTIVE_CLASS);
281300
me._update_display($item);
282301
return false;
@@ -296,14 +315,14 @@
296315

297316
$active_item = this.get_active_item();
298317

299-
if (!$active_item) {
318+
if (!$active_item || $active_item === this.internal.o.empty_val) {
300319
if (direction === NI.co.directions.UP) {
301320
$t = $items.last();
302321
} else {
303322
$t = $items.first();
304323
}
305324
} else {
306-
index = $items.index($active_item);
325+
index = $items.index($active_item.last());
307326
if (direction === NI.co.directions.UP) {
308327
$t = $items.eq(index - 1);
309328
if (!$t.length) {
@@ -342,7 +361,7 @@
342361
}
343362

344363
if (typeof arguments[0] === "string") {
345-
this.elements.display.html("<span>"+ arguments[0] +"</span>");
364+
this.elements.display.html("<span class='"+ field.co.MSG_CLASS +"'>"+ arguments[0] +"</span>");
346365
return;
347366
}
348367

@@ -371,7 +390,11 @@
371390
return this;
372391
}
373392
if (this.get_items()) {
374-
this.elements.menu.show();
393+
if (this.internal.o.fx && $.isFunction(this.internal.o.fx.open)) {
394+
this.internal.o.fx.open(this.elements.menu);
395+
} else {
396+
this.elements.menu.show();
397+
}
375398
this.$e.addClass(field.co.OPEN_CLASS);
376399
$(window.document).bind("keydown."+ this._name, {me: this}, this.events.keydown);
377400
this.internal.open = true;
@@ -384,7 +407,11 @@
384407

385408
// (chainable)
386409
close: function() {
387-
this.elements.menu.hide();
410+
if (this.internal.o.fx && $.isFunction(this.internal.o.fx.close)) {
411+
this.internal.o.fx.close(this.elements.menu);
412+
} else {
413+
this.elements.menu.hide();
414+
}
388415
this.$e.removeClass(field.co.OPEN_CLASS);
389416
$(window.document).unbind("keydown."+ this._name, this.events.keydown);
390417
this.internal.open = false;
@@ -438,4 +465,143 @@
438465
}
439466
});
440467

468+
field.synthesize({
469+
name: "MultiDropdown",
470+
471+
base: field.Dropdown,
472+
proto: {
473+
474+
options: {
475+
item_handlers: {
476+
click: function(e) {
477+
e.data.me.toggle_item(e.data.$item);
478+
}
479+
},
480+
481+
empty_val: [],
482+
483+
circular: false,
484+
filtering: false
485+
},
486+
487+
_init: function() {
488+
this.internal.multi = true;
489+
this._base._init.apply(this, arguments);
490+
this.get_items(true).each(function() {
491+
var $item = $(this);
492+
if ($item.hasClass(field.co.ACTIVE_CLASS)) {
493+
$item.children("input").attr("checked", true);
494+
} else {
495+
$item.children("input").removeAttr("checked");
496+
}
497+
});
498+
},
499+
500+
get_val: function() {
501+
var $active, value_list;
502+
$active = this.get_active_item();
503+
if ($active) {
504+
value_list = [];
505+
$active.each(function() {
506+
value_list.push($(this).attr(field.co.VALUE));
507+
});
508+
return value_list;
509+
}
510+
return this.internal.o.empty_val;
511+
},
512+
513+
// (chainable)
514+
toggle_item: function($item, prevent_change) {
515+
var value_list, val, index;
516+
517+
value_list = this.get_val();
518+
val = $item.attr(field.co.VALUE);
519+
if (value_list === this.internal.o.empty_val) {
520+
return this.set_val(val);
521+
}
522+
index = $.inArray(val, value_list);
523+
if (index > -1) {
524+
value_list.splice(index, 1);
525+
} else {
526+
value_list.push(val);
527+
}
528+
return this.set_val(value_list);
529+
},
530+
531+
_set_val: function(val) {
532+
var me = this, $items;
533+
534+
if (!$.isArray(val)) {
535+
this._base._set_val.apply(this, arguments);
536+
return;
537+
}
538+
539+
$items = this.get_items(true);
540+
541+
$items.each(function() {
542+
var $item = $(this);
543+
$item.removeClass(field.co.ACTIVE_CLASS);
544+
$item.children("input").removeAttr("checked");
545+
});
546+
547+
$.each(val, function(i, v) {
548+
$items.each(function(i) {
549+
var $item = $(this);
550+
if ($item.attr(field.co.VALUE) == v) {
551+
$item.addClass(field.co.ACTIVE_CLASS);
552+
$item.children("input").attr("checked", true);
553+
return false;
554+
}
555+
});
556+
});
557+
558+
this._update_display();
559+
},
560+
561+
clear: function() {
562+
this.get_items(true).removeClass(field.co.ACTIVE_CLASS).children("input").removeAttr("checked");
563+
this._update_display(false);
564+
return this;
565+
},
566+
567+
_update_display: function() {
568+
var display_list, $active, buf, i;
569+
570+
if (arguments[0] instanceof $ && arguments[0].length > 1) {
571+
display_list = [];
572+
$.each(arguments[0], function() {
573+
display_list.push(field.get_display_text($(this)));
574+
});
575+
576+
} else if (arguments[0] !== false && !arguments[0]) {
577+
$active = this.get_active_item();
578+
if ($active) {
579+
display_list = [];
580+
$active.each(function() {
581+
display_list.push(field.get_display_text($(this)));
582+
});
583+
584+
} else {
585+
this._base._update_display.apply(this, [false]);
586+
return;
587+
}
588+
589+
} else {
590+
this._base._update_display.apply(this, arguments);
591+
return;
592+
}
593+
594+
buf = "";
595+
for (i = 0; i < display_list.length; i += 1) {
596+
buf += "<span>"+ display_list[i] +"</span>";
597+
if (i < display_list.length - 1) {
598+
buf += ", ";
599+
}
600+
}
601+
this.elements.display.html(buf);
602+
}
603+
604+
}
605+
});
606+
441607
}(this, this.jQuery));

lib/tc.field.js

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@
3636
BASE_CLASS: "tc-field",
3737
LABEL_CLASS: "tc-field-label",
3838
HINT_CLASS: "tc-field-hint",
39+
MSG_CLASS: "tc-field-message",
40+
41+
MULTI_CLASS: "tc-multiple",
3942

4043
ACTIVE_CLASS: "state-active",
4144
DISABLED_CLASS: "state-disabled",
@@ -71,8 +74,8 @@
7174
this.elements = {};
7275
this.events = {};
7376
this.internal = {
74-
o: $.extend({}, (this.options || {}),
75-
(o.base.prototype.options || {}),
77+
o: $.extend({}, (o.base.prototype.options || {}),
78+
(this.options || {}),
7679
user_options)
7780
};
7881

@@ -99,7 +102,7 @@
99102
return this;
100103
};
101104
field[o.name].prototype = $.extend({}, o.base.prototype, o.proto);
102-
105+
103106
if (o.props && typeof o.props === "object") {
104107
$.each(o.props, function(k, v) {
105108
field[o.name][k] = v;
@@ -127,8 +130,12 @@
127130
if ($f.hasClass(field.co.BASE_CLASS)) {
128131
$e = $f;
129132

130-
if ($e.hasClass(field.Dropdown.container_class)) {
131-
field_type = "Dropdown";
133+
if ($e.hasClass(field.Dropdown.CONTAINER_CLASS)) {
134+
if ($e.hasClass(field.co.MULTI_CLASS)) {
135+
field_type = "MultiDropdown";
136+
} else {
137+
field_type = "Dropdown";
138+
}
132139
}
133140

134141
} else {
@@ -138,12 +145,9 @@
138145
}
139146
$e = $f.parent();
140147

141-
console.log(o);
142-
console.log($f);
143-
144148
switch ($f[0].nodeName.toLowerCase()) {
145149
case "input":
146-
switch ( ($f.attr("type") ? $f.attr("type").toLowerCase() : 'text')) {
150+
switch ($f.attr("type") ? $f.attr("type").toLowerCase() : "text") {
147151
case "text":
148152
field_type = "TextInput";
149153
break;

0 commit comments

Comments
 (0)