diff --git a/ui (gen4)/lib/widget/browse2.html b/ui (gen4)/lib/widget/browse2.html index 5c1b8eac..cedf3e9a 100755 --- a/ui (gen4)/lib/widget/browse2.html +++ b/ui (gen4)/lib/widget/browse2.html @@ -171,109 +171,118 @@ requirejs([ // XXX split this into several dialogues, show each and then combine... - dialog = browser.Browser(function(make){ - // XXX put this in the header... - // ways to do this: - // - a different handler - // .__header__(..) is like .__list__(..) but can be - // called less often... - // - a group like thing/constructor... - // make.Header( - // make(..), - // .. - // ) - // - both??? - make('path', { - id:'selected_path', - //cls: 'path sticky', - cls: 'path', - }) - - make.Separator() - - make(['list', 'of', 'text']) - make.group( - make('$group item 0 (open event)', - function(){ console.log('##', ...arguments) }), - 'group item 1 (bare)') - // XXX Q: should we show only one if multiple lines are in sequence??? - make('---') - // embeded browser... - make(browser.Browser(function(make){ - make('inlined browser item 0 (open/menu events)', { - open: function(){ console.log('!!! OPEN', ...arguments) }, - menu: function(){ console.log('!!! MENU', ...arguments) }, + dialog = browser.Browser( + function(make, options){ + make('CURRENT_PATH', { + id: 'current_path', + cls: 'path', + buttons: (options || {}).headerButtons + || (this.options || {}).headerButtons + || [], + }) }, + function(make){ + // XXX put this in the header... + // ways to do this: + // - a different handler + // .__header__(..) is like .__list__(..) but can be + // called less often... + // - a group like thing/constructor... + // make.Header( + // make(..), + // .. + // ) + // - both??? + make('path', { + id:'selected_path', + //cls: 'path sticky', + cls: 'path', }) - make(1) - make(2) - })) - // basic nested list... - make.nest('$nested', [ - make('mo$o', {disabled: true}), - 2, - // XXX this is not supported by .map(..)... - make.nest('$ne$sted', browser.Browser(function(make){ - make('ab') - //make('', - //make($('')[0], - make($(''), - { - alt: 'image item example' - }) - })), - ]) - make('in between two $subtrees...') - // nested browser... - make.nest('B', - browser.Browser(function(make){ - make('xx') - make.nest('C', browser.Browser(function(make){ - make('aaa') - make.nest('D', browser.Browser(function(make){ - make('a') - make('---') - make('b') + + make.Separator() + + make(['list', 'of', 'text']) + make.group( + make('$group item 0 (open event)', + function(){ console.log('##', ...arguments) }), + 'group item 1 (bare)') + // XXX Q: should we show only one if multiple lines are in sequence??? + make('---') + // embeded browser... + make(browser.Browser(function(make){ + make('inlined browser item 0 (open/menu events)', { + open: function(){ console.log('!!! OPEN', ...arguments) }, + menu: function(){ console.log('!!! MENU', ...arguments) }, + }) + make(1) + make(2) + })) + // basic nested list... + make.nest('$nested', [ + make('mo$o', {disabled: true}), + 2, + // XXX this is not supported by .map(..)... + make.nest('$ne$sted', browser.Browser(function(make){ + make('ab') + //make('', + //make($('')[0], + make($(''), + { + alt: 'image item example' + }) + })), + ]) + make('in between two $subtrees...') + // nested browser... + make.nest('B', + browser.Browser(function(make){ + make('xx') + make.nest('C', browser.Browser(function(make){ + make('aaa') + make.nest('D', browser.Browser(function(make){ + make('a') + make('---') + make('b') + })) + make('bbb', {buttons: [ + ['test', 'focus: "parent"'], + ]}) + make('bbb') })) - make('bbb', {buttons: [ - ['test', 'focus: "parent"'], - ]}) - make('bbb') - })) - }), { - //collapsed: true, - // XXX this does not appear to survive attaching an item - // to the document... - open: function(e){ - e.preventDefault() - console.log('test: open: default prevented...') }, - //cls: ['heading'], - }) + }), { + //collapsed: true, + // XXX this does not appear to survive attaching an item + // to the document... + open: function(e){ + e.preventDefault() + console.log('test: open: default prevented...') }, + //cls: ['heading'], + }) - make.Separator() + make.Separator() - make.nest( - 'scroll testing', - (new Array(100)) - .fill(1) - .map(function(_, i){ - return make(i) })) + make.nest( + 'scroll testing', + (new Array(100)) + .fill(1) + .map(function(_, i){ + return make(i) })) - }, { - itemButtons: [ - ['–', - 'buttonAction: item button focused -- example button action...'], - ['□', - function(){ console.log('BUTTON:', ...arguments) }, - { keys: 'r' }], - 'ToggleCollapse', - 'ToggleDisabled', - ], - }) - // XXX EXPEREMENT -- need to wrap this into partial .update(..) - .focus(function(){ - var e = this.get({id: 'selected_path'}) - e.value = this.pathArray - this.renderItem(e) }) + }, { + itemButtons: [ + ['–', + 'buttonAction: item button focused -- example button action...'], + ['□', + function(){ console.log('BUTTON:', ...arguments) }, + { keys: 'r' }], + 'ToggleCollapse', + 'ToggleDisabled', + ], + }) + // XXX EXPEREMENT -- need to wrap this into partial .update(..) + .focus(function(){ + var e = this.get({id: 'selected_path'}) + e.value = this.pathArray + this.renderItem(e) }) dialog.container = $('.container').first()[0] diff --git a/ui (gen4)/lib/widget/browse2.js b/ui (gen4)/lib/widget/browse2.js index f39d4471..bfbf0686 100755 --- a/ui (gen4)/lib/widget/browse2.js +++ b/ui (gen4)/lib/widget/browse2.js @@ -787,13 +787,20 @@ var makeItemEventToggler2 = function(get_state, set_state, unset_state, default_ var BaseBrowserClassPrototype = { } -// XXX Q: should we be able to add/remove/change items outside of .__list__(..)??? +// XXX Q: should we be able to add/remove/change items outside of .__items__(..)??? // ...only some item updates (how .collapsed is handled) make // sense at this time -- need to think about this more // carefully + strictly document the result... var BaseBrowserPrototype = { // XXX should we mix item/list options or separate them into sub-objects??? options: { + sections: [ + 'header', + 'items', + // XXX do we need this? + //'footer', + ], + focusDisabled: false, // If true item keys must be unique... @@ -838,6 +845,7 @@ var BaseBrowserPrototype = { // NOTE: this may or may not be a Browser object. parent: null, + // Section containers... // // Format: // [ @@ -855,13 +863,12 @@ var BaseBrowserPrototype = { // NOTE: this can't be a map/dict as we need both order manipulation // and nested structures which would overcomplicate things, as // a compromise we use .index below for item identification. - // XXX should the header be restricted to being separate??? __header: null, get header(){ this.__header - // XXX should this be .makeHeader(..) ??? - || this.make() - return this.__header }, + || (this.__header__ + && this.make({section: 'header'})) + return this.__header || [] }, set header(value){ this.__header = value }, __items: null, @@ -871,6 +878,14 @@ var BaseBrowserPrototype = { return this.__items }, set items(value){ this.__items = value }, + __footer: null, + get footer(){ + this.__footer + || (this.__footer__ + && this.make({section: 'footer'})) + return this.__footer || [] }, + set footer(value){ + this.__footer = value }, // Clear cached data... @@ -1059,9 +1074,11 @@ var BaseBrowserPrototype = { __item__: BaseItem, - // Item list constructor... + // Section item list constructor... // - // .__list__(make, options) + // .__header__(make, options) + // .__items__(make, options) + // .__footer__(make, options) // -> undefined // -> list // @@ -1085,14 +1102,14 @@ var BaseBrowserPrototype = { // When calling make(..) (mode #1) the item is built by combining // the following in order: // - original item (.items[key]) if present, - // - options passed to .make() method calling .__list__(..), + // - options passed to .make() method calling .__items__(..), // - options passed to make(.., ) constructing the item, // - {value: } where passed to make(, ..) // // Each of the above will override values of the previous sections. // // The resulting item is stored in: - // .items + // .header, .items or .footer // .index (keyed via .id or JSONified .value) // // Each of the above structures is reset on each call to .make(..) @@ -1126,19 +1143,19 @@ var BaseBrowserPrototype = { // // // NOTE: this is not designed to be called directly... - __list__: function(make, options){ - throw new Error('.__list__(..): Not implemented.') }, - - // XXX header constructor, same as .__list__(..) but optional... __header__: null, + __items__: function(make, options){ + throw new Error('.__items__(..): Not implemented.') }, + __footer__: null, + // Make extension... // - // This is called per item created by make(..) in .__list__(..) + // This is called per item created by make(..) in .__items__(..) // // NOTE: this can update/modify the item but it can not replace it. - //__make__: function(item){ + //__make__: function(section, item){ //}, @@ -1148,7 +1165,7 @@ var BaseBrowserPrototype = { // .make(options) // -> this // - // The items are constructed by passing a make function to .__list__(..) + // The items are constructed by passing a make function to .__items__(..) // which in turn will call this make(..) per item created. // // For more doc on item construction see: .__init__(..) @@ -1159,7 +1176,7 @@ var BaseBrowserPrototype = { // identify an item thus no state is maintained between .make(..) // calls for such items... // - // XXX revise options handling for .__list__(..) + // XXX revise options handling for .__items__(..) // XXX might be a good idea to enable the user to merge the state // manually... // one way to do: @@ -1167,19 +1184,30 @@ var BaseBrowserPrototype = { // - update it // - pass it to make(..) // Example: - // // just a rough example in .__list__(..)... + // // just a rough example in .__items__(..)... // make(value, // value in this.index ? // Object.assign( // this.index[value], // opts) // : opts) + // XXX revise if stage 2 is applicable to sections other than .items make: function(options){ var that = this options = Object.assign( Object.create(this.options || {}), options || {}) + // sections to make... + var sections = options.section == '*' ? + (options.sections || ['header', 'items', 'footer']) + : (options.section || 'items') + sections = (sections instanceof Array ? + sections + : [sections]) + // keep only sections we know how to make... + .filter(function(name){ + return that[`__${name}__`] instanceof Function }) // item constructor... // @@ -1203,9 +1231,10 @@ var BaseBrowserPrototype = { // while the latter stores a list of items. // ...would be more logical to store the object (i.e. browser/list) // directly as the element... + var section var make_called = false - var list = [] var ids = new Set() + var list = [] var keys = options.uniqueKeys ? new Set() : null @@ -1278,8 +1307,8 @@ var BaseBrowserPrototype = { // user extended make... // XXX differentiate this for header and list... - //this.__make__ - // && this.__make__(item) + this.__make__ + && this.__make__(section, item) // store the item... list.push(item) @@ -1290,22 +1319,17 @@ var BaseBrowserPrototype = { make.__proto__ = Items make.dialog = this - // build the lists... - //* XXX - ;[...(this.__header__ ? - [['__header__', 'header']] - : []), - ['__list__', 'items']] - .forEach(function([method, name]){ + // build the sections... + sections + .forEach(function(name){ // setup closure for make(..)... + section = name make_called = false ids = new Set() list = make.items = that[name] = [] // build list... - // XXX check that this is enough... - // ...do we index header items??? - var res = that[method](make, + var res = that[`__${name}__`](make, // XXX not sure about this -- revise options handling... options ? Object.assign( @@ -1313,50 +1337,36 @@ var BaseBrowserPrototype = { options || {}) : null) - // if make was not called use the .__list__(..) return value... + // if make was not called use the .__items__(..) return value... that[name] = make_called ? that[name] : res }) - /*/ - // setup closure for make(..)... - list = make.items = this.items = [] - // XXX not sure about this -- revise options handling... - var res = this.__list__(make, - options ? - Object.assign( - Object.create(this.options || {}), - options || {}) - : null) - // if make was not called use the .__list__(..) return value... - this.items = make_called ? - this.items - : res - //*/ - // reset the index/cache... - var old_index = this.__item_index_cache || {} - this.clearCache() + // XXX should this be only for .items??? + // ...should this be global (all items?) + if(sections.includes('items')){ + var old_index = this.__item_index_cache || {} + this.clearCache() - // 2'nd pass -> make item index (unique id's)... - // NOTE: we are doing this in a separate pass as items can get - // rearranged during the make phase (Items.nest(..) ...), - // thus avoiding odd duplicate index numbering... - // XXX should we also index header??? - var index = this.__item_index_cache = this.index + // 2'nd pass -> make item index (unique id's)... + // NOTE: we are doing this in a separate pass as items can get + // rearranged during the make phase (Items.nest(..) ...), + // thus avoiding odd duplicate index numbering... + var index = this.__item_index_cache = this.index - // post process the items... - Object.entries(index) - .forEach(function([id, e]){ - // update item.id of items with duplicate keys... - !id.endsWith(that.__key__(e)) - && (e.id = id.split(/[\/]/g).pop()) - // merge old item state... - id in old_index - // XXX this is not very elegant(???), revise... - && Object.assign(e, - old_index[id], - e) }) + // post process the items... + Object.entries(index) + .forEach(function([id, e]){ + // update item.id of items with duplicate keys... + !id.endsWith(that.__key__(e)) + && (e.id = id.split(/[\/]/g).pop()) + // merge old item state... + id in old_index + // XXX this is not very elegant(???), revise... + && Object.assign(e, + old_index[id], + e) }) } return this }, @@ -1539,7 +1549,16 @@ var BaseBrowserPrototype = { context.index = context.index || 0 // options specifics... - var source = options.source || 'items' + // NOTE: we include header/footer only for the root context... + var sections = context.root === this ? + 'items' + : options.section == '*' ? + (options.sections || 'items') + : (options.section || 'items') + sections = sections instanceof Array ? + sections + : [sections] + var iterateNonIterable = options.iterateAll || options.iterateNonIterable var iterateCollapsed = options.iterateAll || options.iterateCollapsed var skipNested = !options.iterateAll && options.skipNested @@ -1710,11 +1729,14 @@ var BaseBrowserPrototype = { }, [], // input items... - ...(reverse ? - this[source] - .slice() - .reverse() - : this[source])) }, + ...(sections + .map(function(name){ + return that[name] || [] }) + .flat() + .run(function(){ + return reverse ? + this.reverse() + : this }))) }, // Test/Example Text renders... @@ -3083,10 +3105,10 @@ var BaseBrowserPrototype = { // XXX should we update on init.... __init__: function(func, options){ var args = [...arguments] - this.__list__ = args.shift() + this.__items__ = args.shift() if(args[0] instanceof Function){ - this.__header__ = this.__list__ - this.__list__ = args.shift() + this.__header__ = this.__items__ + this.__items__ = args.shift() } this.options = Object.assign( Object.create(this.options || {}), @@ -3555,18 +3577,6 @@ var HTMLBrowserPrototype = { this.__dom = value }, - // XXX header... - // XXX might be a good idea to make this expandable -- show/hide options... - __header__: function(make, options){ - make('CURRENT_PATH', { - id: 'current_path', - cls: 'path', - buttons: (options || {}).headerButtons - || (this.options || {}).headerButtons - || [], - }) }, - - // Extending query... // // Extended .search(..) to support: