browser dialog filter/search now mostly works...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2015-06-18 00:13:30 +03:00
parent eb23b632f7
commit 3dbd5367e5
2 changed files with 141 additions and 40 deletions

View File

@ -115,6 +115,14 @@
background: rgba(0,0,0, 0.05); background: rgba(0,0,0, 0.05);
} }
/* XXX need to make the next two different... */
.browse .list div.filtered-out {
display: none;
}
.browse .list div.disabled {
opacity: 0.3;
}
.browse:focus .list div.selected { .browse:focus .list div.selected {
background: rgba(0,0,0, 0.1); background: rgba(0,0,0, 0.1);
box-shadow: rgba(0,0,0,0.2) 0.1em 0.1em 0.2em; box-shadow: rgba(0,0,0,0.2) 0.1em 0.1em 0.2em;
@ -125,6 +133,30 @@
} }
::-webkit-scrollbar {
width: 10px;
}
::-webkit-scrollbar-button {
display: none;
}
::-webkit-scrollbar-track {
}
::-webkit-scrollbar-track-piece {
background: gray;
}
::-webkit-scrollbar-thumb {
background: rgba(0, 0, 0, 0.05);
}
::-webkit-scrollbar-thumb:hover {
background: rgba(0, 0, 0, 0.3);
}
::-webkit-scrollbar-corner {
}
::-webkit-resizer {
}
</style> </style>
<script src="../ext-lib/jquery.js"></script> <script src="../ext-lib/jquery.js"></script>
@ -238,7 +270,7 @@ $(function(){
[title] [title]
</div> </div>
<!-- the actual list --> <!-- current path -->
<div class="v-block path"> <div class="v-block path">
<div class="dir"> <div class="dir">
[dir] [dir]
@ -251,7 +283,7 @@ $(function(){
</div> </div>
</div> </div>
<!-- the actual list --> <!-- path list -->
<div class="v-block list"> <div class="v-block list">
<div> <div>
[dir] [dir]

View File

@ -9,6 +9,7 @@
/*********************************************************************/ /*********************************************************************/
// XXX add a hook to render the content of an element...
// XXX NOTE: the widget itself does not need a title, that's the job for // XXX NOTE: the widget itself does not need a title, that's the job for
// a container widget (dialog, field, ...) // a container widget (dialog, field, ...)
// ...it can be implemented trivially via an attribute and a :before // ...it can be implemented trivially via an attribute and a :before
@ -70,7 +71,26 @@ var BrowserPrototype = {
// XXX this should prevent event handler deligation... // XXX this should prevent event handler deligation...
keyboard: { keyboard: {
'.browse':{ // filter mappings...
Filter: {
pattern: '.browse .path div.cur[contenteditable]',
// keep text edeting action from affecting the seelction...
ignore: [
'Backspace',
'Left',
'Right',
'Enter',
'Esc',
],
Enter: 'action!',
Esc: 'stopFilter!',
},
General: {
pattern: '.browse',
Up: 'prev', Up: 'prev',
Backspace: 'Up', Backspace: 'Up',
Down: 'next', Down: 'next',
@ -79,6 +99,8 @@ var BrowserPrototype = {
Enter: 'action', Enter: 'action',
Esc: 'close', Esc: 'close',
'/': 'startFilter!',
}, },
}, },
@ -130,21 +152,10 @@ var BrowserPrototype = {
// add current selction indicator... // add current selction indicator...
p.append($('<div>') p.append($('<div>')
.addClass('dir cur') .addClass('dir cur')
// XXX start search/filter...
// - on click / letter set content editable
// - triger filterig on modified
// - disable nav in favor of editing
// - enter/blur to exit edit mode
// - esc to cancel and reset
// XXX add a filter mode... // XXX add a filter mode...
.click(function(){ .click(function(){
that.startFilter()
//that.update(path.concat($(this).text())) //that.update(path.concat($(this).text()))
$(this)
.text('')
.attr('contenteditable', true)
.keyup(function(){
that.filter($(this).text())
})
})) }))
// fill the children list... // fill the children list...
@ -152,7 +163,9 @@ var BrowserPrototype = {
.forEach(function(e){ .forEach(function(e){
l.append($('<div>') l.append($('<div>')
.click(function(){ .click(function(){
that.update(that.path.concat([$(this).text()])) if(!$(this).hasClass('disabled')){
that.update(that.path.concat([$(this).text()]))
}
}) })
.text(e)) .text(e))
}) })
@ -160,9 +173,8 @@ var BrowserPrototype = {
return this return this
}, },
// XXX should have two non_matched modes: // internal actions...
// - hide - hide non-matching content
// - shadow - shadow non-matching content
// XXX pattern modes: // XXX pattern modes:
// - lazy match // - lazy match
// abc -> *abc* -> ^.*abc.*$ // abc -> *abc* -> ^.*abc.*$
@ -172,27 +184,34 @@ var BrowserPrototype = {
// XXX sort: // XXX sort:
// - as-is // - as-is
// - best match // - best match
filter: function(pattern, mode, non_matched, sort){ filter: function(pattern, non_matched, sort){
var that = this var that = this
var browser = this.dom var browser = this.dom
// show all... // show all...
if(pattern == null || pattern.trim() == '*'){ if(pattern == null || pattern.trim() == '*'){
this.update() browser.find('.filtered-out')
.removeClass('filtered-out')
// clear the highlighing...
browser.find('.list b')
.replaceWith(function() { return this.innerHTML })
// basic filter... // basic filter...
} else { } else {
var l = browser.find('.list>div') var l = browser.find('.list>div:not(disabled)')
l.each(function(i, e){ l.each(function(i, e){
e = $(e) e = $(e)
var t = e.text() var t = e.text()
var i = t.search(pattern) var i = t.search(pattern)
if(i < 0){ if(i < 0){
e.remove() e
.addClass('filtered-out')
.removeClass('selected')
} else { } else {
e.html(t.replace(pattern, pattern.bold())) e.html(t.replace(pattern, pattern.bold()))
.removeClass('filtered-out')
} }
}) })
} }
@ -200,7 +219,49 @@ var BrowserPrototype = {
return this return this
}, },
// internal actions... // XXX start search/filter...
// - set content editable
// - triger filterig on modified
// - disable nav in favor of editing
// - enter/blur to exit edit mode
// - esc to cancel and reset
// XXX BUG: when starting with '/' key the '/' gets appended to the
// field...
startFilter: function(){
var range = document.createRange()
var selection = window.getSelection()
var that = this
var e = this.dom.find('.path .dir.cur')
.text('')
.attr('contenteditable', true)
.keyup(function(){
that.filter($(this).text())
})
.focus()
// place the cursor...
range.setStart(e[0], 0)
range.collapse(true)
// XXX
selection.removeAllRanges()
selection.addRange(range)
return this
},
stopFilter: function(){
this.filter('*')
this.dom.find('.path .dir.cur')
.text('')
.removeAttr('contenteditable')
this
.focus()
return this
},
get filtering(){
return this.dom.find('.path .dir.cur[contenteditable]').length > 0
},
// Select a list element... // Select a list element...
// //
@ -257,9 +318,12 @@ var BrowserPrototype = {
// XXX revise return values... // XXX revise return values...
// XXX Q: should this trigger a "select" event??? // XXX Q: should this trigger a "select" event???
// XXX on string/regexp mismatch this will select the first, is this correct??? // XXX on string/regexp mismatch this will select the first, is this correct???
select: function(elem){ select: function(elem, filtering){
var pattern = '.list div:not(.disabled):not(.filtered-out)'
var browser = this.dom var browser = this.dom
var elems = browser.find('.list div') var elems = browser.find(pattern)
filtering = filtering == null ? this.filtering : filtering
if(elems.length == 0){ if(elems.length == 0){
return $() return $()
@ -272,20 +336,22 @@ var BrowserPrototype = {
// first/last... // first/last...
if(elem == 'first' || elem == 'last'){ if(elem == 'first' || elem == 'last'){
return this.select(elems[elem]()) return this.select(elems[elem](), filtering)
// prev/next... // prev/next...
} else if(elem == 'prev' || elem == 'next'){ } else if(elem == 'prev' || elem == 'next'){
var to = this.select('!', browser)[elem]('.list div') var to = this.select('!', filtering)[elem + 'All'](pattern).first()
if(to.length == 0){ if(to.length == 0){
return this.select(elem == 'prev' ? 'last' : 'first', browser) return this.select(elem == 'prev' ? 'last' : 'first', filtering)
} }
this.select('none') this.select('none', filtering)
return this.select(to) return this.select(to, filtering)
// deselect... // deselect...
} else if(elem == 'none'){ } else if(elem == 'none'){
browser.find('.path .dir.cur').empty() if(!filtering){
browser.find('.path .dir.cur').empty()
}
return elems return elems
.filter('.selected') .filter('.selected')
.removeClass('selected') .removeClass('selected')
@ -297,7 +363,7 @@ var BrowserPrototype = {
// number... // number...
// NOTE: on overflow this will get the first/last element... // NOTE: on overflow this will get the first/last element...
} else if(typeof(elem) == typeof(123)){ } else if(typeof(elem) == typeof(123)){
return this.select($(elems.slice(elem)[0] || elems.slice(-1)[0] )) return this.select($(elems.slice(elem)[0] || elems.slice(-1)[0] ), filtering)
// string... // string...
// XXX on mismatch this will select the first, is this correct??? // XXX on mismatch this will select the first, is this correct???
@ -305,29 +371,32 @@ var BrowserPrototype = {
if(/^'.*'$|^".*"$/.test(elem.trim())){ if(/^'.*'$|^".*"$/.test(elem.trim())){
elem = elem.trim().slice(1, -1) elem = elem.trim().slice(1, -1)
} }
return this.select(browser.find('.list div') return this.select(browser.find(pattern)
.filter(function(i, e){ .filter(function(i, e){
return $(e).text() == elem return $(e).text() == elem
})) }), filtering)
// regexp... // regexp...
// XXX on mismatch this will select the first, is this correct??? // XXX on mismatch this will select the first, is this correct???
} else if(elem.constructor === RegExp){ } else if(elem.constructor === RegExp){
return this.select(browser.find('.list div') return this.select(browser.find(pattern)
.filter(function(i, e){ .filter(function(i, e){
return elem.test($(e).text()) return elem.test($(e).text())
})) }), filtering)
// element... // element...
} else { } else {
elem = $(elem).first() elem = $(elem).first()
if(elem.length == 0){ if(elem.length == 0){
this.select() this.select(null, filtering)
} else { } else {
this.select('none')
browser.find('.path .dir.cur').text(elem.text()) this.select('none', filtering)
if(!filtering){
browser.find('.path .dir.cur').text(elem.text())
}
return elem.addClass('selected') return elem.addClass('selected')
} }
} }