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
@@ -1,6 +1,9 @@
1
1
  <%= lookbook_render :split_layout,
2
2
  alpine_data: "$store.layout.inspector",
3
- ":class": "($store.inspector.drawer.hidden || #{@drawer_panels.none?}) && '!grid-rows-[1fr] !grid-cols-[1fr]'" do |layout| %>
3
+ ":class": "($store.inspector.drawer.hidden || #{@drawer_panels.none?}) && '!grid-rows-[1fr] !grid-cols-[1fr]'",
4
+ data: {
5
+ "preview-target": @target.id
6
+ } do |layout| %>
4
7
 
5
8
  <%= layout.pane class: "flex flex-col h-full overflow-hidden",
6
9
  "x-effect": "forceOrientation = (layoutWidth < $store.inspector.minVerticalSplitWidth) ? 'horizontal' : null" do %>
@@ -98,7 +101,7 @@
98
101
  copy: !!panel.copy,
99
102
  "x-show": "$store.inspector.drawer.activeTab === '#{panel.name}'",
100
103
  cloak: true do %>
101
- <%== panel.copy ? panel.copy : "" %>
104
+ <%= panel.copy ? panel.copy : "" %>
102
105
  <% end %>
103
106
  <% end %>
104
107
  <% end %>
data/config/app.yml CHANGED
@@ -2,11 +2,12 @@ shared:
2
2
  project_name: Lookbook
3
3
  log_level: 2
4
4
  log_use_rails_logger: true
5
- auto_refresh: true
5
+ auto_refresh: false
6
6
  components_path: app/components
7
7
  page_controller: Lookbook::PageController
8
8
  page_route: pages
9
9
  page_paths: [test/components/docs]
10
+ page_extensions: [html.*, md.*]
10
11
  page_options: {}
11
12
  markdown_options:
12
13
  tables: true
@@ -16,6 +17,9 @@ shared:
16
17
  highlight: true
17
18
  with_toc_data: true
18
19
  lax_spacing: true
20
+ highlighter_options:
21
+ theme: github
22
+ dark: false
19
23
  sort_examples: false
20
24
  preview_paths: []
21
25
  preview_display_options: {}
@@ -33,4 +37,10 @@ shared:
33
37
 
34
38
  development:
35
39
  listen: true
40
+ auto_refresh: true
41
+ debug_menu: true
42
+
43
+ test:
44
+ listen: false
45
+ auto_refresh: false
36
46
  debug_menu: true
data/config/inputs.yml CHANGED
@@ -1,48 +1,48 @@
1
1
  shared:
2
2
  select:
3
- partial: lookbook/previews/inputs/select
3
+ partial: lookbook/inspector/inputs/select
4
4
  opts: {}
5
5
 
6
6
  textarea:
7
- partial: lookbook/previews/inputs/textarea
7
+ partial: lookbook/inspector/inputs/textarea
8
8
  opts: {}
9
9
 
10
10
  toggle:
11
- partial: lookbook/previews/inputs/toggle
11
+ partial: lookbook/inspector/inputs/toggle
12
12
  opts: {}
13
13
 
14
14
  color:
15
- partial: lookbook/previews/inputs/color
15
+ partial: lookbook/inspector/inputs/color
16
16
  opts: {}
17
17
 
18
18
  range:
19
- partial: lookbook/previews/inputs/range
19
+ partial: lookbook/inspector/inputs/range
20
20
  opts: {}
21
21
 
22
22
  text:
23
- partial: lookbook/previews/inputs/text
23
+ partial: lookbook/inspector/inputs/text
24
24
  opts: {}
25
25
 
26
26
  email:
27
- partial: lookbook/previews/inputs/text
27
+ partial: lookbook/inspector/inputs/text
28
28
  opts: {}
29
29
 
30
30
  number:
31
- partial: lookbook/previews/inputs/text
31
+ partial: lookbook/inspector/inputs/text
32
32
  opts: {}
33
33
 
34
34
  tel:
