implemented better files path/tree dropdown using a modified bootstrap-combobox
This commit is contained in:
parent
55bba64481
commit
0f5302ba1b
150
resources/public/css/bootstrap-combobox.css
vendored
Normal file
150
resources/public/css/bootstrap-combobox.css
vendored
Normal file
|
@ -0,0 +1,150 @@
|
|||
.combobox-container {
|
||||
margin-bottom: 5px;
|
||||
*zoom: 1;
|
||||
}
|
||||
.combobox-container:before,
|
||||
.combobox-container:after {
|
||||
display: table;
|
||||
content: "";
|
||||
}
|
||||
.combobox-container:after {
|
||||
clear: both;
|
||||
}
|
||||
.combobox-container input,
|
||||
.combobox-container .uneditable-input {
|
||||
-webkit-border-radius: 0 3px 3px 0;
|
||||
-moz-border-radius: 0 3px 3px 0;
|
||||
border-radius: 0 3px 3px 0;
|
||||
}
|
||||
.combobox-container input:focus,
|
||||
.combobox-container .uneditable-input:focus {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
.combobox-container .uneditable-input {
|
||||
border-left-color: #ccc;
|
||||
}
|
||||
.combobox-container .add-on {
|
||||
float: left;
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
min-width: 16px;
|
||||
height: inherit !important;
|
||||
margin-right: -1px;
|
||||
padding: 4px 5px;
|
||||
font-weight: normal;
|
||||
color: #999999;
|
||||
text-align: center;
|
||||
text-shadow: 0 1px 0 #ffffff;
|
||||
background-color: #f5f5f5;
|
||||
border: 1px solid #ccc;
|
||||
-webkit-border-radius: 3px 0 0 3px;
|
||||
-moz-border-radius: 3px 0 0 3px;
|
||||
border-radius: 3px 0 0 3px;
|
||||
|
||||
}
|
||||
.combobox-container .active {
|
||||
background-color: #a9dba9;
|
||||
border-color: #46a546;
|
||||
}
|
||||
.combobox-container input,
|
||||
.combobox-container .uneditable-input {
|
||||
float: left;
|
||||
-webkit-border-radius: 3px 0 0 3px;
|
||||
-moz-border-radius: 3px 0 0 3px;
|
||||
border-radius: 3px 0 0 3px;
|
||||
}
|
||||
.combobox-container .uneditable-input {
|
||||
border-left-color: #eee;
|
||||
border-right-color: #ccc;
|
||||
}
|
||||
.combobox-container .add-on {
|
||||
margin-right: 0;
|
||||
margin-left: -1px;
|
||||
-webkit-border-radius: 0 3px 3px 0;
|
||||
-moz-border-radius: 0 3px 3px 0;
|
||||
border-radius: 0 3px 3px 0;
|
||||
}
|
||||
.combobox-container input:first-child {
|
||||
*margin-left: -160px;
|
||||
}
|
||||
.combobox-container input:first-child + .add-on {
|
||||
*margin-left: -21px;
|
||||
}
|
||||
.combobox-container select {
|
||||
display: inline-block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
text-indent: -99999px;
|
||||
*text-indent: 0;
|
||||
}
|
||||
.form-search .combobox-container,
|
||||
.form-inline .combobox-container {
|
||||
display: inline-block;
|
||||
margin-bottom: 0;
|
||||
vertical-align: top;
|
||||
}
|
||||
.form-search .combobox-container .add-on,
|
||||
.form-inline .combobox-container .add-on {
|
||||
vertical-align: middle;
|
||||
}
|
||||
.combobox-selected .combobox-clear {
|
||||
/*display: inline-block;*/
|
||||
}
|
||||
.combobox-selected .caret {
|
||||
/*display: none;*/
|
||||
}
|
||||
.combobox-clear {
|
||||
display: none;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
line-height: 14px;
|
||||
vertical-align: top;
|
||||
opacity: 0.3;
|
||||
filter: alpha(opacity=30);
|
||||
}
|
||||
.dropdown:hover .combobox-clear,
|
||||
.open.dropdown .combobox-clear {
|
||||
opacity: 1;
|
||||
filter: alpha(opacity=100);
|
||||
}
|
||||
.btn .combobox-clear {
|
||||
margin-top: 1px;
|
||||
margin-left: 1px;
|
||||
}
|
||||
.btn:hover .combobox-clear,
|
||||
.open.btn-group .combobox-clear {
|
||||
opacity: 1;
|
||||
filter: alpha(opacity=100);
|
||||
}
|
||||
.typeahead-long {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.control-group.error .combobox-container .add-on {
|
||||
color: #B94A48;
|
||||
border-color: #B94A48;
|
||||
}
|
||||
.control-group.error .combobox-container .caret {
|
||||
border-top-color: #B94A48;
|
||||
}
|
||||
.control-group.warning .combobox-container .add-on {
|
||||
color: #C09853;
|
||||
border-color: #C09853;
|
||||
}
|
||||
.control-group.warning .combobox-container .caret {
|
||||
border-top-color: #C09853;
|
||||
}
|
||||
.control-group.success .combobox-container .add-on {
|
||||
color: #468847;
|
||||
border-color: #468847;
|
||||
}
|
||||
.control-group.success .combobox-container .caret {
|
||||
border-top-color: #468847;
|
||||
}
|
||||
.btn .combobox-clear [class^="icon-"] {
|
||||
line-height: 1.4em;
|
||||
}
|
265
resources/public/js/bootstrap-combobox.js
vendored
Normal file
265
resources/public/js/bootstrap-combobox.js
vendored
Normal file
|
@ -0,0 +1,265 @@
|
|||
/* =============================================================
|
||||
* bootstrap-combobox.js v1.1.1
|
||||
* =============================================================
|
||||
* Copyright 2012 Daniel Farrell
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* ============================================================ */
|
||||
|
||||
!function($) {
|
||||
|
||||
"use strict";
|
||||
|
||||
var Combobox = function(element, options) {
|
||||
this.options = $.extend({}, $.fn.combobox.defaults, options)
|
||||
this.$source = $(element)
|
||||
this.$container = this.setup()
|
||||
this.$element = this.$container.find('input[type=text]')
|
||||
this.$target = this.$container.find('input[type=hidden]')
|
||||
this.$button = this.$container.find('.dropdown-toggle')
|
||||
this.$menu = $(this.options.menu).appendTo('body')
|
||||
this.matcher = this.options.matcher || this.matcher
|
||||
this.sorter = this.options.sorter || this.sorter
|
||||
this.highlighter = this.options.highlighter || this.highlighter
|
||||
this.shown = false
|
||||
this.selected = false
|
||||
this.refresh()
|
||||
this.transferAttributes()
|
||||
this.listen()
|
||||
}
|
||||
|
||||
/* NOTE: COMBOBOX EXTENDS BOOTSTRAP-TYPEAHEAD.js
|
||||
========================================== */
|
||||
|
||||
Combobox.prototype = $.extend({}, $.fn.typeahead.Constructor.prototype, {
|
||||
|
||||
constructor: Combobox,
|
||||
|
||||
setup: function() {
|
||||
var combobox = $(this.options.template)
|
||||
this.$source.before(combobox)
|
||||
this.$source.hide()
|
||||
return combobox
|
||||
},
|
||||
|
||||
parse: function() {
|
||||
var that = this, map = {}, source = [], selected = false
|
||||
this.$source.find('option').each(function() {
|
||||
var option = $(this)
|
||||
if (option.val() === '') {
|
||||
that.options.placeholder = option.text()
|
||||
return
|
||||
|
||||
}
|
||||
map[option.text()] = option.val()
|
||||
source.push(option.text())
|
||||
if (option.attr('selected'))
|
||||
selected = option.html()
|
||||
})
|
||||
this.map = map
|
||||
if (selected) {
|
||||
this.$element.val(selected)
|
||||
this.$container.addClass('combobox-selected')
|
||||
this.selected = true
|
||||
}
|
||||
return source
|
||||
},
|
||||
|
||||
transferAttributes: function() {
|
||||
this.options.placeholder = this.$source.attr('data-placeholder')
|
||||
|| this.options.placeholder
|
||||
this.$element.attr('placeholder', this.options.placeholder)
|
||||
this.$target.prop('name', this.$source.prop('name'))
|
||||
this.$target.val(this.$source.val())
|
||||
this.$source.removeAttr('name') // Remove from source otherwise form will pass parameter twice.
|
||||
this.$element.attr('required', this.$source.attr('required'))
|
||||
this.$element.attr('rel', this.$source.attr('rel'))
|
||||
this.$element.attr('title', this.$source.attr('title'))
|
||||
this.$element.attr('class', this.$source.attr('class'))
|
||||
},
|
||||
|
||||
toggle: function(e) {
|
||||
if (this.shown) {
|
||||
this.hide()
|
||||
} else {
|
||||
// don't clear the textbox when the arrow is clicked and keep the focus
|
||||
// inside the textbox to make sure that when the focus leaves (e.g. user
|
||||
// clicks outside of it) that the dropdown closes as expected
|
||||
this.lookup(e, '') // pass empty query to show all options (this seems more intuitive to me?)
|
||||
this.$element.focus()
|
||||
}
|
||||
},
|
||||
|
||||
clearElement: function() {
|
||||
this.$element.val('').focus()
|
||||
},
|
||||
|
||||
clearTarget: function() {
|
||||
this.$source.val('')
|
||||
this.$target.val('')
|
||||
this.$container.removeClass('combobox-selected')
|
||||
this.selected = false
|
||||
},
|
||||
|
||||
triggerChange: function() {
|
||||
this.$source.trigger('change')
|
||||
},
|
||||
|
||||
refresh: function() {
|
||||
this.source = this.parse()
|
||||
this.options.items = this.source.length
|
||||
},
|
||||
|
||||
// modified typeahead function adding container and target handling
|
||||
select: function() {
|
||||
var val = this.$menu.find('.active').attr('data-value')
|
||||
this.$element.val(this.updater(val)).trigger('change')
|
||||
this.$target.val(this.map[val]).trigger('change')
|
||||
this.$source.val(this.map[val]).trigger('change')
|
||||
this.$container.addClass('combobox-selected')
|
||||
this.selected = true
|
||||
return this.hide()
|
||||
},
|
||||
|
||||
// modified typeahead function removing the blank handling and source function handling
|
||||
lookup: function(event, existingVal) {
|
||||
if (existingVal != undefined)
|
||||
this.query = existingVal
|
||||
else
|
||||
this.query = this.$element.val()
|
||||
return this.process(this.source)
|
||||
},
|
||||
|
||||
// modified typeahead function adding button handling and remove mouseleave
|
||||
listen: function() {
|
||||
this.$element.on('focus', $.proxy(this.focus, this)).on('blur',
|
||||
$.proxy(this.blur, this)).on('keypress',
|
||||
$.proxy(this.keypress, this)).on('keyup',
|
||||
$.proxy(this.keyup, this))
|
||||
|
||||
if (this.eventSupported('keydown')) {
|
||||
this.$element.on('keydown', $.proxy(this.keydown, this))
|
||||
}
|
||||
|
||||
this.$menu.on('click', $.proxy(this.click, this)).on('mouseenter',
|
||||
'li', $.proxy(this.mouseenter, this)).on('mouseleave',
|
||||
'li', $.proxy(this.mouseleave, this))
|
||||
|
||||
this.$button.on('click', $.proxy(this.toggle, this))
|
||||
},
|
||||
|
||||
// modified typeahead function to clear on type and prevent on moving around
|
||||
keyup: function(e) {
|
||||
switch (e.keyCode) {
|
||||
case 40: // down arrow
|
||||
case 39: // right arrow
|
||||
case 38: // up arrow
|
||||
case 37: // left arrow
|
||||
case 36: // home
|
||||
case 35: // end
|
||||
case 16: // shift
|
||||
case 17: // ctrl
|
||||
case 18: // alt
|
||||
break
|
||||
|
||||
case 9: // tab
|
||||
case 13: // enter
|
||||
if (!this.shown) {
|
||||
if (this.options.enter_triggers_change === true)
|
||||
this.select()
|
||||
else
|
||||
return
|
||||
}
|
||||
this.select()
|
||||
break
|
||||
|
||||
case 27: // escape
|
||||
if (!this.shown)
|
||||
return
|
||||
|
||||
this.hide()
|
||||
break
|
||||
|
||||
default:
|
||||
this.clearTarget()
|
||||
this.lookup()
|
||||
}
|
||||
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
},
|
||||
|
||||
// modified typeahead function to force a match and add a delay on hide
|
||||
blur: function(e) {
|
||||
var that = this
|
||||
this.focused = false
|
||||
var val = this.$element.val()
|
||||
if (!!this.options.force_match) {
|
||||
if (!this.selected && val !== '') {
|
||||
this.$element.val('')
|
||||
this.$target.val('').trigger('change')
|
||||
this.$source.val('').trigger('change')
|
||||
}
|
||||
} else {
|
||||
this.$target.val(val).trigger('change')
|
||||
this.$source.val(val).trigger('change')
|
||||
}
|
||||
if (!this.mousedover && this.shown)
|
||||
setTimeout(function() {
|
||||
that.hide()
|
||||
}, 200)
|
||||
},
|
||||
|
||||
// modified typeahead function to not hide
|
||||
mouseleave: function(e) {
|
||||
this.mousedover = false
|
||||
},
|
||||
|
||||
val: function(val) {
|
||||
if (val != undefined) {
|
||||
this.$element.val(val)
|
||||
this.$target.val(val).trigger('change')
|
||||
this.$source.val(val).trigger('change')
|
||||
return val;
|
||||
} else
|
||||
return this.$target.val()
|
||||
}
|
||||
})
|
||||
|
||||
/* COMBOBOX PLUGIN DEFINITION
|
||||
* =========================== */
|
||||
|
||||
$.fn.combobox = function(option) {
|
||||
return this
|
||||
.each(function() {
|
||||
var $this = $(this), data = $this.data('combobox'), options = typeof option == 'object'
|
||||
&& option
|
||||
if (!data)
|
||||
$this.data('combobox', (data = new Combobox(this,
|
||||
options)))
|
||||
if (typeof option == 'string')
|
||||
data[option]()
|
||||
})
|
||||
}
|
||||
|
||||
$.fn.combobox.defaults = {
|
||||
template: '<div class="combobox-container"><input type="hidden" /><input type="text" autocomplete="off" /><span class="add-on btn dropdown-toggle" data-dropdown="dropdown"><span class="caret"/><span class="combobox-clear"><i class="icon-remove"/></span></span></div>',
|
||||
menu: '<ul class="typeahead typeahead-long dropdown-menu"></ul>',
|
||||
item: '<li><a href="#"></a></li>',
|
||||
force_match: true,
|
||||
enter_triggers_change: false
|
||||
}
|
||||
|
||||
$.fn.combobox.Constructor = Combobox
|
||||
|
||||
}(window.jQuery);
|
|
@ -10,6 +10,7 @@
|
|||
<link href="{{context}}/css/prettify.css" rel="stylesheet" type="text/css" />
|
||||
{% if user-id %}
|
||||
<link href="{{context}}/css/select2.css" rel="stylesheet" type="text/css" />
|
||||
<link href="{{context}}/css/bootstrap-combobox.css" rel="stylesheet" type="text/css" />
|
||||
{% endif %}
|
||||
<link href="{{context}}/css/screen.css" rel="stylesheet" type="text/css" />
|
||||
|
||||
|
@ -24,6 +25,7 @@
|
|||
<script src="{{context}}/js/prettify.js" type="text/javascript"></script>
|
||||
{% if user-id %}
|
||||
<script src="{{context}}/js/select2.min.js" type="text/javascript"></script>
|
||||
<script src="{{context}}/js/bootstrap-combobox.js" type="text/javascript"></script>
|
||||
<script src="{{context}}/js/modals.js" type="text/javascript"></script>
|
||||
{% endif %}
|
||||
<script src="{{context}}/js/site.js" type="text/javascript"></script>
|
||||
|
|
|
@ -7,12 +7,13 @@
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<div class="pull-left">
|
||||
<div class="pull-left form-inline">
|
||||
<select id="tree">
|
||||
{% for folder in tree %}
|
||||
<option{% ifequal folder path %} selected{% endifequal %}>{{folder}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<a class="btn" href="#" id="tree-selector">Go</a>
|
||||
</div>
|
||||
<div class="pull-right">
|
||||
<a class="btn btn-primary" href="#" data-toggle="modal" data-target="#newFileModal">Upload File</a>
|
||||
|
@ -94,8 +95,14 @@
|
|||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
$('#tree').change(function() {
|
||||
window.location.href = '{{context}}/listfiles' + $(this).val();
|
||||
$(document).ready(function() {
|
||||
$('#tree').combobox({force_match: false});
|
||||
$('#tree').data('combobox').val('{{path}}');
|
||||
});
|
||||
|
||||
$('#tree-selector').click(function() {
|
||||
var path = $('#tree').data('combobox').val();
|
||||
window.location.href = '{{context}}/listfiles' + path;
|
||||
});
|
||||
|
||||
$('a[data-deletefileid]').click(function() {
|
||||
|
|
Reference in a new issue