iugu-docs-theme 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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)