35
- partial: lookbook/previews/inputs/text
35
+ partial: lookbook/inspector/inputs/text
36
36
  opts: {}
37
37
 
38
38
  url:
39
- partial: lookbook/previews/inputs/text
39
+ partial: lookbook/inspector/inputs/text
40
40
  opts: {}
41
41
 
42
42
  date:
43
- partial: lookbook/previews/inputs/text
43
+ partial: lookbook/inspector/inputs/text
44
44
  opts: {}
45
45
 
46
46
  datetime_local:
47
- partial: lookbook/previews/inputs/text
47
+ partial: lookbook/inspector/inputs/text
48
48
  opts: {}
@@ -0,0 +1,41 @@
1
+ shared:
2
+ definitions:
3
+ - name: ruby
4
+ ext: .rb
5
+ label: Ruby
6
+ comment: "# %s"
7
+
8
+ - name: html
9
+ ext: .html
10
+ label: HTML
11
+ comment: "<!-- %s -->"
12
+
13
+ - name: erb
14
+ ext: .erb
15
+ label: ERB
16
+ comment: "<%%# %s %%>"
17
+
18
+ - name: haml
19
+ ext: .haml
20
+ label: Haml
21
+ comment: "<!-- %s -->"
22
+
23
+ - name: slim
24
+ ext: .slim
25
+ label: Slim
26
+ comment: "<!-- %s -->"
27
+
28
+ - name: tsx
29
+ ext: .tsx
30
+ label: TypeScript
31
+ comment: "// %s"
32
+
33
+ - name: js
34
+ ext: .js
35
+ label: JavasScript
36
+ comment: "// %s"
37
+
38
+ - name: css
39
+ ext: .css
40
+ label: CSS
41
+ comment: "/* %s */"
data/config/panels.yml CHANGED
@@ -1,30 +1,30 @@
1
1
  shared:
2
2
  main:
3
3
  - name: preview
4
- partial: lookbook/previews/panels/preview
4
+ partial: lookbook/inspector/panels/preview
5
5
  label: Preview
6
6
  hotkey: v
7
7
 
8
8
  - name: output
9
- partial: lookbook/previews/panels/output
9
+ partial: lookbook/inspector/panels/output
10
10
  label: HTML
11
11
  hotkey: h
12
12
 
13
13
  drawer:
14
14
  - name: source
15
- partial: lookbook/previews/panels/source
15
+ partial: lookbook/inspector/panels/source
16
16
  label: Source
17
17
  hotkey: s
18
18
  copy: "->(data) { data.examples.map { |e| e.source }.join(\n) }"
19
19
 
20
20
  - name: notes
21
- partial: lookbook/previews/panels/notes
21
+ partial: lookbook/inspector/panels/notes
22
22
  label: Notes
23
23
  hotkey: n
24
24
  disabled: ->(data) { data.examples.select { |e| e.notes.present? }.none? }
25
25
 
26
26
  - name: params
27
- partial: lookbook/previews/panels/params
27
+ partial: lookbook/inspector/panels/params
28
28
  label: Params
29
29
  hotkey: p
30
- disabled: ->(data) { data.preview.params.none? }
30
+ disabled: ->(data) { data.context.params.none? }
data/config/routes.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  Lookbook::Engine.routes.draw do
2
- if Lookbook::Engine.websocket?
3
- mount Lookbook::Engine.websocket => "/cable", :as => :cable
2
+ if Lookbook::Engine.websocket.mountable?
3
+ mount Lookbook::Engine.websocket.server => "/cable", :as => :cable
4
4
  end
5
5
 
6
6
  root to: "application#index", as: :lookbook_home
@@ -8,8 +8,8 @@ Lookbook::Engine.routes.draw do
8
8
  get "/#{Lookbook.config.page_route}", to: "pages#index", as: :lookbook_page_index
9
9
  get "/#{Lookbook.config.page_route}/*path", to: "pages#show", as: :lookbook_page
10
10
 
