iugu-docs-theme 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +50 -0
- data/_config.yml +35 -0
- data/_data/contentful/spaces/iugu.yaml +26 -0
- data/_data/method-colors.yml +5 -0
- data/_includes/alert.liquid +7 -0
- data/_includes/auth_data.liquid +37 -0
- data/_includes/dark_mode_button.liquid +31 -0
- data/_includes/footer.html +11 -0
- data/_includes/header.html +79 -0
- data/_includes/icons/auth.liquid +3 -0
- data/_includes/icons/danger.liquid +3 -0
- data/_includes/icons/description.liquid +3 -0
- data/_includes/icons/info.liquid +3 -0
- data/_includes/icons/money.liquid +3 -0
- data/_includes/icons/success.liquid +3 -0
- data/_includes/icons/warning.liquid +3 -0
- data/_includes/icons/wip.liquid +3 -0
- data/_includes/lixo.html +10 -0
- data/_includes/navigation_widget.liquid +14 -0
- data/_includes/openapi_ref.liquid +36 -0
- data/_includes/params/card.liquid +18 -0
- data/_includes/request/body_example.liquid +27 -0
- data/_includes/request/list.liquid +48 -0
- data/_includes/response/table.liquid +40 -0
- data/_includes/schema/nested_table.liquid +44 -0
- data/_includes/schema/table.liquid +36 -0
- data/_includes/search_modal.liquid +34 -0
- data/_includes/sidebar_menu.liquid +28 -0
- data/_includes/toc.html +174 -0
- data/_includes/toc_content.liquid +21 -0
- data/_includes/widget.liquid +17 -0
- data/_includes/widgets_base.liquid +5 -0
- data/_layouts/default.html +100 -0
- data/_layouts/page.html +5 -0
- data/_layouts/post.html +5 -0
- data/_plugins/authorization_parser.rb +51 -0
- data/_plugins/button_processor.rb +24 -0
- data/_plugins/check_required_filter.rb +48 -0
- data/_plugins/collapse_link_tag.rb +81 -0
- data/_plugins/converters/alert.rb +34 -0
- data/_plugins/converters/base_converter.rb +8 -0
- data/_plugins/converters/custom.rb +28 -0
- data/_plugins/converters/openapi_ref.rb +134 -0
- data/_plugins/converters/widget.rb +12 -0
- data/_plugins/doc_toc_filter.rb +27 -0
- data/_plugins/navigation_widget.rb +49 -0
- data/_plugins/response_filter.rb +28 -0
- data/_plugins/type_style_filter.rb +23 -0
- data/assets/css/application.css +5484 -0
- data/assets/css/highlight.css +217 -0
- data/assets/js/highlight_menu.js +19 -0
- data/assets/js/main.js +110 -0
- data/src/css/application.css +100 -0
- 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,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)
|