iugu-docs-theme 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +50 -0
  4. data/_config.yml +35 -0
  5. data/_data/contentful/spaces/iugu.yaml +26 -0
  6. data/_data/method-colors.yml +5 -0
  7. data/_includes/alert.liquid +7 -0
  8. data/_includes/auth_data.liquid +37 -0
  9. data/_includes/dark_mode_button.liquid +31 -0
  10. data/_includes/footer.html +11 -0
  11. data/_includes/header.html +79 -0
  12. data/_includes/icons/auth.liquid +3 -0
  13. data/_includes/icons/danger.liquid +3 -0
  14. data/_includes/icons/description.liquid +3 -0
  15. data/_includes/icons/info.liquid +3 -0
  16. data/_includes/icons/money.liquid +3 -0
  17. data/_includes/icons/success.liquid +3 -0
  18. data/_includes/icons/warning.liquid +3 -0
  19. data/_includes/icons/wip.liquid +3 -0
  20. data/_includes/lixo.html +10 -0
  21. data/_includes/navigation_widget.liquid +14 -0
  22. data/_includes/openapi_ref.liquid +36 -0
  23. data/_includes/params/card.liquid +18 -0
  24. data/_includes/request/body_example.liquid +27 -0
  25. data/_includes/request/list.liquid +48 -0
  26. data/_includes/response/table.liquid +40 -0
  27. data/_includes/schema/nested_table.liquid +44 -0
  28. data/_includes/schema/table.liquid +36 -0
  29. data/_includes/search_modal.liquid +34 -0
  30. data/_includes/sidebar_menu.liquid +28 -0
  31. data/_includes/toc.html +174 -0
  32. data/_includes/toc_content.liquid +21 -0
  33. data/_includes/widget.liquid +17 -0
  34. data/_includes/widgets_base.liquid +5 -0
  35. data/_layouts/default.html +100 -0
  36. data/_layouts/page.html +5 -0
  37. data/_layouts/post.html +5 -0
  38. data/_plugins/authorization_parser.rb +51 -0
  39. data/_plugins/button_processor.rb +24 -0
  40. data/_plugins/check_required_filter.rb +48 -0
  41. data/_plugins/collapse_link_tag.rb +81 -0
  42. data/_plugins/converters/alert.rb +34 -0
  43. data/_plugins/converters/base_converter.rb +8 -0
  44. data/_plugins/converters/custom.rb +28 -0
  45. data/_plugins/converters/openapi_ref.rb +134 -0
  46. data/_plugins/converters/widget.rb +12 -0
  47. data/_plugins/doc_toc_filter.rb +27 -0
  48. data/_plugins/navigation_widget.rb +49 -0
  49. data/_plugins/response_filter.rb +28 -0
  50. data/_plugins/type_style_filter.rb +23 -0
  51. data/assets/css/application.css +5484 -0
  52. data/assets/css/highlight.css +217 -0
  53. data/assets/js/highlight_menu.js +19 -0
  54. data/assets/js/main.js +110 -0
  55. data/src/css/application.css +100 -0
  56. metadata +209 -0
