refactored item rendering + features and tweaks...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2019-06-23 04:45:31 +03:00
parent c1eac80503
commit 9c0f48ea3b
2 changed files with 161 additions and 44 deletions

View File

@ -52,6 +52,12 @@ body {
text-decoration-skip-ink: none; text-decoration-skip-ink: none;
} }
/*
.browse-widget .list .item .button.toggle-collapse {
float: left;
}
*/
/* XXX stub... /* XXX stub...
.browse-widget:not(.flat) .list .text:first-child:before { .browse-widget:not(.flat) .list .text:first-child:before {
@ -216,6 +222,9 @@ requirejs([
// XXX this is not supported by .map(..)... // XXX this is not supported by .map(..)...
make.nest('$ne$sted', browser.Browser(function(make){ make.nest('$ne$sted', browser.Browser(function(make){
make('ab') make('ab')
make($('<img src="../../experiments/grayscale.jpg">'))
//make($('<img src="../../experiments/grayscale.jpg">')[0])
//make('<img src="../../experiments/grayscale.jpg">')
})), })),
]) ])
make('in between two $subtrees...') make('in between two $subtrees...')
@ -259,7 +268,8 @@ requirejs([
['&ndash;', ['&ndash;',
'buttonAction: item button focused -- example button action...'], 'buttonAction: item button focused -- example button action...'],
['&square;', ['&square;',
function(){ console.log('BUTTON:', ...arguments) }], function(){ console.log('BUTTON:', ...arguments) },
{ keys: 'r' }],
'ToggleCollapse', 'ToggleCollapse',
'ToggleDisabled', 'ToggleDisabled',
], ],

View File

@ -172,26 +172,37 @@ var buttons = Items.buttons = {}
// Checkbox('!attr') // Checkbox('!attr')
// //
// XXX rename -- distinguish from actual button... // XXX rename -- distinguish from actual button...
buttons.Checkbox = function(attr){ buttons.Checkbox = function(item, attr){
return function(item){ return (attr[0] == '!'
return (attr[0] == '!' && !item[attr.slice(1)])
&& !item[attr.slice(1)]) || item[attr] ?
|| item[attr] ? '&#9744;'
'&#9744;' : '&#9745;' }
: '&#9745;' } }
// XXX can we make these not use the same icon... // XXX can we make these not use the same icon...
buttons.ToggleDisabled = [ buttons.ToggleDisabled = [
'Checkbox: "disabled"', 'Checkbox: "disabled"',
'toggleDisabled: item', 'toggleDisabled: item',
true] true,
{
alt: 'Disable/enable item',
cls: 'toggle-disabled',
}]
buttons.ToggleHidden = [ buttons.ToggleHidden = [
'Checkbox: "hidden"', 'Checkbox: "hidden"',
'toggleHidden: item'] 'toggleHidden: item',
{
alt: 'Show/hide item',
cls: 'toggle-hidden',
}]
buttons.ToggleSelected = [ buttons.ToggleSelected = [
'Checkbox: "selected"', 'Checkbox: "selected"',
'toggleSelect: item'] 'toggleSelect: item',
{
alt: 'Select/deselect item',
cls: 'toggle-select',
}]
// NOTE: this button is disabled for all items but the ones with .children... // NOTE: this button is disabled for all items but the ones with .children...
buttons.ToggleCollapse = [ buttons.ToggleCollapse = [
function(item){ function(item){
@ -204,8 +215,24 @@ buttons.ToggleCollapse = [
'toggleCollapse: item', 'toggleCollapse: item',
// disable button for all items that do not have children... // disable button for all items that do not have children...
function(item){ function(item){
return 'children' in item }] return 'children' in item },
{
alt: 'Collapse/expand item',
cls: function(item){
return 'children' in item ?
'toggle-collapse'
: ['toggle-collapse', 'blank'] },
}]
// XXX delete button -- requires .markDelete(..) action...
buttons.Delete = [
'&times;',
'markDelete: item',
{
alt: 'Mark item for deletion',
cls: 'toggle-delete',
//keys: ['Delete', 'd'],
}]
@ -3151,7 +3178,7 @@ var BrowserPrototype = {
// ['html', // ['html',
// <handler>], // <handler>],
// //
// // action button handler... // // full button handler...
// // // //
// // <arg> can be: // // <arg> can be:
// // - item - item containing the button // // - item - item containing the button
@ -3164,13 +3191,28 @@ var BrowserPrototype = {
// // - number/string/list/object // // - number/string/list/object
// // - any values... // // - any values...
// // // //
// // <force> (optional, bool), of true the button will // // <force> (optional, bool or function), of true the button will
// // be active while the item is disabled... // // be active while the item is disabled...
// // // //
// // NOTE: for more doc see keyboard.Keyboard.parseStringHandler(..) // // NOTE: for more doc see keyboard.Keyboard.parseStringHandler(..)
// ['html', // [
// '<action>: <arg> .. -- comment', // // button view...
// <force>], // 'text or html'
// | '<button-generator>: <arg> .. -- comment'
// | <function>
// | <HTMLElement>,
//
// // button action...
// '<action>: <arg> .. -- comment'
// | <function>,
//
// // force active (optional)...
// bool
// | <function>,
//
// // button metadata (optional)...
// <metadata>,
// ],
// //
// ... // ...
// ] // ]
@ -3216,6 +3258,9 @@ var BrowserPrototype = {
}, },
}, },
// If true will disable button shortcut key handling...
//disableButtonSortcuts: false,
// debug and testing options... // debug and testing options...
//keyboardReportUnhandled: false, //keyboardReportUnhandled: false,
}, },
@ -3254,6 +3299,10 @@ var BrowserPrototype = {
&& console.log('KEY:', ...arguments) }, && console.log('KEY:', ...arguments) },
this)) }, this)) },
// Proxy to .keyboard.parseStringHandler(..)
parseStringHandler: function(code, context){
return this.keyboard.parseStringHandler(code, context || this) },
// DOM props.. // DOM props..
// //
@ -3650,6 +3699,25 @@ var BrowserPrototype = {
return null return null
} }
// helpers...
var resolveValue = function(value, context, exec_context){
var htmlhandler = typeof(value) == typeof('str') ?
that.parseStringHandler(value, exec_context)
: null
return value instanceof Function ?
value.call(that, item)
: htmlhandler && htmlhandler.action in context ?
context[htmlhandler.action]
.call(that, item, ...htmlhandler.arguments)
: value }
var setDOMValue = function(target, value){
value instanceof HTMLElement ?
target.appendChild(value)
: (typeof(jQuery) != 'undefined' && value instanceof jQuery) ?
value.appendTo(target)
: (target.innerHTML = value)
return target }
// special-case: item shorthands... // special-case: item shorthands...
if(item.value in (options.elementShorthand || {})){ if(item.value in (options.elementShorthand || {})){
// XXX need to merge and not overwrite -- revise... // XXX need to merge and not overwrite -- revise...
@ -3699,24 +3767,26 @@ var BrowserPrototype = {
// values... // values...
text != null text != null
&& (item.value instanceof Array ? item.value : [item.value]) && (item.value instanceof Array ? item.value : [item.value])
// XXX handle $keys and other stuff... // handle $keys and other stuff...
.map(function(v){ .map(function(v){
// handle key-shortcuts $K... // handle key-shortcuts $K...
v = typeof(v) == typeof('str') ? v = typeof(v) == typeof('str') ?
v.replace(/\$\w/g, v.replace(/\$\w/g,
function(k){ function(k){
k = k[1] k = k[1]
return (item.keys || []) return (item.keys || [])
.includes(that.keyboard.normalizeKey(k)) ? .includes(that.keyboard.normalizeKey(k)) ?
`<u class="key-hint">${k}</u>` `<u class="key-hint">${k}</u>`
: k }) : k })
: v : v
var value = document.createElement('span') var value = document.createElement('span')
value.classList.add('text') value.classList.add('text')
value.innerHTML = v != null ?
v // set the value...
: (item || '') setDOMValue(value,
resolveValue(v, that))
elem.appendChild(value) elem.appendChild(value)
}) })
@ -3724,6 +3794,8 @@ var BrowserPrototype = {
elem.addEventListener('click', elem.addEventListener('click',
function(evt){ function(evt){
evt.stopPropagation() evt.stopPropagation()
// NOTE: if an item is disabled we retain its expand/collapse
// functionality...
// XXX revise... // XXX revise...
item.disabled ? item.disabled ?
that.toggleCollapse(item) that.toggleCollapse(item)
@ -3750,6 +3822,7 @@ var BrowserPrototype = {
&& elem.addEventListener(evt, handler.bind(that)) }) && elem.addEventListener(evt, handler.bind(that)) })
// buttons... // buttons...
var button_keys = {}
// XXX migrate the default buttons functionality and button inheritance... // XXX migrate the default buttons functionality and button inheritance...
var buttons = (item.buttons || options.itemButtons || []) var buttons = (item.buttons || options.itemButtons || [])
// resolve buttons from library... // resolve buttons from library...
@ -3760,28 +3833,43 @@ var BrowserPrototype = {
: Items.buttons[button] instanceof Function ? : Items.buttons[button] instanceof Function ?
Items.buttons[button].call(that, item) Items.buttons[button].call(that, item)
: Items.buttons[button] || button }) : Items.buttons[button] || button })
// NOTE: keep the order unsurprising... // NOTE: keep the order unsurprising -- first defined, first from left...
.reverse() .reverse()
var stopPropagation = function(evt){ evt.stopPropagation() } var stopPropagation = function(evt){ evt.stopPropagation() }
buttons buttons
// XXX add option to use a shortcut key...
// XXX use keyword to inherit buttons... // XXX use keyword to inherit buttons...
.forEach(function([html, handler, force]){ .forEach(function([html, handler, ...rest]){
var force = (rest[0] === true
|| rest[0] === false
|| rest[0] instanceof Function) ?
rest.shift()
: undefined
var metadata = rest.shift() || {}
// resolve metadata...
var cls = metadata.cls || []
cls = cls instanceof Function ?
cls.call(that, item)
: cls
cls = cls instanceof Array ?
cls
: cls.split(/\s+/g)
var alt = metadata.alt
alt = alt instanceof Function ?
alt.call(that, item)
: alt
var keys = metadata.keys
var button = document.createElement('div') var button = document.createElement('div')
button.classList.add('button') button.classList.add('button', ...cls)
alt
&& button.setAttribute('alt', alt)
var htmlhandler = typeof(html) == typeof('str') ? // button content...
that.keyboard.parseStringHandler(html, {item}) setDOMValue(button,
: null resolveValue(html, Items.buttons, {item}))
button.innerHTML = html instanceof Function ?
html.call(that, item)
// XXX reference the actual make(..) and not Items...
: htmlhandler && htmlhandler.action in Items.buttons ?
Items.buttons[htmlhandler.action]
.call(that, ...htmlhandler.arguments)
.call(that, item)
: html
// active button...
if(force instanceof Function ? if(force instanceof Function ?
force.call(that, item) force.call(that, item)
: (force || !item.disabled) ){ : (force || !item.disabled) ){
@ -3790,6 +3878,14 @@ var BrowserPrototype = {
;(options.buttonLocalEvents || options.localEvents || []) ;(options.buttonLocalEvents || options.localEvents || [])
.forEach(function(evt){ .forEach(function(evt){
button.addEventListener(evt, stopPropagation) }) button.addEventListener(evt, stopPropagation) })
// button keys...
keys && !options.disableButtonSortcuts
&& (keys instanceof Array ? keys : [keys])
.forEach(function(key){
// XXX should we break or warn???
if(key in button_keys){
throw new Error(`renderItem(..): button key already used: ${key}`) }
button_keys[keyboard.joinKey(keyboard.normalizeKey(key))] = button })
// keep focus on the item containing the button -- i.e. if // keep focus on the item containing the button -- i.e. if
// we tab out of the item focus the item we get to... // we tab out of the item focus the item we get to...
button.addEventListener('focus', function(){ button.addEventListener('focus', function(){
@ -3803,7 +3899,7 @@ var BrowserPrototype = {
handler handler
// string handler -> that.<handler>(item) // string handler -> that.<handler>(item)
: function(evt, ...args){ : function(evt, ...args){
var a = that.keyboard.parseStringHandler( var a = that.parseStringHandler(
handler, handler,
// button handler arg namespace... // button handler arg namespace...
{ {
@ -3828,9 +3924,20 @@ var BrowserPrototype = {
event.stopPropagation() event.stopPropagation()
func.call(that, evt, item) } }) } func.call(that, evt, item) } }) }
} }
elem.appendChild(button) elem.appendChild(button)
}) })
// button shortcut keys...
Object.keys(button_keys).length > 0
&& elem.addEventListener('keydown',
function(evt){
var k = keyboard.joinKey(keyboard.event2key(evt))
if(k in button_keys){
evt.preventDefault()
evt.stopPropagation()
button_keys[k].click() } })
item.dom = elem item.dom = elem
return elem return elem