11
- get "/preview/*path", to: "previews#preview", as: :lookbook_preview
12
- get "/inspect/*path", to: "previews#show", as: :lookbook_inspect
11
+ get "/preview/*path", to: "previews#show", as: :lookbook_preview
12
+ get "/inspect/*path", to: "inspector#show", as: :lookbook_inspect
13
13
 
14
- get "/*path", to: "previews#show_legacy", as: :lookbook_inspect_legacy
14
+ get "/*path", to: "inspector#show_legacy", as: :lookbook_inspect_legacy
15
15
  end
data/config/tags.yml CHANGED
@@ -25,5 +25,12 @@ shared:
25
25
 
26
26
  param:
27
27
  label: Param
28
- yard_parser: with_name
28
+ opts: {}
29
+
30
+ logical_path:
31
+ label: Logical Path
32
+ opts: {}
33
+
34
+ source:
35
+ label: Source file
29
36
  opts: {}
@@ -1,6 +1,7 @@
1
1
  require "view_component"
2
2
  require "action_cable/engine"
3
3
  require "listen"
4
+ require "yard"
4
5
 
5
6
  module Lookbook
6
7
  class Engine < Rails::Engine
@@ -8,18 +9,17 @@ module Lookbook
8
9
 
9
10
  config.autoload_paths << File.expand_path(root.join("app/components"))
10
11
 
11
- initializer "lookbook.viewcomponent.config" do
12
- Lookbook.config.preview_paths += config.view_component.preview_paths
13
- Lookbook.config.preview_controller ||= config.view_component.preview_controller
14
-
15
- Lookbook.config.components_path = config.view_component.view_component_path if config.view_component.view_component_path.present?
16
-
17
- Lookbook.config.listen_paths += Lookbook.config.preview_paths
18
- Lookbook.config.listen_paths << Lookbook.config.components_path
12
+ config.before_configuration do
13
+ config.lookbook = Lookbook.config
19
14
  end
20
15
 
21
- initializer "lookbook.parser.tags" do
22
- Lookbook::Parser.define_tags(Engine.tags)
16
+ initializer "lookbook.viewcomponent.config_sync" do
17
+ opts.preview_paths += config.view_component.preview_paths
18
+ opts.preview_controller ||= config.view_component.preview_controller
19
+
20
+ if config.view_component.view_component_path.present?
21
+ opts.components_path = config.view_component.view_component_path
22
+ end
23
23
  end
24
24
 
25
25
  initializer "lookbook.assets.serve" do
@@ -29,119 +29,93 @@ module Lookbook
29
29
  )
30
30
  end
31
31
 
32
- config.before_configuration do
33
- config.lookbook = Lookbook.config
32
+ initializer "lookbook.file_watcher.paths" do
33
+ opts.listen_paths += opts.preview_paths
34
+ opts.listen_paths << opts.components_path
34
35
  end
35
36
 
36
- config.after_initialize do
37
- @preview_controller = Lookbook.config.preview_controller.constantize
38
- @preview_controller.include(Lookbook::PreviewController)
39
-
40
- parser.after_parse do |registry|
41
- Preview.load!(registry.all(:class))
42
- reload_ui
37
+ initializer "lookbook.file_watcher.previews" do
38
+ file_watcher.watch(opts.listen_paths, opts.listen_extensions, wait_for_delay: 0.5) do |changes|
39
+ parser.parse { run_hooks(:after_change, changes) }
43
40
  end
41
+ end
44
42
 
45
- if Gem::Version.new(Rails.version) >= Gem::Version.new("6.1.3.1")
46
- # Rails.application.server is only available for newer Rails versions
47
- Rails.application.server do
48
- init_listeners
49
- end
50
- else
51
- # Fallback for older Rails versions - don't start listeners if running in a rake task.
52
- unless prevent_listening?
53
- init_listeners
54
- end
43
+ initializer "lookbook.file_watcher.pages" do
44
+ file_watcher.watch(opts.page_paths, opts.page_extensions) do |changes|
45
+ Engine.pages.load(Engine.page_paths)
46
+ Engine.websocket.broadcast(:reload)
47
+ run_hooks(:after_change, changes)
55
48
  end
