Module: Jekyll::VitePressTheme::Sidebar
- Defined in:
- lib/jekyll/vitepress_theme/hooks.rb
Overview
rubocop:disable Metrics/ModuleLength
Constant Summary collapse
- DATA_KEY =
'jekyll_vitepress_sidebar'.freeze
- MAX_ITEM_LEVEL =
5
Class Method Summary collapse
- .ancestor_titles(doc, docs) ⇒ Object
- .apply(site) ⇒ Object
- .attach_nodes(collection_name, docs, nodes) ⇒ Object
- .attaching_creates_cycle?(doc, parent_doc, docs) ⇒ Boolean
- .build_collection(collection_name, docs) ⇒ Object
- .collection_data(group) ⇒ Object
- .collection_docs(site, collection_name) ⇒ Object
- .data_value(doc, key) ⇒ Object
- .doc_url(doc) ⇒ Object
- .finalize_children(node, level) ⇒ Object
- .finalize_nodes(nodes, level) ⇒ Object
- .flatten_docs(nodes) ⇒ Object
- .generate(site) ⇒ Object
- .generated_group(site, group) ⇒ Object
- .group_hash(group) ⇒ Object
- .group_value(group, key) ⇒ Object
- .node_for(doc) ⇒ Object
- .normalized_string(value) ⇒ Object
- .numeric?(value) ⇒ Boolean
- .parent_doc_for(doc, docs) ⇒ Object
- .parent_title(doc) ⇒ Object
- .sort_docs(docs) ⇒ Object
- .sort_key(doc) ⇒ Object
- .title(doc) ⇒ Object
- .truthy?(value) ⇒ Boolean
- .valid_parent?(doc, parent_doc, docs) ⇒ Boolean
- .warn_depth_limit(node) ⇒ Object
- .warn_missing_parent(collection_name, doc) ⇒ Object
Class Method Details
.ancestor_titles(doc, docs) ⇒ Object
151 152 153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/jekyll/vitepress_theme/hooks.rb', line 151 def ancestor_titles(doc, docs) titles = [] current = parent_doc_for(doc, docs) seen = [] while current && !seen.include?(current) seen << current titles << title(current) current = parent_doc_for(current, docs) end titles end |
.apply(site) ⇒ Object
13 14 15 16 17 18 |
# File 'lib/jekyll/vitepress_theme/hooks.rb', line 13 def apply(site) = generate(site) site.data[DATA_KEY] = if rescue StandardError => e Jekyll.logger.warn('jekyll-vitepress-theme', "Sidebar hierarchy generation failed: #{e.}") end |
.attach_nodes(collection_name, docs, nodes) ⇒ Object
62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/jekyll/vitepress_theme/hooks.rb', line 62 def attach_nodes(collection_name, docs, nodes) docs.each_with_object([]) do |doc, roots| parent_doc = parent_doc_for(doc, docs) if valid_parent?(doc, parent_doc, docs) nodes[parent_doc]['children'] << nodes[doc] else warn_missing_parent(collection_name, doc) if parent_title(doc) roots << nodes[doc] end end end |
.attaching_creates_cycle?(doc, parent_doc, docs) ⇒ Boolean
137 138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/jekyll/vitepress_theme/hooks.rb', line 137 def attaching_creates_cycle?(doc, parent_doc, docs) current = parent_doc seen = [] while current return true if current == doc || seen.include?(current) seen << current current = parent_doc_for(current, docs) end false end |
.build_collection(collection_name, docs) ⇒ Object
47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
# File 'lib/jekyll/vitepress_theme/hooks.rb', line 47 def build_collection(collection_name, docs) ordered_docs = sort_docs(docs) nodes = ordered_docs.to_h { |doc| [doc, node_for(doc)] } roots = attach_nodes(collection_name, ordered_docs, nodes) finalize_nodes(roots, 1) flat_docs = flatten_docs(roots) { 'collection' => collection_name.to_s, 'items' => roots, 'docs' => flat_docs, 'active_urls' => flat_docs.filter_map { |doc| doc_url(doc) } } end |
.collection_data(group) ⇒ Object
43 44 45 |
# File 'lib/jekyll/vitepress_theme/hooks.rb', line 43 def collection_data(group) group.slice('collection', 'items', 'docs', 'active_urls') end |
.collection_docs(site, collection_name) ⇒ Object
78 79 80 81 82 83 84 85 |
# File 'lib/jekyll/vitepress_theme/hooks.rb', line 78 def collection_docs(site, collection_name) return [] unless collection_name collection = site.collections[collection_name.to_s] return [] unless collection collection.docs end |
.data_value(doc, key) ⇒ Object
189 190 191 192 |
# File 'lib/jekyll/vitepress_theme/hooks.rb', line 189 def data_value(doc, key) data = doc.respond_to?(:data) ? doc.data : {} data[key] || data[key.to_sym] end |
.doc_url(doc) ⇒ Object
202 203 204 |
# File 'lib/jekyll/vitepress_theme/hooks.rb', line 202 def doc_url(doc) doc.url if doc.respond_to?(:url) end |
.finalize_children(node, level) ⇒ Object
174 175 176 177 178 179 180 181 |
# File 'lib/jekyll/vitepress_theme/hooks.rb', line 174 def finalize_children(node, level) if level >= MAX_ITEM_LEVEL warn_depth_limit(node) unless node['children'].empty? node['children'] = [] else finalize_nodes(node['children'], level + 1) end end |
.finalize_nodes(nodes, level) ⇒ Object
165 166 167 168 169 170 171 172 |
# File 'lib/jekyll/vitepress_theme/hooks.rb', line 165 def finalize_nodes(nodes, level) nodes.sort_by! { |node| sort_key(node['doc']) } nodes.each do |node| finalize_children(node, level) node['active_urls'] = [node['url'], *node['children'].flat_map { |child| child['active_urls'] }].compact end end |
.flatten_docs(nodes) ⇒ Object
183 184 185 186 187 |
# File 'lib/jekyll/vitepress_theme/hooks.rb', line 183 def flatten_docs(nodes) nodes.flat_map do |node| [node['doc'], *flatten_docs(node['children'])] end end |
.generate(site) ⇒ Object
20 21 22 23 24 25 26 27 28 29 30 |
# File 'lib/jekyll/vitepress_theme/hooks.rb', line 20 def generate(site) = site.data['sidebar'] return nil unless .respond_to?(:each) groups = .filter_map { |group| generated_group(site, group) } { 'groups' => groups, 'collections' => groups.to_h { |group| [group['collection'], collection_data(group)] } } end |
.generated_group(site, group) ⇒ Object
32 33 34 35 36 37 38 39 40 41 |
# File 'lib/jekyll/vitepress_theme/hooks.rb', line 32 def generated_group(site, group) collection_name = group_value(group, 'collection') docs = collection_docs(site, collection_name) return nil if docs.empty? collection_data = build_collection(collection_name, docs) return nil if collection_data['docs'].empty? group_hash(group).merge(collection_data) end |
.group_hash(group) ⇒ Object
87 88 89 90 91 |
# File 'lib/jekyll/vitepress_theme/hooks.rb', line 87 def group_hash(group) return group if group.is_a?(Hash) group.respond_to?(:to_h) ? group.to_h : {} end |
.group_value(group, key) ⇒ Object
93 94 95 |
# File 'lib/jekyll/vitepress_theme/hooks.rb', line 93 def group_value(group, key) group_hash(group)[key] || group_hash(group)[key.to_sym] end |
.node_for(doc) ⇒ Object
115 116 117 118 119 120 121 122 123 124 |
# File 'lib/jekyll/vitepress_theme/hooks.rb', line 115 def node_for(doc) { 'doc' => doc, 'title' => title(doc), 'url' => doc_url(doc), 'collapsed' => truthy?(data_value(doc, 'collapsed')), 'children' => [], 'active_urls' => [] } end |
.normalized_string(value) ⇒ Object
206 207 208 209 |
# File 'lib/jekyll/vitepress_theme/hooks.rb', line 206 def normalized_string(value) string = value.to_s.strip string.empty? ? nil : string end |
.numeric?(value) ⇒ Boolean
111 112 113 |
# File 'lib/jekyll/vitepress_theme/hooks.rb', line 111 def numeric?(value) value.is_a?(Numeric) || value.to_s.match?(/\A-?\d+(?:\.\d+)?\z/) end |
.parent_doc_for(doc, docs) ⇒ Object
126 127 128 129 130 131 132 133 134 135 |
# File 'lib/jekyll/vitepress_theme/hooks.rb', line 126 def parent_doc_for(doc, docs) direct_parent = parent_title(doc) return nil unless direct_parent candidates = docs.select { |candidate| candidate != doc && title(candidate) == direct_parent } grand_parent = normalized_string(data_value(doc, 'grand_parent')) return candidates.first unless grand_parent candidates.find { |candidate| ancestor_titles(candidate, docs).include?(grand_parent) } end |
.parent_title(doc) ⇒ Object
198 199 200 |
# File 'lib/jekyll/vitepress_theme/hooks.rb', line 198 def parent_title(doc) normalized_string(data_value(doc, 'parent')) end |
.sort_docs(docs) ⇒ Object
97 98 99 |
# File 'lib/jekyll/vitepress_theme/hooks.rb', line 97 def sort_docs(docs) docs.sort_by { |doc| sort_key(doc) } end |
.sort_key(doc) ⇒ Object
101 102 103 104 105 106 107 108 109 |
# File 'lib/jekyll/vitepress_theme/hooks.rb', line 101 def sort_key(doc) nav_order = data_value(doc, 'nav_order') order_bucket = nav_order.nil? ? 1 : 0 numeric_order = numeric?(nav_order) order_type = numeric_order ? 0 : 1 order_value = numeric_order ? nav_order.to_f : nav_order.to_s [order_bucket, order_type, order_value, title(doc).downcase, doc_url(doc).to_s] end |
.title(doc) ⇒ Object
194 195 196 |
# File 'lib/jekyll/vitepress_theme/hooks.rb', line 194 def title(doc) normalized_string(data_value(doc, 'title')) || '' end |
.truthy?(value) ⇒ Boolean
211 212 213 |
# File 'lib/jekyll/vitepress_theme/hooks.rb', line 211 def truthy?(value) value == true || value.to_s.casecmp('true').zero? end |
.valid_parent?(doc, parent_doc, docs) ⇒ Boolean
74 75 76 |
# File 'lib/jekyll/vitepress_theme/hooks.rb', line 74 def valid_parent?(doc, parent_doc, docs) parent_doc && !attaching_creates_cycle?(doc, parent_doc, docs) end |
.warn_depth_limit(node) ⇒ Object
222 223 224 225 226 227 |
# File 'lib/jekyll/vitepress_theme/hooks.rb', line 222 def warn_depth_limit(node) Jekyll.logger.warn( 'jekyll-vitepress-theme', "Sidebar item '#{node['title']}' is deeper than #{MAX_ITEM_LEVEL} item levels; nested children were not rendered." ) end |
.warn_missing_parent(collection_name, doc) ⇒ Object
215 216 217 218 219 220 |
# File 'lib/jekyll/vitepress_theme/hooks.rb', line 215 def warn_missing_parent(collection_name, doc) Jekyll.logger.warn( 'jekyll-vitepress-theme', "Missing sidebar parent '#{parent_title(doc)}' for '#{title(doc)}' in #{collection_name}; rendering it at the collection root." ) end |