From 6e86106dd268d278ab5809e15890fb39fc8be92e Mon Sep 17 00:00:00 2001 From: Rene Saarsoo Date: Mon, 10 Sep 2012 15:01:48 +0300 Subject: [PATCH] Support nested grouping of assets. Nested groups can be defined as follows: [{ "title": "Main group", "items": [ {"title: "subgroup", "items: [ ... ]} ] }] In UI the tree will then show subfolders for the nested asset groups. But the ThumbList display will still show all the assets in old way, and the assets in subgroups will be expanded so they will appear as if they were listed directly inside the top level group. I guess in some way this beats the whole point of having the subgroups - as all the assets still show up on the main page as if there were no subgroups at all. On the other hand we now technically have support for subgroups, but I feel that's not really what these users were after when they asked for nested grouping. Additionally eliminated :dup_asset warning. It didn't fit nicely to the new way of iterating over assets, and I think it wasn't such a useful warning anyway. --- lib/jsduck/examples.rb | 2 +- lib/jsduck/grouped_asset.rb | 43 ++++++++++++++++++++------------- lib/jsduck/guides.rb | 30 ++++++++++++----------- lib/jsduck/logger.rb | 1 - lib/jsduck/videos.rb | 2 +- template/app/view/GroupTree.js | 29 +++++++++++++--------- template/app/view/ThumbList.js | 44 +++++++++++++++++++++++++++++++++- 7 files changed, 106 insertions(+), 45 deletions(-) diff --git a/lib/jsduck/examples.rb b/lib/jsduck/examples.rb index 30380d9b..257e9b49 100644 --- a/lib/jsduck/examples.rb +++ b/lib/jsduck/examples.rb @@ -20,7 +20,7 @@ module JsDuck @groups = Util::Json.read(filename) @opts = opts fix_examples_data - build_map_by_name("Two examples have the same name", filename) + build_map_by_name end # Prefix all relative URL-s in examples list with path given in --examples-base-url diff --git a/lib/jsduck/grouped_asset.rb b/lib/jsduck/grouped_asset.rb index 53ddc200..6b6b2f55 100644 --- a/lib/jsduck/grouped_asset.rb +++ b/lib/jsduck/grouped_asset.rb @@ -10,20 +10,10 @@ module JsDuck class GroupedAsset # Should be called from constructor after @groups have been read in, # and after it's been ensured that all items in groupes have names. - # - # Prints warning when there is a duplicate item within a group. - # The warning message should say something like "duplicate " - def build_map_by_name(warning_msg, filename) + def build_map_by_name @map_by_name = {} - @groups.each do |group| - group_map = {} - group["items"].each do |item| - if group_map[item["name"]] - Logger.instance.warn(:dup_asset, "#{warning_msg} '#{item['name']}'", filename) - end - @map_by_name[item["name"]] = item - group_map[item["name"]] = item - end + each_item do |item| + @map_by_name[item["name"]] = item end end @@ -33,9 +23,30 @@ module JsDuck end # Iterates over all items in all groups - def each_item - @groups.each do |group| - group["items"].each {|item| yield item } + def each_item(group=nil, &block) + group = group || @groups + + group.each do |item| + if item["items"] + each_item(item["items"], &block) + else + block.call(item) + end + end + end + + def map_items(group=nil, &block) + group = group || @groups + + group.map do |item| + if item["items"] + { + "title" => item["title"], + "items" => map_items(item["items"], &block) + } + else + block.call(item) + end end end diff --git a/lib/jsduck/guides.rb b/lib/jsduck/guides.rb index c9edb985..2c5a527d 100644 --- a/lib/jsduck/guides.rb +++ b/lib/jsduck/guides.rb @@ -24,9 +24,10 @@ module JsDuck def initialize(filename, formatter, opts) @path = File.dirname(filename) @groups = Util::Json.read(filename) - build_map_by_name("Two guides have the same name", filename) @formatter = formatter @opts = opts + build_map_by_name + load_all_guides end # Writes all guides to given dir in JsonP format @@ -35,23 +36,16 @@ module JsDuck each_item {|guide| write_guide(guide, dir) } end - # Modified each_item that also loads HTML for each guide - def each_item - super do |guide| - # Load the guide if not loaded - guide[:html] = load_guide(guide) if guide[:html] == nil - # Pass guide to block if it was successfully loaded. - yield guide if guide[:html] + def load_all_guides + each_item do |guide| + guide[:html] = load_guide(guide) end end # Modified to_array that excludes the :html from guide nodes def to_array - @groups.map do |group| - { - "title" => group["title"], - "items" => group["items"].map {|g| Hash[g.select {|k, v| k != :html }] } - } + map_items do |item| + Hash[item.select {|k, v| k != :html }] end end @@ -141,7 +135,7 @@ module JsDuck [ "