49
+ end
56
50
 
57
- parser.parse do
58
- run_hooks(:after_initialize)
51
+ initializer "lookbook.parser.previews_load_callback" do
52
+ parser.after_parse do |code_objects|
53
+ Engine.previews.load(code_objects.all(:class))
54
+ Engine.websocket.broadcast(:reload)
59
55
  end
60
56
  end
61
57
 
62
- at_exit do
63
- if listeners.any?
64
- Lookbook.logger.debug "Stopping listeners"
65
- stop_listeners
58
+ # The preview controller handles the rendering of individual previews.
59
+ #
60
+ # Lookbook injects some actions into whichever controller has been
61
+ # specified by the user in order to render previews within the context of
62
+ # the particular controller class instance so that any before_action/after_action
63
+ # callbacks will be correctly processed.
64
+ config.after_initialize do
65
+ @preview_controller = opts.preview_controller.constantize
66
+ @preview_controller.class_eval { include Lookbook::PreviewActions }
67
+ end
68
+
69
+ config.after_initialize do
70
+ if Rails.application.respond_to?(:server)
71
+ Rails.application.server { file_watcher.start if listen? }
72
+ elsif process.supports_listening?
73
+ file_watcher.start if listen?
66
74
  end
67
- run_hooks(:before_exit)
68
75
  end
69
76
 