@@ -0,0 +1,81 @@
1
+ module Jekyll
2
+ class CollapseLinkTagBlock < Liquid::Block
3
+ attr_reader :blocks
4
+
5
+ def initialize(tag_name, markup, options)
6
+ super
7
+ @blocks = []
8
+ @markup = markup
9
+ push_block(tag_name, @markup)
10
+ end
11
+
12
+ def render(context)
13
+ return empty_content(@blocks, context) if empty_content(@blocks, context)
14
+
15
+ result = ''
16
+ context.stack do
17
+ result << inject_collapse(@blocks, context)
18
+ end
19
+ result
20
+ end
21
+
22
+ def empty_content(blocks, context)
23
+ return unless blocks.last.render(context).strip.empty?
24
+
25
+ result = ''
26
+ context.stack do
27
+ blocks.each do |block|
28
+ result << block.render(context)
29
+ end
30
+ end
31
+ result
32
+ end
33
+
34
+ def inject_collapse(elements, context)
35
+ <<~HTML
36
+ <div class="cursor-pointer" x-data="{ isOpen: false }" @click="isOpen = !isOpen">
37
+ #{add_icon(elements.first, context, @markup)}
38
+ #{expandable_content(elements.last, context)}
39
+ </div>
40
+ HTML
41
+ end
42
+
43
+ def add_icon(element, context, markup)
44
+ arrow = "<i class='ml-2 arrow right' :class='{ down: isOpen, right: !isOpen }'></i>"
45
+ html_content = element.render(context)
46
+ return element.render(context) if html_content.include?('arrow')
47
+
48
+ tag_html = markup.gsub(/['"]/, '').strip
49
+ html_content.insert(html_content.index("</#{tag_html}>"), arrow) if html_content.include?("</#{tag_html}>")
50
+ end
51
+
52
+ def expandable_content(element, context)
53
+ <<~HTML
54
+ <div x-show="isOpen" x-collapse>
55
+ #{element.render(context)}
56
+ </div>
57
+ HTML
58
+ end
59
+
60
+ def parse(tokens)
61
+ while parse_body(@blocks.last, tokens)
62
+ end
63
+ end
64
+
65
+ def unknown_tag(tag, markup, tokens)
66
+ if tag == 'collapse_hidden_content'.freeze
67
+ push_block(tag, markup)
68
+ else
69
+ super
70
+ end
71
+ end
72
+
73
+ private
74
+
75
+ def push_block(tag, markup)
76
+ @blocks.push(Liquid::BlockBody.new)
77
+ end
78
+ end
79
+ end
80
+
81
+ Liquid::Template.register_tag('collapse_link', Jekyll::CollapseLinkTagBlock)
@@ -0,0 +1,34 @@
1
+ require_relative 'base_converter'
2
+
3
+ class Alert < BaseConverter
4
+ def alert
5
+ alert_type = @opts || 'description'
6
+ alert_file = File.read("_includes/alert.liquid")
7
+ context_hash = {'content' => @element.value, 'type' => alert_type}.merge(alert_classes(alert_type))
8
+ @context.merge(context_hash)
9
+ Liquid::Template.parse(alert_file).render!(@context)
10
+ end
11
+
12
+ private
13
+
14
+ def alert_classes(alert_type)
15
+ case alert_type
16
+ when 'success'
17
+ { "border_color" => "border-l-green-500", "text_color" => "text-green-500"}
18
+ when 'info'
19
+ { "border_color" => "border-l-blue-500", "text_color" => "text-blue-500"}
20
+ when 'warning'
21
+ { "border_color" => "border-l-amber-500", "text_color" => "text-amber-500"}
22
+ when 'danger'
23
+ { "border_color" => "border-l-red-500", "text_color" => "text-red-500"}
24
+ when 'money'
25
+ { "border_color" => "border-l-purple-500", "text_color" => "text-purple-500"}
26
+ when 'auth'
27
+ { "border_color" => "border-l-black dark:border-l-white", "text_color" => "dark:text-white text-black"}
28
+ when 'wip'
29
+ { "border_color" => "border-l-orange-500", "text_color" => "text-orange-500"}
30
+ else
31
+ { "border_color" => "border-l", "text_color" => "text-neutral-500"}
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,8 @@
1
+ class BaseConverter
2
+ def initialize(element, indent)
3
+ @lang, @opts = element.options[:lang]&.split('=')
4
+ @element = element
5
+ @site = Jekyll.sites.first
6
+ @context = Liquid::Context.new(@site.site_payload, {}, :site => @site)
7
+ end
8
+ end
@@ -0,0 +1,28 @@
1
+ require 'kramdown/converter'
2
+ require 'yaml'
3
+ require_relative 'alert'
4
+ require_relative 'openapi_ref'
5
+ require_relative 'widget'
6
+
7
+ module Kramdown #:nodoc:
8
+ module Converter #:nodoc:
9
+ module Custom
10
+ def convert_codeblock(element, indent)
11
+ lang, opts = element.options[:lang]&.split('=')
12
+ begin
13
+ klass = ::Module.const_get(lang.split('_').collect!{ |w| w.capitalize }.join)
14
+
15
+ if klass.new(element, indent).respond_to?(lang, true)
16
+ klass.new(element, indent).send(lang)
17
+ else
18
+ super(element, indent)
19
+ end
20
+ rescue
21
+ super(element, indent)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ Kramdown::Converter::Html.prepend(Kramdown::Converter::Custom)
@@ -0,0 +1,134 @@
1
+ require_relative 'base_converter'
2
+
3
+ class OpenapiRef < BaseConverter
4
+ OPERATIONS = %w[get post put delete options head path trace]
5
+ NORMALIZE_FIELDS = %w[summary description parameters servers]
6
+
7
+ def openapi_ref
8
+ setup_context
9
+ endpoints = @element.value.split("\n").each(&:strip!)
10
+
11
+ result = ''
12
+ endpoints.each do |endpoint_path|
13
+ return result += handle_schema if endpoint_path == 'schema'
14
+
15
+ endpoint, method = endpoint_path.split(":")
16
+ paths_object = if method
17
+ { method => @api['paths'][endpoint][method] }
18
+ else
19
+ @api['paths'][endpoint]
20
+ end
21
+
22
+ return "Endpoint missing: #{endpoint}" if paths_object.nil?
23
+
24
+ paths_object.each do |k, v|
25
+ v['color'] = method_color(k)
26
+ v['formatted'] = format_endpoint(endpoint)
27
+ if v.key?('requestBody') && v.dig('requestBody', 'content', 'application/json', 'schema')
28
+ example = body_builder(v['requestBody']['content']['application/json']['schema'])
29
+ v['body_example'] = JSON.pretty_generate(example)
30
+ end
31
+
32
+ auth_data = AuthorizationParser.parse(@api)
33
+ @context.merge('auth_data' => auth_data)
34
+ @context.merge('method' => k, 'content' => v)
35
+ result += Liquid::Template.parse(@html).render!(@context)
36
+ end
37
+ end
38
+
39
+ result
40
+ rescue StandardError => e
41
+ puts e
42
+ end
43
+
44
+ private
45
+
46
+ def setup_context
47
+ extension = File.extname(Dir["api.*"].first || "")
48
+ @api = case extension
49
+ when '.yaml'
50
+ YAML.load_file("api.yaml")
51
+ when '.json'
52
+ JSON.parse(File.read("api.json"))
53
+ else
54
+ {}
55
+ end
56
+
57
+ gem_theme_path = Gem.loaded_specs["iugu-docs-theme"].full_gem_path
58
+
59
+ @html = File.read("#{gem_theme_path}/_includes/openapi_ref.liquid")
60
+ @api = rebuild_refs(@api, 1)
61
+
62
+ normalize_paths
63
+ end
64
+
65
+ def rebuild_refs(object, nested)
66
+ if object.is_a?(Hash)
67
+ if object.key?('$ref')
68
+ object.merge!(read_path(object['$ref'], @api))
69
+ object.delete('$ref')
70
+ rebuild_refs(object, nested + 1)
71
+ else
72
+ object.each do |key, value|
73
+ rebuild_refs(value, nested + 1)
74
+ end
75
+ end
76
+ elsif object.is_a?(Array)
77
+ object.each do |item|
78
+ rebuild_refs(item, nested + 1)
79
+ end
80
+ end
81
+ end
82
+
83
+ def handle_schema
84
+ schema = @api['components']['schemas']
85
+ gem_theme_path = Gem.loaded_specs["iugu-docs-theme"].full_gem_path
86
+ schema_table = File.read("#{gem_theme_path}/_includes/schema/table.liquid")
87
+ @context.merge('schema' => schema)
88
+
89
+ Liquid::Template.parse(schema_table).render!(@context)
90
+ end
91
+
92
+ def normalize_paths
93
+ @api['paths'].each do |path, path_object|
94
+ path_object.each do |object, item|
95
+ next unless OPERATIONS.include?(object)
96
+
97
+ NORMALIZE_FIELDS.each do |field|
98
+ @api['paths'][path][object][field] = @api['paths'][path].delete(field) unless @api['paths'][path][field].nil?
99
+ end
100
+ end
101
+ end
102
+ end
103
+
104
+ def format_endpoint(endpoint)
105
+ "#{@api.dig('servers', 0, 'url')}#{endpoint.gsub("{", "<span class='text-orange-500'>{").gsub("}", "}</span>")}"
106
+ end
107
+
108
+ def body_builder(params)
109
+ case params["type"]
110
+ when "object"
111
+ object = {}
112
+ if params.key?("properties")
113
+ params["properties"].each do |key, value|
114
+ object[key] = body_builder(value)
115
+ end
116
+ end
117
+ object
118
+ when "array"
119
+ [body_builder(params["items"])]
120
+ else
121
+ params["example"] || ''
122
+ end
123
+ end
124
+
125
+ def read_path(path, json)
126
+ keys = path.split("/")
127
+ keys = keys.drop(1) if keys.first == "#"
128
+ json.dig(*keys)
129
+ end
130
+
131
+ def method_color(method)
132
+ Jekyll.sites.first.data["method-colors"][method]
133
+ end
134
+ end
@@ -0,0 +1,12 @@
1
+ require_relative 'base_converter'
2
+
3
+ class Widget < BaseConverter
4
+ def widget
5
+ widget_file = File.read("_includes/widgets_base.liquid")
6
+ apps = YAML.load_file("apps.yaml")
7
+ context_hash = { 'content' => apps }
8
+ @context.merge(context_hash)
9
+
10
+ Liquid::Template.parse(widget_file).render!(@context)
11
+ end
12
+ end
@@ -0,0 +1,27 @@
1
+ module Jekyll
2
+ module DocTocFilter
3
+ def toc_yaml(input)
4
+ return nil unless input
5
+
6
+ gem_theme_path = Gem.loaded_specs["iugu-docs-theme"].full_gem_path
7
+
8
+ relative_dir = File.expand_path(File.dirname(File.dirname(__FILE__) + "../"))
9
+ base = File.basename(relative_dir)
10
+ begin
11
+ toc_yaml = YAML.load_file("toc.yaml")
12
+ return nil unless toc_yaml
13
+
14
+ toc_content = File.read("#{gem_theme_path}/_includes/toc_content.liquid")
15
+ nav_h = toc_yaml.map { |it| it.merge("base" => base) }
16
+ @context.merge('nav_h' => nav_h)
17
+
18
+
19
+ Liquid::Template.parse(toc_content).render!(@context)
20
+ rescue StandardError => e
21
+ nil
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ Liquid::Template.register_filter(Jekyll::DocTocFilter)
@@ -0,0 +1,49 @@
1
+ module Jekyll
2
+ module NavigationWidgetFilter
3
+ def navigation(input)
4
+ return nil unless input
5
+
6
+ gem_theme_path = Gem.loaded_specs["iugu-docs-theme"].full_gem_path
7
+
8
+ begin
9
+ toc_yaml = YAML.load_file("toc.yaml")
10
+ return nil unless toc_yaml
11
+
12
+ docs = toc_yaml.map { |key| key["children"]}
13
+ return nil if docs.nil?
14
+
15
+ index = nil
16
+ docs = docs.flatten
17
+ docs.each_with_index do |doc, i|
18
+ next unless normalize_path(doc["url"]) == normalize_path(input)
19
+
20
+ index = i
21
+ break
22
+ end
23
+ return nil if index.nil?
24
+
25
+ previous_doc = docs[index - 1] unless index == 0
26
+ next_doc = docs[index + 1] unless index == docs.length - 1
27
+
28
+ navigation = File.read("#{gem_theme_path}/_includes/navigation_widget.liquid")
29
+
30
+ @site = Jekyll.sites.first
31
+ @context = Liquid::Context.new(@site.site_payload, {}, :site => @site)
32
+ @context.merge({ 'previous_doc' => previous_doc, 'next_doc' => next_doc })
33
+
34
+ Liquid::Template.parse(navigation).render(@context)
35
+ rescue StandardError => e
36
+ puts "Error: #{e}"
37
+ nil
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def normalize_path(path)
44
+ path.gsub(/\/|\\/,"").gsub(".html","").gsub(".md","")
45
+ end
46
+ end
47
+ end
48
+
49
+ Liquid::Template.register_filter(Jekyll::NavigationWidgetFilter)
@@ -0,0 +1,28 @@
1
+ module Jekyll
2
+ module ResponseFilter
3
+ def response_status(type)
4
+ status_color = case type.to_i
5
+ when 100..199
6
+ "fill-blue-500"
7
+ when 200..299
8
+ "fill-green-500"
9
+ when 300..399
10
+ "fill-yellow-500"
11
+ when 400..499
12
+ "fill-orange-500"
13
+ when 500..599
14
+ "fill-red-500"
15
+ end
16
+ <<-HTML
17
+ <span class='flex gap-x-2 items-center font-semibold dark:text-gray-200'>
18
+ <svg class='h-1.5 w-1.5 #{status_color}' viewBox='0 0 6 6' aria-hidden='true'>
19
+ <circle cx='3' cy='3' r='3' />
20
+ </svg>
21
+ #{type}
22
+ </span>
23
+ HTML
24
+ end
25
+ end
26
+ end
27
+
28
+ Liquid::Template.register_filter(Jekyll::ResponseFilter)
@@ -0,0 +1,23 @@
1
+ module Jekyll
2
+ module TypeStyleFilter
3
+ def type_style(type)
4
+
5
+ case type
6
+ when 'string'
7
+ "bg-orange-50 text-orange-700 ring-orange-300 dark:bg-orange-950 dark:text-orange-300"
8
+ when 'boolean'
9
+ "bg-green-50 text-green-700 ring-green-300 dark:bg-green-950 dark:text-green-300"
10
+ when 'array'
11
+ "bg-neutral-50 text-neutral-700 ring-neutral-300 dark:bg-neutral-950 dark:text-neutral-300"
12
+ when 'integer'
13
+ "bg-blue-50 text-blue-700 ring-blue-300 dark:bg-blue-950 dark:text-blue-300"
14
+ when 'object'
15
+ "bg-neutral-50 text-neutral-700 ring-neutral-300 dark:bg-neutral-950 dark:text-neutral-300"
16
+ else
17
+ "bg-gray-50 text-gray-700 ring-gray-300 dark:bg-gray-950 dark:text-gray-300"
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ Liquid::Template.register_filter(Jekyll::TypeStyleFilter)