#{group['title']}

", "", ] end.flatten.join("\n") @@ -153,6 +147,14 @@ module JsDuck EOHTML end + def flatten_subgroups(items) + result = [] + each_item(items) do |item| + result << item + end + result + end + # Extracts guide icon URL from guide hash def icon_url(guide) "guides/" + guide["name"] + "/icon.png" diff --git a/lib/jsduck/logger.rb b/lib/jsduck/logger.rb index a34ef2f4..5be95f02 100644 --- a/lib/jsduck/logger.rb +++ b/lib/jsduck/logger.rb @@ -25,7 +25,6 @@ module JsDuck [:no_doc, "Member or class without documentation"], [:dup_param, "Method has two parameters with the same name"], [:dup_member, "Class has two members with the same name"], - [:dup_asset, "Duplicate guide/video/example"], [:req_after_opt, "Required parameter comes after optional"], [:subproperty, "@param foo.bar where foo param doesn't exist"], [:sing_static, "Singleton class member marked as @static"], diff --git a/lib/jsduck/videos.rb b/lib/jsduck/videos.rb index fa28a799..3ec91606 100644 --- a/lib/jsduck/videos.rb +++ b/lib/jsduck/videos.rb @@ -18,7 +18,7 @@ module JsDuck def initialize(filename) @groups = Util::Json.read(filename) add_names_if_missing - build_map_by_name("Two videos have the same name", filename) + build_map_by_name end # Each video should have a name, which is used in URL to reference the video. diff --git a/template/app/view/GroupTree.js b/template/app/view/GroupTree.js index bf4cebad..ca2bc5a5 100644 --- a/template/app/view/GroupTree.js +++ b/template/app/view/GroupTree.js @@ -25,19 +25,26 @@ Ext.define('Docs.view.GroupTree', { initComponent: function() { this.root = { - children: [], - text: 'Root' + text: 'Root', + children: this.buildTree(this.data) }; - Ext.Array.each(this.data, function(group) { - this.root.children.push({ - text: group.title, - expanded: true, - children: Ext.Array.map(group.items, this.convert), - iconCls: 'icon-pkg' - }); - }, this); - this.callParent(); + }, + + buildTree: function(items) { + return Ext.Array.map(items, function(item) { + if (item.items) { + return { + text: item.title, + expanded: true, + iconCls: 'icon-pkg', + children: this.buildTree(item.items) + }; + } + else { + return this.convert(item); + } + }, this); } }); diff --git a/template/app/view/ThumbList.js b/template/app/view/ThumbList.js index 2fee6d83..a0da045a 100644 --- a/template/app/view/ThumbList.js +++ b/template/app/view/ThumbList.js @@ -47,13 +47,14 @@ Ext.define('Docs.view.ThumbList', { fields: ['id', 'title', 'items'] }); + // Can't just pass the #data config to store as in Ext 4.1 the // value of data config gets modified - each array element // gets replaced with a Model record. // // But we don't want to modify the items in the global // Docs.data... - this.store.loadData(this.data); + this.store.loadData(this.flattenSubgroups(this.data)); // Place itemTpl inside main template this.tpl = new Ext.XTemplate(Ext.Array.flatten([ @@ -90,6 +91,47 @@ Ext.define('Docs.view.ThumbList', { this.callParent(arguments); }, + // Given groups data with subgroups like this: + // + // - group A + // - subgroup AA + // - item 1 + // - item 2 + // - subgroup AB + // - item 3 + // - item 4 + // - group B + // - item 5 + // + // Eliminates the subgroups so we're left with: + // + // - group A + // - item 1 + // - item 2 + // - item 3 + // - item 4 + // - group B + // - item 5 + // + flattenSubgroups: function(data) { + function expand(item) { + if (item.items) { + return Ext.Array.map(item.items, expand); + } + else { + return item; + } + } + + return Ext.Array.map(data, function(group) { + return { + id: group.id, + title: group.title, + items: Ext.Array.flatten(Ext.Array.map(group.items, expand)) + }; + }); + }, + onContainerClick: function(e) { var group = e.getTarget('h2', 3, true); -- GitLab