lookbook 1.2.1 → 1.4.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (182) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +47 -14
  4. data/app/assets/lookbook/css/themes/blue.css +4 -2
  5. data/app/assets/lookbook/css/themes/green.css +66 -0
  6. data/app/assets/lookbook/css/themes/indigo.css +4 -2
  7. data/app/assets/lookbook/css/themes/rose.css +66 -0
  8. data/app/assets/lookbook/css/themes/zinc.css +4 -2
  9. data/app/components/lookbook/base_component.rb +2 -2
  10. data/app/components/lookbook/code/component.css +2 -2
  11. data/app/components/lookbook/code/component.html.erb +3 -2
  12. data/app/components/lookbook/code/component.rb +13 -2
  13. data/app/components/lookbook/code/highlight_github.css +406 -0
  14. data/app/components/lookbook/header/component.html.erb +1 -1
  15. data/app/components/lookbook/inspector_panel/component.rb +4 -6
  16. data/app/components/lookbook/nav/component.rb +8 -15
  17. data/app/components/lookbook/nav/directory/component.html.erb +28 -0
  18. data/app/components/lookbook/nav/directory/component.rb +4 -0
  19. data/app/components/lookbook/nav/{item → entity}/component.html.erb +8 -8
  20. data/app/components/lookbook/nav/entity/component.rb +49 -0
  21. data/app/components/lookbook/nav/item/component.css +15 -0
  22. data/app/components/lookbook/nav/item/component.js +4 -0
  23. data/app/components/lookbook/nav/item/component.rb +13 -56
  24. data/app/components/lookbook/params/editor/component.html.erb +2 -2
  25. data/app/components/lookbook/params/editor/component.rb +3 -10
  26. data/app/components/lookbook/params/field/component.css +3 -3
  27. data/app/components/lookbook/params/field/component.html.erb +8 -8
  28. data/app/components/lookbook/params/field/component.rb +21 -72
  29. data/app/components/lookbook/split_layout/component.html.erb +1 -1
  30. data/app/components/lookbook/tabs/component.html.erb +1 -1
  31. data/app/components/lookbook/tabs/component.js +4 -0
  32. data/app/components/lookbook/tag_component.rb +1 -1
  33. data/app/components/lookbook/viewport/component.css +1 -1
  34. data/app/components/lookbook/viewport/component.html.erb +1 -1
  35. data/app/components/lookbook/viewport/component.rb +1 -1
  36. data/app/controllers/concerns/lookbook/targetable_concern.rb +131 -0
  37. data/app/controllers/concerns/lookbook/with_preview_controller_concern.rb +13 -0
  38. data/app/controllers/lookbook/application_controller.rb +21 -9
  39. data/app/controllers/lookbook/inspector_controller.rb +45 -0
  40. data/app/controllers/lookbook/page_controller.rb +13 -9
  41. data/app/controllers/lookbook/pages_controller.rb +9 -15
  42. data/app/controllers/lookbook/previews_controller.rb +4 -210
  43. data/app/helpers/lookbook/application_helper.rb +2 -2
  44. data/app/helpers/lookbook/output_helper.rb +5 -5
  45. data/app/helpers/lookbook/page_helper.rb +7 -4
  46. data/app/views/layouts/lookbook/application.html.erb +40 -38
  47. data/app/views/layouts/lookbook/page.html.erb +2 -2
  48. data/app/views/layouts/lookbook/shell.html.erb +3 -2
  49. data/app/views/layouts/lookbook/skeleton.html.erb +7 -7
  50. data/app/views/lookbook/index.html.erb +13 -2
  51. data/app/views/lookbook/{previews → inspector}/inputs/_color.html.erb +0 -0
  52. data/app/views/lookbook/{previews → inspector}/inputs/_range.html.erb +0 -0
  53. data/app/views/lookbook/{previews → inspector}/inputs/_select.html.erb +0 -0
  54. data/app/views/lookbook/{previews → inspector}/inputs/_text.html.erb +0 -0
  55. data/app/views/lookbook/{previews → inspector}/inputs/_textarea.html.erb +0 -0
  56. data/app/views/lookbook/{previews → inspector}/inputs/_toggle.html.erb +5 -5
  57. data/app/views/lookbook/{previews → inspector}/panels/_content.html.erb +0 -0
  58. data/app/views/lookbook/{previews → inspector}/panels/_notes.html.erb +2 -2
  59. data/app/views/lookbook/{previews → inspector}/panels/_output.html.erb +0 -0
  60. data/app/views/lookbook/inspector/panels/_params.html.erb +15 -0
  61. data/app/views/lookbook/{previews → inspector}/panels/_preview.html.erb +0 -0
  62. data/app/views/lookbook/{previews → inspector}/panels/_source.html.erb +0 -0
  63. data/app/views/lookbook/{previews → inspector}/show.html.erb +5 -2
  64. data/config/app.yml +11 -1
  65. data/config/inputs.yml +12 -12
  66. data/config/languages.yml +41 -0
  67. data/config/panels.yml +6 -6
  68. data/config/routes.rb +5 -5
  69. data/config/tags.yml +8 -1
  70. data/lib/lookbook/engine.rb +103 -130
  71. data/lib/lookbook/entities/collections/component_collection.rb +4 -0
  72. data/lib/lookbook/entities/collections/concerns/hierarchical_collection.rb +23 -0
  73. data/lib/lookbook/entities/collections/entity_collection.rb +61 -0
  74. data/lib/lookbook/entities/collections/page_collection.rb +30 -0
  75. data/lib/lookbook/entities/collections/preview_collection.rb +41 -0
  76. data/lib/lookbook/entities/collections/preview_example_collection.rb +4 -0
  77. data/lib/lookbook/entities/component.rb +31 -0
  78. data/lib/lookbook/entities/concerns/annotatable.rb +58 -0
  79. data/lib/lookbook/entities/concerns/inspectable.rb +44 -0
  80. data/lib/lookbook/entities/concerns/locatable.rb +73 -0
  81. data/lib/lookbook/entities/concerns/navigable.rb +43 -0
  82. data/lib/lookbook/entities/entity.rb +53 -0
  83. data/lib/lookbook/entities/page.rb +80 -0
  84. data/lib/lookbook/entities/page_section.rb +43 -0
  85. data/lib/lookbook/entities/preview.rb +87 -0
  86. data/lib/lookbook/entities/preview_example.rb +100 -0
  87. data/lib/lookbook/entities/preview_group.rb +48 -0
  88. data/lib/lookbook/file_watcher.rb +47 -0
  89. data/lib/lookbook/lang.rb +12 -35
  90. data/lib/lookbook/param.rb +99 -0
  91. data/lib/lookbook/{preview_controller.rb → preview_actions.rb} +14 -3
  92. data/lib/lookbook/preview_parser.rb +53 -0
  93. data/lib/lookbook/process.rb +21 -0
  94. data/lib/lookbook/rendered_example.rb +37 -0
  95. data/lib/lookbook/services/code/code_beautifier.rb +21 -0
  96. data/lib/lookbook/services/code/code_highlighter.rb +69 -0
  97. data/lib/lookbook/services/code/code_indenter.rb +14 -0
  98. data/lib/lookbook/services/data/parsers/data_parser.rb +22 -0
  99. data/lib/lookbook/services/data/parsers/json_parser.rb +7 -0
  100. data/lib/lookbook/services/data/parsers/yaml_parser.rb +7 -0
  101. data/lib/lookbook/services/data/resolvers/data_resolver.rb +70 -0
  102. data/lib/lookbook/services/data/resolvers/eval_resolver.rb +10 -0
  103. data/lib/lookbook/services/data/resolvers/file_resolver.rb +28 -0
  104. data/lib/lookbook/services/data/resolvers/method_resolver.rb +10 -0
  105. data/lib/lookbook/services/data/resolvers/yaml_resolver.rb +18 -0
  106. data/lib/lookbook/services/entities/entity_tree_builder.rb +45 -0
  107. data/lib/lookbook/services/markdown_renderer.rb +29 -0
  108. data/lib/lookbook/services/position_prefix_parser.rb +16 -0
  109. data/lib/lookbook/services/string_value_caster.rb +60 -0
  110. data/lib/lookbook/services/tags/tag_options_parser.rb +62 -0
  111. data/lib/lookbook/services/templates/action_view_annotations_handler.rb +21 -0
  112. data/lib/lookbook/services/templates/action_view_annotations_stripper.rb +15 -0
  113. data/lib/lookbook/services/templates/frontmatter_extractor.rb +28 -0
  114. data/lib/lookbook/services/templates/styles_extractor.rb +38 -0
  115. data/lib/lookbook/services/{search_param_builder.rb → urls/search_param_builder.rb} +1 -1
  116. data/lib/lookbook/services/{search_param_parser.rb → urls/search_param_parser.rb} +1 -1
  117. data/lib/lookbook/stores/config_store.rb +12 -9
  118. data/lib/lookbook/stores/input_store.rb +7 -3
  119. data/lib/lookbook/stores/panel_store.rb +2 -2
  120. data/lib/lookbook/stores/tag_store.rb +3 -5
  121. data/lib/lookbook/support/null_object.rb +10 -0
  122. data/lib/lookbook/support/service.rb +2 -2
  123. data/lib/lookbook/support/store.rb +2 -35
  124. data/lib/lookbook/support/tree_node.rb +87 -0
  125. data/lib/lookbook/support/utils/path_utils.rb +32 -5
  126. data/lib/lookbook/support/utils/utils.rb +24 -0
  127. data/lib/lookbook/tags/component_tag.rb +13 -0
  128. data/lib/lookbook/tags/custom_tag.rb +61 -0
  129. data/lib/lookbook/tags/display_tag.rb +15 -0
  130. data/lib/lookbook/tags/hidden_tag.rb +13 -0
  131. data/lib/lookbook/tags/id_tag.rb +7 -0
  132. data/lib/lookbook/tags/label_tag.rb +4 -0
  133. data/lib/lookbook/tags/logical_path_tag.rb +7 -0
  134. data/lib/lookbook/tags/param_tag.rb +63 -0
  135. data/lib/lookbook/tags/position_tag.rb +16 -0
  136. data/lib/lookbook/tags/source_tag.rb +7 -0
  137. data/lib/lookbook/tags/tag_provider.rb +18 -0
  138. data/lib/lookbook/tags/yard_tag.rb +90 -0
  139. data/lib/lookbook/theme.rb +8 -0
  140. data/lib/lookbook/version.rb +1 -1
  141. data/lib/lookbook/websocket.rb +60 -0
  142. data/lib/lookbook.rb +13 -8
  143. data/public/lookbook-assets/css/lookbook.css +487 -411
  144. data/public/lookbook-assets/css/lookbook.css.map +1 -1
  145. data/public/lookbook-assets/css/themes/blue.css +3 -1
  146. data/public/lookbook-assets/css/themes/blue.css.map +1 -1
  147. data/public/lookbook-assets/css/themes/green.css +68 -0
  148. data/public/lookbook-assets/css/themes/green.css.map +1 -0
  149. data/public/lookbook-assets/css/themes/indigo.css +3 -1
  150. data/public/lookbook-assets/css/themes/indigo.css.map +1 -1
  151. data/public/lookbook-assets/css/themes/rose.css +68 -0
  152. data/public/lookbook-assets/css/themes/rose.css.map +1 -0
  153. data/public/lookbook-assets/css/themes/zinc.css +3 -1
  154. data/public/lookbook-assets/css/themes/zinc.css.map +1 -1
  155. data/public/lookbook-assets/js/embed.js +10 -1
  156. data/public/lookbook-assets/js/embed.js.map +1 -1
  157. data/public/lookbook-assets/js/lookbook.js +358 -629
  158. data/public/lookbook-assets/js/lookbook.js.map +1 -1
  159. metadata +96 -44
  160. data/app/components/lookbook/code/highlight_github_light.css +0 -217
  161. data/app/views/lookbook/previews/panels/_params.html.erb +0 -15
  162. data/lib/lookbook/code_formatter.rb +0 -68
  163. data/lib/lookbook/collection.rb +0 -161
  164. data/lib/lookbook/component.rb +0 -34
  165. data/lib/lookbook/entity.rb +0 -47
  166. data/lib/lookbook/markdown.rb +0 -22
  167. data/lib/lookbook/page.rb +0 -195
  168. data/lib/lookbook/page_collection.rb +0 -19
  169. data/lib/lookbook/page_section.rb +0 -29
  170. data/lib/lookbook/params.rb +0 -157
  171. data/lib/lookbook/parser.rb +0 -42
  172. data/lib/lookbook/preview.rb +0 -174
  173. data/lib/lookbook/preview_collection.rb +0 -23
  174. data/lib/lookbook/preview_example.rb +0 -93
  175. data/lib/lookbook/preview_group.rb +0 -62
  176. data/lib/lookbook/source_inspector.rb +0 -95
  177. data/lib/lookbook/support/utils/attribute_utils.rb +0 -9
  178. data/lib/lookbook/tag.rb +0 -122
  179. data/lib/lookbook/tag_options.rb +0 -111
  180. data/lib/lookbook/tags.rb +0 -17
  181. data/lib/lookbook/template_parser.rb +0 -72
  182. data/lib/lookbook/utils.rb +0 -105