70
- class << self
71
- def init_listeners
72
- config = Lookbook.config
73
- return unless config.listen == true
74
-
75
- listen_paths = PathUtils.normalize_all(config.listen_paths)
76
- if listen_paths.any?
77
- preview_listener = Listen.to(*listen_paths,
78
- only: /\.(#{config.listen_extensions.join("|")})$/,
79
- wait_for_delay: 0.5,
80
- force_polling: config.listen_use_polling) do |modified, added, removed|
81
- parser.parse do
82
- run_hooks(:after_change, {modified: modified, added: added, removed: removed})
83
- end
84
- end
85
- register_listener(preview_listener)
86
- end
77
+ config.after_initialize do
78
+ Engine.pages.load(Engine.page_paths)
79
+ parser.parse { run_hooks(:after_initialize) }
80
+ end
87
81
 
88
- page_paths = PathUtils.normalize_all(config.page_paths)
89
- if page_paths.any?
90
- page_listener = Listen.to(*page_paths,
91
- only: /\.(html.*|md.*)$/,
92
- force_polling: config.listen_use_polling) do |modified, added, removed|
93
- changes = {modified: modified, added: added, removed: removed}
94
- reload_ui
95
- run_hooks(:after_change, changes)
96
- end
97
- register_listener(page_listener)
98
- end
99
- end
82
+ def opts
83
+ Lookbook.config
84
+ end
100
85
 
101
- def websocket
102
- config = Lookbook.config
103
- return @websocket unless @websocket.nil?
104
- return unless config.auto_refresh == true && config.listen == true && !Rails.env.test?
105
- Lookbook.logger.info "Initializing websocket"
106
-
107
- cable = ActionCable::Server::Configuration.new
108
- cable.cable = {adapter: "async"}.with_indifferent_access
109
- cable.mount_path = nil
110
- cable.connection_class = -> { Lookbook::Connection }
111
- cable.logger = Lookbook.logger
112
-
113
- @websocket ||= if Gem::Version.new(Rails.version) >= Gem::Version.new(6.0)
114
- ActionCable::Server::Base.new(config: cable)
115
- else
116
- ws = ActionCable::Server::Base.new
117
- ws.config = cable
118
- ws
119
- end
86
+ def run_hooks(event_name, *args)
87
+ Engine.hooks.for_event(event_name).each do |hook|
88
+ hook.call(Lookbook, *args)
120
89
  end
90
+ end
121
91
 
122
- def websocket_mount_path
123
- "#{mounted_path}/cable".gsub("//", "/") if websocket?
124
- end
92
+ def parser
93
+ @_parser ||= PreviewParser.new(opts.preview_paths, Engine.tags)
94
+ end
125
95
 
126
- def websocket?
127
- websocket.present?
128
- end
96
+ def file_watcher
97
+ @_file_watcher ||= FileWatcher.new(force_polling: opts.listen_use_polling)
98
+ end
129
99
 
130
- def mounted_path
131
- routes.find_script_name({})
132
- end
100
+ def process
101
+ @_process ||= Process.new(env: Rails.env)
102
+ end
103
+
104
+ def listen?
105
+ opts.listen && process.supports_listening?
106
+ end
133
107
 
134
- def parser
135
- preview_paths = PathUtils.normalize_all(Lookbook.config.preview_paths)
136
- @parser ||= Lookbook::Parser.new(preview_paths)
108
+ class << self
109
+ def mount_path
110
+ routes.find_script_name({})
137
111
  end
138
112
 
139
- def log_level
140
- Lookbook.logger.level
113
+ def mounted?
114
+ mount_path.present?
141
115
  end
142
116
 
143
117
  def app_name
144
- name = if Gem::Version.new(Rails.version) >= Gem::Version.new("6.1")
118
+ name = if Rails.application.class.respond_to?(:module_parent_name)
145
119
  Rails.application.class.module_parent_name
146
120
  else
147
121
  Rails.application.class.parent_name
@@ -149,58 +123,57 @@ module Lookbook
149
123
  name.underscore
150
124
  end
151
125
 
152
- def register_listener(listener)
153
- listener.start
154
- listeners << listener
155
- end
156
-
157
- def listeners
158
- @listeners ||= []
126
+ def websocket
127
+ if mounted?
128
+ use_websocket = opts.auto_refresh && opts.listen && process.supports_listening?
129
+ @websocket ||= use_websocket ? Websocket.new(mount_path, logger: Lookbook.logger) : Websocket.noop
130
+ else
131
+ Websocket.noop
132
+ end
159
133
  end
160
134
 
161
- def stop_listeners
162
- listeners.each { |listener| listener.stop }
135
+ def panels
136
+ @_panels ||= PanelStore.init_from_config
163
137
  end
164
138
 
165
- def run_hooks(event_name, *args)
166
- hooks.for_event(event_name).each do |hook|
167
- hook.call(Lookbook, *args)
168
- end
139
+ def inputs
140
+ @_inputs ||= InputStore.init_from_config
169
141
  end
170
142
 
171
- def reload_ui
172
- websocket&.broadcast("reload", {})
143
+ def tags
144
+ @_tags ||= TagStore.init_from_config
173
145
  end
174
146
 
175
- def prevent_listening?
176
- Rails.env.test? || running_in_rake_task?
147
+ def hooks
148
+ @_hooks ||= HookStore.init_from_config
177
149
  end
178
150
 
179
- def running_in_rake_task?
180
- if defined?(Rake) && Rake.respond_to?(:application)
181
- File.basename($0) == "rake" || Rake.application.top_level_tasks.any?
182
- else
183
- false
184
- end
151
+ def component_paths
152
+ @_component_paths ||= Array(PathUtils.to_absolute(opts.components_path))
185
153
  end
186
154
 
187
- def panels
188
- @panels ||= PanelStore.init_from_config
155
+ def page_paths
156
+ @_page_paths ||= PathUtils.normalize_paths(opts.page_paths)
189
157
  end
190
158
 
191
- def inputs
192
- @inputs ||= InputStore.init_from_config
159
+ def preview_paths
160
+ @_preview_paths ||= PathUtils.normalize_paths(opts.preview_paths)
193
161
  end
194
162
 
195
- def tags
196
- @tags ||= TagStore.init_from_config
163
+ def pages
164
+ @_pages ||= PageCollection.new
197
165
  end
198
166
 
199
- def hooks
200
- @hooks ||= HookStore.init_from_config
167
+ def previews
168
+ @_previews ||= PreviewCollection.new
201
169
  end
202
170
 
203
171
  attr_reader :preview_controller
204
172
  end
173
+
174
+ at_exit do
175
+ file_watcher.stop
176
+ run_hooks(:before_exit)
177
+ end
205
178
  end
206
179
  end
@@ -0,0 +1,4 @@
1
+ module Lookbook
2
+ class ComponentCollection < EntityCollection
3
+ end
4
+ end
@@ -0,0 +1,23 @@
1
+ module Lookbook
2
+ module HierarchicalCollection
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ def entities
7
+ @_cache[:entities] ||= collect_ordered_entities(to_tree(include_hidden: true))
8
+ end
9
+
10
+ def to_tree(include_hidden: false)
11
+ @_cache[include_hidden ? :tree_with_hidden : :tree] ||= EntityTreeBuilder.call(@entities, include_hidden: include_hidden)
12
+ end
13
+
14
+ protected
15
+
16
+ def collect_ordered_entities(start_node)
17
+ start_node.inject([]) do |entities, node|
18
+ entities.append(node.content? ? node.content : collect_ordered_entities(node))
19
+ end.flatten
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,61 @@
1
+ module Lookbook
2
+ class EntityCollection
3
+ include Enumerable
4
+
5
+ delegate_missing_to :entities
6
+
7
+ attr_reader :entities
8
+
9
+ def initialize(entities = nil)
10
+ @_cache = {}
11
+ @entities = []
12
+ add(entities)
13
+ end
14
+
15
+ def add(to_add = nil)
16
+ Array(to_add).each do |entity|
17
+ unless find_by_path(entity.path)
18
+ @entities.push(entity)
19
+ end
20
+ end
21
+ clear_cache
22
+ end
23
+
24
+ def find_by_id(id)
25
+ id = Utils.id(id)
26
+ entities.find { |entity| entity.id == id }
27
+ end
28
+
29
+ def find_by_path(path)
30
+ entities.find { |entity| entity.path.to_s == path.to_s }
31
+ end
32
+
33
+ def next(entity)
34
+ index = entities.find_index { |i| i.path == entity.path }
35
+ entities[index + 1] unless index.nil?
36
+ end
37
+
38
+ def previous(entity)
39
+ index = entities.find_index { |i| i.path == entity.path }
40
+ entities[index - 1] if !index.nil? && index > 0
41
+ end
42
+
43
+ def each(&block)
44
+ if block
45
+ entities.sort.each { |entity| yield entity }
46
+ else
47
+ to_enum(:each)
48
+ end
49
+ end
50
+
51
+ def flat_map(...)
52
+ entities.map(...).map(&:to_a).flatten
53
+ end
54
+
55
+ protected
56
+
57
+ def clear_cache
58
+ @_cache = {}
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,30 @@
1
+ module Lookbook
2
+ class PageCollection < EntityCollection
3
+ include HierarchicalCollection
4
+
5
+ def load(page_paths)
6
+ @entities = []
7
+ clear_cache
8
+
9
+ file_paths = page_paths.flat_map do |dir|
10
+ PathUtils.normalize_paths(Dir["#{dir}/**/*.html.*", "#{dir}/**/*.md.*"].sort)
11
+ end
12
+
13
+ entities = file_paths.map { |path| PageCollection.entity(path) }
14
+ pages, sections = entities.partition { |page| page.type == :page }
15
+
16
+ page_dict = pages.index_by(&:lookup_path)
17
+ sections.each do |section|
18
+ parent = page_dict[section.lookup_path]
19
+ section.parent = parent
20
+ parent.add_section(section)
21
+ end
22
+
23
+ add(pages)
24
+ end
25
+
26
+ def self.entity(file_path)
27
+ File.basename(file_path).match?(%r{\[(.*?\w+)\]}) ? PageSection.new(file_path) : Page.new(file_path)
28
+ end
29
+ end
30
+ end