@@ -0,0 +1,41 @@
1
+ module Lookbook
2
+ class PreviewCollection < EntityCollection
3
+ include HierarchicalCollection
4
+
5
+ def find_example_by_path(lookup_path)
6
+ examples.find_by_path(lookup_path)
7
+ end
8
+
9
+ def find_by_preview_class(klass)
10
+ find { |preview| preview.preview_class.name == klass.to_s }
11
+ end
12
+
13
+ def load(code_objects)
14
+ @entities = []
15
+ clear_cache
16
+
17
+ previews = Array(code_objects).map { |obj| PreviewCollection.preview_from_code_object(obj) }.compact
18
+ add(previews)
19
+ end
20
+
21
+ def self.preview_from_code_object(code_object)
22
+ klass = code_object.path.constantize
23
+ Preview.new(code_object) if preview_class?(klass)
24
+ rescue => exception
25
+ Lookbook.logger.error exception.to_s
26
+ nil
27
+ end
28
+
29
+ def self.preview_class?(klass)
30
+ if klass.ancestors.include?(ViewComponent::Preview)
31
+ !klass.respond_to?(:abstract_class) || klass.abstract_class != true
32
+ end
33
+ end
34
+
35
+ protected
36
+
37
+ def examples
38
+ @_cache[:examples] ||= PreviewExampleCollection.new(flat_map(&:examples))
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,4 @@
1
+ module Lookbook
2
+ class PreviewExampleCollection < EntityCollection
3
+ end
4
+ end
@@ -0,0 +1,31 @@
1
+ module Lookbook
2
+ class Component < Entity
3
+ include Locatable
4
+
5
+ attr_reader :component_class
6
+
7
+ def initialize(component_class)
8
+ @component_class = component_class
9
+ @file_path = "#{Engine.component_paths.first}/#{name.underscore}.rb"
10
+ @base_directories = Engine.component_paths
11
+ @lookup_path = PathUtils.to_lookup_path(relative_file_path)
12
+ end
13
+
14
+ def name
15
+ component_class.name
16
+ end
17
+
18
+ def template_file_path
19
+ Dir.glob("#{directory_path}/#{file_name(true)}.*.erb").first
20
+ end
21
+
22
+ def inline?
23
+ template_file_path.present?
24
+ end
25
+
26
+ def template_path
27
+ Lookbook.logger.warn "The `template_path` method has been deprecated - use `template_file_path` instead. `template_file_path` will be removed in v2.0"
28
+ template_file_path
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,58 @@
1
+ module Lookbook
2
+ module Annotatable
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ delegate :has_tag?, to: :code_object
7
+
8
+ def notes
9
+ code_object.docstring.to_s.strip
10
+ end
11
+
12
+ def tags(name = nil)
13
+ code_object.tags(name)
14
+ end
15
+
16
+ def tag(name = nil)
17
+ tags(name).first
18
+ end
19
+
20
+ protected
21
+
22
+ attr_reader :code_object
23
+
24
+ def fetch_config(key, fallback = nil, &block)
25
+ value = case key.to_sym
26
+ when :components
27
+ components_config
28
+ when :display_options
29
+ display_options_config
30
+ else
31
+ tag(key).value if has_tag?(key)
32
+ end
33
+
34
+ Utils.value_or_fallback(value, fallback, &block)
35
+ end
36
+
37
+ private
38
+
39
+ def components_config
40
+ return unless has_tag?(:component)
41
+
42
+ Array(tags(:component)).map(&:value).compact
43
+ end
44
+
45
+ def display_options_config
46
+ return unless has_tag?(:display)
47
+
48
+ # Dynamic params set at the entity level are
49
+ # not (yet?) supported so filter them out.
50
+ display_tags = tags(:display).select do |tag|
51
+ !tag.value.is_a?(Hash) && !tag.value.is_a?(Array)
52
+ end
53
+
54
+ display_tags.map { |tag| [tag.key.to_sym, tag.value] }.to_h
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,44 @@
1
+ module Lookbook
2
+ module Inspectable
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ def source
7
+ source_code = if custom_source?
8
+ File.read(source_file_path)
9
+ else
10
+ src = CodeIndenter.call(code_object.source)
11
+ begin
12
+ send(:format_source, src)
13
+ rescue NoMethodError
14
+ src
15
+ end
16
+ end
17
+
18
+ source_code.strip_heredoc.strip
19
+ end
20
+
21
+ def source_lang
22
+ custom_source? ? Lang.guess(source_file_path, :ruby) : Lang.find(:ruby)
23
+ end
24
+
25
+ def custom_source?
26
+ source_file_path.present?
27
+ end
28
+
29
+ protected
30
+
31
+ attr_reader :code_object
32
+
33
+ def source_file_path
34
+ @_source_path ||= if code_object.has_tag?(:source)
35
+ source_path = code_object.tag(:source).value
36
+ unless source_path.present? && File.exist?(source_path)
37
+ raise LookbookError, "Could not find source file '#{source_path}'"
38
+ end
39
+ source_path
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,73 @@
1
+ module Lookbook
2
+ module Locatable
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ attr_reader :file_path, :base_directories
7
+
8
+ def file_name(strip_ext = false)
9
+ basename = file_pathname.basename
10
+ (strip_ext ? basename.to_s.split(".").first : basename).to_s
11
+ end
12
+
13
+ def file_name_base
14
+ @_file_name_slug ||= file_name(true).gsub(/(_component_preview|component_preview|preview)$/, "")
15
+ end
16
+
17
+ def file_extension
18
+ @_file_extension ||= file_pathname.extname
19
+ end
20
+
21
+ def directory_path
22
+ @_directory_path ||= Pathname(file_pathname.dirname)
23
+ end
24
+
25
+ def relative_file_path
26
+ @_relative_file_path ||= file_pathname.relative_path_from(base_directory)
27
+ end
28
+
29
+ def relative_directory_path
30
+ @_relative_directory_path ||= directory_path.relative_path_from(base_directory)
31
+ end
32
+
33
+ def last_modified
34
+ @_last_modified ||= File.mtime(file_path)
35
+ end
36
+
37
+ def logical_path
38
+ return @_logical_path if @_logical_path
39
+
40
+ directory = fetch_config(:logical_path) { relative_directory_path.to_s }
41
+ @_logical_path ||= PathUtils.to_path(directory, file_name_base)
42
+ end
43
+
44
+ def full_path
45
+ Lookbook.logger.warn "The `full_path` method has been deprecated - use `file_path` instead. `full_path` will be removed in v2.0"
46
+ file_path
47
+ end
48
+
49
+ def rel_path
50
+ Lookbook.logger.warn "The `rel_path` method has been deprecated - use `relative_file_path` instead. `rel_path` will be removed in v2.0"
51
+ relative_file_path
52
+ end
53
+
54
+ def dir_path
55
+ Lookbook.logger.warn "The `dir_path` method has been deprecated - use `directory_path` instead. `dir_path` will be removed in v2.0"
56
+ directory_path
57
+ end
58
+
59
+ protected
60
+
61
+ def file_pathname
62
+ Pathname(file_path)
63
+ end
64
+
65
+ def base_directory
66
+ return @_base_directory if @_base_directory
67
+
68
+ directories = Array(base_directories).map(&:to_s).sort_by { |path| path.split("/").size }.reverse
69
+ @_base_directory ||= directories.find { |dir| file_path.to_s.start_with?(dir) }
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,43 @@
1
+ module Lookbook
2
+ module Navigable
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ def hidden?
7
+ fetch_config(:hidden, false)
8
+ end
9
+
10
+ def visible?
11
+ !hidden?
12
+ end
13
+
14
+ def position
15
+ return @_position if @_position
16
+
17
+ pos = if @position_prefixes && respond_to?(:file_name)
18
+ PositionPrefixParser.call(file_name).first || default_position
19
+ else
20
+ fetch_config(:position, default_position)
21
+ end
22
+
23
+ @_position ||= pos.to_i
24
+ end
25
+
26
+ def depth
27
+ path.split("/").size
28
+ end
29
+
30
+ def default_position
31
+ @default_position || 10000
32
+ end
33
+
34
+ def <=>(other)
35
+ if respond_to?(:sort_handler, true)
36
+ sort_handler(other)
37
+ else
38
+ [position, label] <=> [other.position, other.label]
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,53 @@
1
+ module Lookbook
2
+ class Entity
3
+ include Comparable
4
+ include Lookbook::Engine.routes.url_helpers
5
+
6
+ def initialize(lookup_path = nil)
7
+ @lookup_path = lookup_path
8
+ end
9
+
10
+ def id
11
+ Utils.id(fetch_config(:id) { lookup_path.tr("/", "-") })
12
+ end
13
+
14
+ def name
15
+ @_name ||= Utils.name(File.basename(@lookup_path))
16
+ end
17
+
18
+ def label
19
+ @_label ||= fetch_config(:label) { name.titleize }
20
+ end
21
+
22
+ def lookup_path
23
+ return @_lookup_path if @_lookup_path
24
+
25
+ directory = fetch_config(:logical_path) do
26
+ dir = File.dirname(@lookup_path)
27
+ dir if !dir.start_with?(".")
28
+ end
29
+ @_lookup_path ||= PathUtils.strip_slashes(PathUtils.to_path(directory, name))
30
+ end
31
+
32
+ def url_path
33
+ nil
34
+ end
35
+
36
+ def type
37
+ @_type ||= self.class.name.demodulize.underscore.downcase.to_sym
38
+ end
39
+
40
+ def <=>(other)
41
+ label <=> other.label
42
+ end
43
+
44
+ alias_method :path, :lookup_path
45
+ alias_method :logical_path, :lookup_path
46
+
47
+ protected
48
+
49
+ def fetch_config(key, fallback = nil, &block)
50
+ Utils.value_or_fallback(nil, fallback, &block)
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,80 @@
1
+ module Lookbook
2
+ class Page < Entity
3
+ include Locatable
4
+ include Navigable
5
+
6
+ attr_reader :content, :sections
7
+
8
+ def initialize(file_path)
9
+ @file_path = Pathname(file_path)
10
+ @base_directories = Engine.page_paths
11
+ @lookup_path = PathUtils.to_lookup_path(relative_file_path)
12
+ @frontmatter, @content = FrontmatterExtractor.call(file_contents)
13
+ @position_prefixes = true
14
+ @sections = []
15
+ end
16
+
17
+ def title
18
+ @_title ||= fetch_config(:title, label)
19
+ end
20
+
21
+ def landing?
22
+ @_landing ||= fetch_config(:landing, false)
23
+ end
24
+
25
+ def markdown?
26
+ @_markdown ||= fetch_config(:markdown) { file_path.to_s.match?(/(.*)\.md\.(.*)$/) }
27
+ end
28
+
29
+ def header?
30
+ @_header ||= fetch_config(:header, true)
31
+ end
32
+
33
+ def footer?
34
+ @_footer ||= fetch_config(:footer, true)
35
+ end
36
+
37
+ def data
38
+ return @_data if @_data
39
+
40
+ config_data = fetch_config(:data, {})
41
+ @_data ||= config_data.is_a?(Hash) ? Store.new(config_data) : config_data
42
+ end
43
+
44
+ def search_terms
45
+ label
46
+ end
47
+
48
+ def url_path
49
+ lookbook_page_path(path)
50
+ end
51
+
52
+ def add_section(section)
53
+ @sections << section
54
+ @sections.sort_by! { |section| [section.position, section.label] }
55
+ end
56
+
57
+ def method_missing(method_name, *args, &block)
58
+ method_name.to_s.end_with?("=") ? super : frontmatter.fetch(method_name, nil)
59
+ end
60
+
61
+ def respond_to_missing?(method_name, include_private = false)
62
+ frontmatter.key?(method_name) || super
63
+ end
64
+
65
+ protected
66
+
67
+ def fetch_config(key, fallback = nil, &block)
68
+ value = frontmatter[key]
69
+ Utils.value_or_fallback(value, fallback, &block)
70
+ end
71
+
72
+ def file_contents
73
+ @_file_contents ||= File.read(file_path)
74
+ end
75
+
76
+ def frontmatter
77
+ @_merged_frontmatter ||= Lookbook.config.page_options.deep_merge(@frontmatter.to_h).deep_symbolize_keys
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,43 @@
1
+ module Lookbook
2
+ class PageSection < Page
3
+ attr_accessor :parent
4
+
5
+ def initialize(file_path)
6
+ @file_path = Pathname(file_path)
7
+ @parent = nil
8
+ super
9
+ end
10
+
11
+ def name
12
+ Utils.name(name_parts[:name])
13
+ end
14
+
15
+ def lookup_path
16
+ directory = if relative_directory_path.present? && !relative_directory_path.to_s.start_with?(".")
17
+ relative_directory_path
18
+ end
19
+
20
+ path = PathUtils.to_path(directory, name_parts[:parent_name])
21
+ PathUtils.to_lookup_path(path)
22
+ end
23
+
24
+ def landing?
25
+ false
26
+ end
27
+
28
+ def url_path
29
+ nil
30
+ end
31
+
32
+ alias_method :page, :parent
33
+
34
+ protected
35
+
36
+ def name_parts
37
+ return @_name_parts if @_name_parts
38
+
39
+ matches = file_name(true).match(/(?<parent_name>.*)\[(?<name>.*)\]/)
40
+ @_name_parts ||= {name: matches[:name], parent_name: matches[:parent_name]}
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,87 @@
1
+ module Lookbook
2
+ class Preview < Entity
3
+ include Annotatable
4
+ include Locatable
5
+ include Navigable
6
+
7
+ delegate :render_args, to: :preview_class
8
+
9
+ attr_reader :preview_class
10
+
11
+ def initialize(code_object)
12
+ @code_object = code_object
13
+ @preview_class = code_object.path.constantize
14
+ @file_path = Pathname(code_object.file)
15
+ @base_directories = Engine.preview_paths
16
+
17
+ cleaned_path = relative_file_path.to_s
18
+ .gsub(/\/(component_preview|preview)(\..*)$/, "")
19
+ .gsub(/(_component_preview|_preview)(\..*)$/, "")
20
+
21
+ @lookup_path = PathUtils.to_lookup_path(cleaned_path)
22
+ end
23
+
24
+ def examples
25
+ @_examples ||= PreviewExampleCollection.new(load_examples)
26
+ end
27
+
28
+ def example(example_name)
29
+ examples.find { |m| m.name == example_name.to_s }
30
+ end
31
+
32
+ def visible_examples
33
+ @_visible_examples ||= PreviewExampleCollection.new(examples.select(&:visible?))
34
+ end
35
+
36
+ def default_example
37
+ visible_examples.first
38
+ end
39
+
40
+ def components
41
+ @_components ||= ComponentCollection.new(examples.flat_map(&:components).uniq(&:path))
42
+ end
43
+
44
+ def component
45
+ components.first
46
+ end
47
+
48
+ def display_options
49
+ global_options = Lookbook.config.preview_display_options
50
+ global_options.deep_merge(fetch_config(:display_options, {}))
51
+ end
52
+
53
+ def layout
54
+ preview_class.instance_variable_get(:@layout)
55
+ end
56
+
57
+ def url_path
58
+ lookbook_inspect_path(path)
59
+ end
60
+
61
+ def preview_class_name
62
+ preview_class.name
63
+ end
64
+
65
+ def guess_components
66
+ [preview_class.name.chomp("Preview").constantize]
67
+ rescue
68
+ []
69
+ end
70
+
71
+ protected
72
+
73
+ def load_examples
74
+ return example_entities unless code_object.groups.any?
75
+
76
+ example_entities.group_by(&:group).flat_map do |group_name, grouped_examples|
77
+ group_name.nil? ? grouped_examples : PreviewGroup.new(group_name.presence || label.pluralize, grouped_examples, self)
78
+ end
79
+ end
80
+
81
+ def example_entities
82
+ public_methods = preview_class.public_instance_methods(false)
83
+ method_objects = code_object.meths.select { |m| public_methods.include?(m.name) }
84
+ method_objects.map.with_index { |code_object, i| PreviewExample.new(code_object, self, position: i) }
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,100 @@
1
+ module Lookbook
2
+ class PreviewExample < Entity
3
+ include Inspectable
4
+ include Annotatable
5
+ include Navigable
6
+
7
+ delegate :group, to: :code_object
8
+
9
+ attr_reader :preview
10
+
11
+ def initialize(code_object, preview, position: nil)
12
+ @code_object = code_object
13
+ @preview = preview
14
+ @default_position = position
15
+ @lookup_path = "#{parent.lookup_path}/#{name}"
16
+ end
17
+
18
+ def id
19
+ @_id ||= Utils.id(fetch_config(:id) { "#{parent.id}-#{code_object.name}" })
20
+ end
21
+
22
+ def name
23
+ @_name ||= Utils.name(code_object.name)
24
+ end
25
+
26
+ def display_options
27
+ parent.display_options.merge(fetch_config(:display_options, {}))
28
+ end
29
+
30
+ def components
31
+ @_components ||= ComponentCollection.new(load_components)
32
+ end
33
+
34
+ def component
35
+ components.first
36
+ end
37
+
38
+ def examples
39
+ [self]
40
+ end
41
+
42
+ def template_source(template_path)
43
+ source_path = template_file_path(template_path)
44
+ source_path ? File.read(source_path) : nil
45
+ end
46
+
47
+ def template_lang(template_path)
48
+ path = template_file_path(template_path)
49
+ Lookbook::Lang.guess(path) || Lookbook::Lang.find(:html)
50
+ end
51
+
52
+ def search_terms
53
+ [parent.label, label]
54
+ end
55
+
56
+ def url_path
57
+ lookbook_inspect_path(path)
58
+ end
59
+
60
+ def type
61
+ :example
62
+ end
63
+
64
+ alias_method :parent, :preview
65
+ alias_method :lang, :source_lang
66
+
67
+ protected
68
+
69
+ def sort_handler(other_entity)
70
+ if Lookbook.config.sort_examples
71
+ label <=> other_entity.label
72
+ else
73
+ [position, label] <=> [other_entity.position, other_entity.label]
74
+ end
75
+ end
76
+
77
+ def format_source(source)
78
+ source.sub(/^def \w+\s?(\([^)]+\))?/m, "").split("\n")[0..-2].join("\n")
79
+ end
80
+
81
+ def template_file_path(template_path)
82
+ return full_template_path(template_path) if respond_to?(:full_template_path, true)
83
+
84
+ template_path = template_path.to_s.sub(/\..*$/, "")
85
+ base_path = Engine.preview_paths.detect do |p|
86
+ Dir["#{p}/#{template_path}.html.*"].first
87
+ end
88
+ path = Dir["#{base_path}/#{template_path}.html.*"].first
89
+ Pathname(path) if path
90
+ end
91
+
92
+ def load_components
93
+ component_classes = [*fetch_config(:components, []), *preview.send(:fetch_config, :components, [])]
94
+ component_classes = preview.guess_components if component_classes.empty?
95
+
96
+ components = component_classes.map { |klass| Component.new(klass) }
97
+ components.uniq(&:path)
98
+ end
99
+ end
100
+ end