lookbook 0.9.3 → 1.0.0.beta.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (207) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +43 -867
  3. data/app/assets/lookbook/css/lookbook.css +55 -0
  4. data/app/assets/lookbook/css/themes/blue.css +42 -0
  5. data/app/assets/lookbook/css/themes/indigo.css +42 -0
  6. data/app/assets/lookbook/css/themes/zinc.css +42 -0
  7. data/app/assets/lookbook/css/{tooltip_theme.css → tooltip.css} +14 -8
  8. data/app/assets/lookbook/js/app.js +64 -63
  9. data/app/assets/lookbook/js/components/clipboard.js +47 -0
  10. data/app/assets/lookbook/js/components/tooltip.js +30 -0
  11. data/app/assets/lookbook/js/config.js +7 -4
  12. data/app/assets/lookbook/js/helpers/build.js +22 -0
  13. data/app/assets/lookbook/js/helpers/dom.js +45 -0
  14. data/app/assets/lookbook/js/helpers/layout.js +21 -0
  15. data/app/assets/lookbook/js/helpers/request.js +16 -0
  16. data/app/assets/lookbook/js/helpers/string.js +11 -0
  17. data/app/assets/lookbook/js/lib/socket.js +4 -3
  18. data/app/assets/lookbook/js/lib/tippy.js +8 -0
  19. data/app/assets/lookbook/js/lookbook.js +61 -0
  20. data/app/assets/lookbook/js/plugins/logger.js +39 -0
  21. data/app/assets/lookbook/js/stores/filter.js +2 -2
  22. data/app/assets/lookbook/js/stores/inspector.js +22 -16
  23. data/app/assets/lookbook/js/stores/layout.js +101 -5
  24. data/app/assets/lookbook/js/stores/nav.js +17 -16
  25. data/app/assets/lookbook/js/stores/pages.js +4 -2
  26. data/app/assets/lookbook/js/stores/settings.js +7 -0
  27. data/app/assets/lookbook/js/stores/workbench.js +29 -0
  28. data/app/components/lookbook/button/component.html.erb +28 -0
  29. data/app/components/lookbook/button/component.js +55 -0
  30. data/app/components/lookbook/button/component.rb +39 -0
  31. data/app/components/lookbook/button_group/component.html.erb +3 -0
  32. data/app/components/lookbook/button_group/component.rb +18 -0
  33. data/app/components/lookbook/code/component.css +57 -0
  34. data/app/components/lookbook/code/component.html.erb +10 -0
  35. data/app/components/lookbook/code/component.js +3 -0
  36. data/app/components/lookbook/code/component.rb +56 -0
  37. data/app/components/lookbook/code/highlight_github_light.css +217 -0
  38. data/app/components/lookbook/component.rb +41 -0
  39. data/app/components/lookbook/copy_button/component.html.erb +11 -0
  40. data/app/components/lookbook/copy_button/component.js +16 -0
  41. data/app/components/lookbook/copy_button/component.rb +23 -0
  42. data/app/components/lookbook/dimensions_display/component.html.erb +10 -0
  43. data/app/components/lookbook/dimensions_display/component.js +30 -0
  44. data/app/components/lookbook/dimensions_display/component.rb +18 -0
  45. data/app/components/lookbook/embed/component.html.erb +50 -0
  46. data/app/components/lookbook/embed/component.js +39 -0
  47. data/app/components/lookbook/embed/component.rb +22 -0
  48. data/app/components/lookbook/filter/component.html.erb +17 -0
  49. data/app/components/lookbook/filter/component.js +21 -0
  50. data/app/components/lookbook/filter/component.rb +15 -0
  51. data/app/components/lookbook/header/component.html.erb +79 -0
  52. data/app/components/lookbook/header/component.rb +9 -0
  53. data/app/components/lookbook/icon/component.css +11 -0
  54. data/app/components/lookbook/icon/component.html.erb +5 -0
  55. data/app/components/lookbook/icon/component.js +5 -0
  56. data/app/components/lookbook/icon/component.rb +23 -0
  57. data/app/components/lookbook/nav/component.html.erb +33 -0
  58. data/app/components/lookbook/nav/component.js +52 -0
  59. data/app/components/lookbook/nav/component.rb +37 -0
  60. data/app/components/lookbook/nav/item/component.html.erb +23 -0
  61. data/app/components/lookbook/nav/item/component.js +66 -0
  62. data/app/components/lookbook/nav/item/component.rb +84 -0
  63. data/app/components/lookbook/params_editor/component.html.erb +3 -0
  64. data/app/components/lookbook/params_editor/component.js +12 -0
  65. data/app/components/lookbook/params_editor/component.rb +11 -0
  66. data/app/components/lookbook/params_editor/field/component.html.erb +50 -0
  67. data/app/components/lookbook/params_editor/field/component.js +36 -0
  68. data/app/components/lookbook/params_editor/field/component.rb +41 -0
  69. data/app/components/lookbook/prose/component.css +12 -0
  70. data/app/components/lookbook/prose/component.html.erb +3 -0
  71. data/app/components/lookbook/prose/component.rb +26 -0
  72. data/app/components/lookbook/split_layout/component.html.erb +13 -0
  73. data/app/components/lookbook/split_layout/component.js +151 -0
  74. data/app/components/lookbook/split_layout/component.rb +11 -0
  75. data/app/components/lookbook/tabbed_content/component.html.erb +5 -0
  76. data/app/components/lookbook/tabbed_content/component.js +21 -0
  77. data/app/components/lookbook/tabbed_content/component.rb +20 -0
  78. data/app/components/lookbook/tabbed_content/section/component.html.erb +8 -0
  79. data/app/components/lookbook/tabbed_content/section/component.rb +9 -0
  80. data/app/components/lookbook/tabs/component.css +8 -0
  81. data/app/components/lookbook/tabs/component.html.erb +14 -0
  82. data/app/components/lookbook/tabs/component.js +107 -0
  83. data/app/components/lookbook/tabs/component.rb +30 -0
  84. data/app/components/lookbook/tabs/dropdown_tab/component.html.erb +14 -0
  85. data/app/components/lookbook/tabs/dropdown_tab/component.rb +16 -0
  86. data/app/components/lookbook/tabs/tab/component.html.erb +18 -0
  87. data/app/components/lookbook/tabs/tab/component.rb +16 -0
  88. data/app/components/lookbook/tag_component.rb +29 -0
  89. data/app/components/lookbook/toolbar/component.css +16 -0
  90. data/app/components/lookbook/toolbar/component.html.erb +5 -0
  91. data/app/components/lookbook/toolbar/component.rb +26 -0
  92. data/app/components/lookbook/viewport/component.css +11 -0
  93. data/app/components/lookbook/viewport/component.html.erb +57 -0
  94. data/app/{assets/lookbook/js/components/preview-window.js → components/lookbook/viewport/component.js} +57 -14
  95. data/app/components/lookbook/viewport/component.rb +21 -0
  96. data/app/controllers/lookbook/application_controller.rb +16 -5
  97. data/app/controllers/lookbook/pages_controller.rb +18 -10
  98. data/app/controllers/lookbook/previews_controller.rb +62 -25
  99. data/app/helpers/lookbook/application_helper.rb +7 -3
  100. data/app/helpers/lookbook/component_helper.rb +22 -10
  101. data/app/helpers/lookbook/output_helper.rb +8 -4
  102. data/app/helpers/lookbook/page_helper.rb +13 -21
  103. data/app/views/layouts/lookbook/application.html.erb +76 -28
  104. data/app/views/layouts/lookbook/inspector.html.erb +7 -0
  105. data/app/views/layouts/lookbook/page.html.erb +53 -0
  106. data/app/views/layouts/lookbook/shell.html.erb +64 -0
  107. data/app/views/layouts/lookbook/skeleton.html.erb +27 -10
  108. data/app/views/layouts/lookbook/standalone.html.erb +5 -0
  109. data/app/views/lookbook/404.html.erb +15 -0
  110. data/app/views/lookbook/error.html.erb +34 -34
  111. data/app/views/lookbook/index.html.erb +11 -6
  112. data/app/views/lookbook/pages/show.html.erb +29 -93
  113. data/app/views/{layouts/lookbook → lookbook}/preview.html.erb +3 -5
  114. data/app/views/lookbook/previews/panels/_notes.html.erb +19 -25
  115. data/app/views/lookbook/previews/panels/_output.html.erb +7 -18
  116. data/app/views/lookbook/previews/panels/_params.html.erb +13 -15
  117. data/app/views/lookbook/previews/panels/_preview.html.erb +6 -52
  118. data/app/views/lookbook/previews/panels/_source.html.erb +7 -16
  119. data/app/views/lookbook/previews/show.html.erb +130 -24
  120. data/config/routes.rb +7 -7
  121. data/lib/lookbook/code_formatter.rb +37 -13
  122. data/lib/lookbook/collection.rb +19 -16
  123. data/lib/lookbook/config.rb +125 -0
  124. data/lib/lookbook/engine.rb +69 -67
  125. data/lib/lookbook/entity.rb +47 -0
  126. data/lib/lookbook/error.rb +1 -2
  127. data/lib/lookbook/features.rb +1 -1
  128. data/lib/lookbook/markdown.rb +3 -5
  129. data/lib/lookbook/page.rb +26 -43
  130. data/lib/lookbook/page_collection.rb +8 -0
  131. data/lib/lookbook/params.rb +14 -3
  132. data/lib/lookbook/parser.rb +4 -0
  133. data/lib/lookbook/preview.rb +16 -7
  134. data/lib/lookbook/preview_collection.rb +8 -0
  135. data/lib/lookbook/preview_controller.rb +6 -2
  136. data/lib/lookbook/preview_example.rb +5 -6
  137. data/lib/lookbook/preview_group.rb +4 -9
  138. data/lib/lookbook/{code_inspector.rb → source_inspector.rb} +2 -2
  139. data/lib/lookbook/theme.rb +22 -0
  140. data/lib/lookbook/utils.rb +11 -3
  141. data/lib/lookbook/version.rb +1 -1
  142. data/lib/lookbook.rb +4 -1
  143. data/lib/tasks/lookbook_tasks.rake +13 -1
  144. data/public/lookbook-assets/css/app.css +2340 -1
  145. data/public/lookbook-assets/css/app.css.map +11 -1
  146. data/public/lookbook-assets/css/lookbook.css +3040 -0
  147. data/public/lookbook-assets/css/lookbook.css.map +1 -0
  148. data/public/lookbook-assets/css/themes/blue.css +44 -0
  149. data/public/lookbook-assets/css/themes/blue.css.map +1 -0
  150. data/public/lookbook-assets/css/themes/indigo.css +44 -0
  151. data/public/lookbook-assets/css/themes/indigo.css.map +1 -0
  152. data/public/lookbook-assets/css/themes/zinc.css +44 -0
  153. data/public/lookbook-assets/css/themes/zinc.css.map +1 -0
  154. data/public/lookbook-assets/js/app.js +10861 -1
  155. data/public/lookbook-assets/js/app.js.map +2571 -1
  156. data/public/lookbook-assets/js/embed.js +895 -1
  157. data/public/lookbook-assets/js/embed.js.map +1 -1
  158. data/public/lookbook-assets/js/lookbook.js +13529 -0
  159. data/public/lookbook-assets/js/lookbook.js.map +1 -0
  160. metadata +128 -116
  161. data/app/assets/lookbook/css/app.css +0 -161
  162. data/app/assets/lookbook/css/code_theme.css +0 -214
  163. data/app/assets/lookbook/js/components/app.js +0 -55
  164. data/app/assets/lookbook/js/components/code.js +0 -5
  165. data/app/assets/lookbook/js/components/copy.js +0 -20
  166. data/app/assets/lookbook/js/components/embed.js +0 -89
  167. data/app/assets/lookbook/js/components/filter.js +0 -35
  168. data/app/assets/lookbook/js/components/inspector.js +0 -66
  169. data/app/assets/lookbook/js/components/nav-group.js +0 -47
  170. data/app/assets/lookbook/js/components/nav-item.js +0 -29
  171. data/app/assets/lookbook/js/components/nav.js +0 -28
  172. data/app/assets/lookbook/js/components/page-tabs.js +0 -9
  173. data/app/assets/lookbook/js/components/page.js +0 -25
  174. data/app/assets/lookbook/js/components/param.js +0 -34
  175. data/app/assets/lookbook/js/components/sidebar.js +0 -18
  176. data/app/assets/lookbook/js/components/sizes.js +0 -16
  177. data/app/assets/lookbook/js/components/splitter.js +0 -25
  178. data/app/assets/lookbook/js/components/tabs.js +0 -52
  179. data/app/assets/lookbook/js/lib/split.js +0 -15
  180. data/app/assets/lookbook/js/stores/sidebar.js +0 -26
  181. data/app/views/layouts/lookbook/basic.html.erb +0 -7
  182. data/app/views/lookbook/components/_branding.html.erb +0 -8
  183. data/app/views/lookbook/components/_code.html.erb +0 -17
  184. data/app/views/lookbook/components/_copy_button.html.erb +0 -11
  185. data/app/views/lookbook/components/_drawer.html.erb +0 -112
  186. data/app/views/lookbook/components/_embed.html.erb +0 -39
  187. data/app/views/lookbook/components/_errors.html.erb +0 -13
  188. data/app/views/lookbook/components/_filter.html.erb +0 -18
  189. data/app/views/lookbook/components/_header.html.erb +0 -6
  190. data/app/views/lookbook/components/_icon.html.erb +0 -5
  191. data/app/views/lookbook/components/_nav.html.erb +0 -16
  192. data/app/views/lookbook/components/_nav_collection.html.erb +0 -5
  193. data/app/views/lookbook/components/_nav_group.html.erb +0 -14
  194. data/app/views/lookbook/components/_nav_item.html.erb +0 -24
  195. data/app/views/lookbook/components/_nav_page.html.erb +0 -22
  196. data/app/views/lookbook/components/_nav_preview.html.erb +0 -13
  197. data/app/views/lookbook/components/_not_found.html.erb +0 -11
  198. data/app/views/lookbook/components/_param.html.erb +0 -21
  199. data/app/views/lookbook/components/_preview.html.erb +0 -77
  200. data/app/views/lookbook/components/_sidebar.html.erb +0 -69
  201. data/app/views/lookbook/pages/not_found.html.erb +0 -15
  202. data/app/views/lookbook/previews/error.html.erb +0 -1
  203. data/app/views/lookbook/previews/inputs/_select.html.erb +0 -7
  204. data/app/views/lookbook/previews/inputs/_text.html.erb +0 -8
  205. data/app/views/lookbook/previews/inputs/_textarea.html.erb +0 -8
  206. data/app/views/lookbook/previews/inputs/_toggle.html.erb +0 -13
  207. data/app/views/lookbook/previews/not_found.html.erb +0 -23
@@ -1,24 +1,130 @@
1
- <div
2
- id="inspector"
3
- class="bg-gray-50 h-screen grid"
4
- x-data="inspector"
5
- :style="`${horizontal ? `grid-template-rows: 1fr 1px ${drawerHidden ? '0' : $store.inspector.drawer.height}px` : `grid-template-columns: 1fr 1px ${drawerHidden ? '0' : $store.inspector.drawer.width}px` }`">
6
- <%= component "preview", example: @example, examples: @examples, panels: @preview_panels %>
7
- <div
8
- x-data="splitter('horizontal', {minSize: $store.inspector.drawer.minHeight})"
9
- class="w-full gutter border-t border-gray-300 relative"
10
- x-effect="$store.inspector.drawer.height = maxDrawerHeight ? Math.min(splits[2] || $store.inspector.drawer.height, maxDrawerHeight) : splits[2] || $store.inspector.drawer.height"
11
- x-show="horizontal && !drawerHidden"
12
- >
13
- <div class="h-[11px] w-full bg-transparent hover:bg-indigo-100 hover:bg-opacity-20 transition absolute left-0 right-0 -translate-y-1/2 cursor-[row-resize]"></div>
14
- </div>
15
- <div
16
- x-data="splitter('vertical', {minSize: $store.inspector.drawer.minWidth})"
17
- class="h-full gutter border-r border-gray-300 relative"
18
- x-effect="$store.inspector.drawer.width = maxDrawerWidth ? Math.min(splits[2] || $store.inspector.drawer.width, maxDrawerWidth) : splits[2] || $store.inspector.drawer.width"
19
- x-show="vertical && !drawerHidden"
20
- >
21
- <div class="w-[9px] h-full bg-transparent hover:bg-indigo-100 hover:bg-opacity-20 transition absolute top-0 bottom-0 -translate-x-1/2 cursor-[col-resize] z-10"></div>
22
- </div>
23
- <%= component "drawer", example: @example, examples: @examples, panels: @drawer_panels %>
24
- </div>
1
+ <%= render_component :split_layout,
2
+ alpine_data: "$store.layout.inspector",
3
+ ":class": "$store.inspector.drawer.hidden && '!grid-rows-[1fr] !grid-cols-[1fr]'" do |layout| %>
4
+
5
+ <%= layout.pane class: "flex flex-col h-full overflow-hidden",
6
+ x_effect: "forceOrientation = (layoutWidth < $store.inspector.minVerticalSplitWidth) ? 'horizontal' : null" do %>
7
+
8
+ <%= render_component :toolbar do |toolbar| %>
9
+ <% toolbar.section ":class": "layoutResizing && 'overflow-hidden'" do %>
10
+ <%= render_component :tabs, alpine_data: "$store.inspector.preview" do |tabs| %>
11
+ <%= @preview_panels.each do |key, panel| %>
12
+ <% tabs.tab ref: key, **panel.extract!(:label, :hotkey, :disabled) %>
13
+ <% end %>
14
+ <% end %>
15
+ <% end %>
16
+
17
+ <% toolbar.section align: :right, padded: true, class: "flex-none min-w-[140px]", ":class": "{invisible: $store.inspector.preview.activeTab !== 'preview'}" do %>
18
+ <%= render_component :dimensions_display,
19
+ target: "[data-component=viewport] iframe",
20
+ class: "ml-auto opacity-30 hover:opacity-100" %>
21
+ <% end %>
22
+
23
+ <% toolbar.section divide: :left, class: "flex-none relative z-10" do %>
24
+ <%= render_component :button_group do |group| %>
25
+ <% if Lookbook.pages.any? %>
26
+ <% group.button icon: :code,
27
+ tooltip: "Copy page embed code",
28
+ copy: true do %>
29
+ &lt;%= embed <%= @preview.preview_class %>, :<%= @example.name %>, params: <%= request.query_parameters.deep_symbolize_keys.to_s %> %&gt;
30
+ <% end %>
31
+ <% end %>
32
+
33
+ <% group.button icon: :refresh_cw,
34
+ tooltip: "Refresh preview",
35
+ "@click.stop": "startSpin(); $dispatch('viewport:reload'); stopSpin(500);" %>
36
+
37
+ <% group.button icon: :external_link,
38
+ href: lookbook.preview_path(@example.lookup_path),
39
+ tooltip: "Open preview in new window",
40
+ target: "_blank" %>
41
+
42
+ <% group.button icon: "vertical ? 'sidebar' : 'credit-card'",
43
+ tooltip: "Show drawer",
44
+ "@click": "$store.inspector.drawer.hidden = false",
45
+ class: "rotate-180",
46
+ x_show: "$store.inspector.drawer.hidden",
47
+ cloak: true %>
48
+ <% end %>
49
+ <% end %>
50
+ <% end %>
51
+
52
+ <div class="h-full relative overflow-auto">
53
+ <%= render_component :tabbed_content, alpine_data: "$store.inspector.preview" do |content| %>
54
+ <%= @preview_panels.each do |key, panel| %>
55
+ <% content.section ref: key, class: "overflow-hidden" do %>
56
+ <%= render panel[:template],
57
+ preview: @preview,
58
+ example: @example,
59
+ rendered_examples: @rendered_examples,
60
+ **panel %>
61
+ <% end %>
62
+ <% end %>
63
+ <% end %>
64
+ </div>
65
+ <% end %>
66
+
67
+ <%= layout.pane class: "flex flex-col h-full overflow-hidden bg-lookbook-drawer", x_show: "!$store.inspector.drawer.hidden" do %>
68
+
69
+ <%= render_component :toolbar do |toolbar| %>
70
+ <% toolbar.section ":class": "layoutResizing && 'overflow-hidden'" do %>
71
+ <%= render_component :tabs, alpine_data: "$store.inspector.drawer" do |tabs| %>
72
+ <%= @drawer_panels.each do |key, panel| %>
73
+ <% tabs.tab ref: key, **panel.extract!(:label, :hotkey, :disabled) %>
74
+ <% end %>
75
+ <% end %>
76
+ <% end %>
77
+
78
+ <% toolbar.section align: :right, divide: :left, class: "flex-none relative z-10" do %>
79
+ <%= render_component :button_group do |group| %>
80
+
81
+ <%= @drawer_panels.each do |key, panel| %>
82
+ <% group.button icon: :clipboard,
83
+ tooltip: "Copy to clipboard",
84
+ copy: !!panel[:copy],
85
+ disabled: panel[:disabled] || !panel[:copy],
86
+ x_show: "$store.inspector.drawer.activeTab === '#{key}'",
87
+ cloak: true do %>
88
+ <%== panel[:copy] ? panel[:copy] : "" %>
89
+ <% end %>
90
+ <% end %>
91
+
92
+ <% group.button icon: :corner_up_right,
93
+ tooltip: "Move drawer to right",
94
+ "@click": "switchOrientation",
95
+ x_show: "horizontal && layoutWidth > $store.inspector.minVerticalSplitWidth",
96
+ cloak: true %>
97
+ <% group.button icon: :corner_up_right,
98
+ x_show: "horizontal && layoutWidth <= $store.inspector.minVerticalSplitWidth",
99
+ disabled: true,
100
+ cloak: true %>
101
+
102
+ <% group.button icon: :corner_left_down,
103
+ tooltip: "Move drawer to bottom",
104
+ "@click": "switchOrientation",
105
+ x_show: "vertical",
106
+ cloak: true %>
107
+
108
+ <% group.button icon: :x_circle,
109
+ tooltip: "Hide drawer",
110
+ "@click": "$store.inspector.drawer.hidden = true",
111
+ cloak: true %>
112
+ <% end %>
113
+ <% end %>
114
+ <% end %>
115
+
116
+ <div class="h-full overflow-auto">
117
+ <%= render_component :tabbed_content, alpine_data: "$store.inspector.drawer" do |content| %>
118
+ <% @drawer_panels.each do |key, panel| %>
119
+ <% content.section ref: key do %>
120
+ <%= render panel[:template],
121
+ preview: @preview,
122
+ example: @example,
123
+ rendered_examples: @rendered_examples,
124
+ **panel %>
125
+ <% end %>
126
+ <% end %>
127
+ <% end %>
128
+ </div>
129
+ <% end %>
130
+ <% end %>
data/config/routes.rb CHANGED
@@ -3,13 +3,13 @@ Lookbook::Engine.routes.draw do
3
3
  mount Lookbook::Engine.websocket => Lookbook.config.cable_mount_path
4
4
  end
5
5
 
6
- root to: "application#index", as: :lookbook_home
6
+ root to: "application#index", as: :home
7
7
 
8
- if Lookbook::Features.enabled?(:pages)
9
- get "/#{Lookbook.config.page_route}", to: "pages#index", as: :lookbook_page_index
10
- get "/#{Lookbook.config.page_route}/*path", to: "pages#show", as: :lookbook_page
11
- end
8
+ get "/#{Lookbook.config.page_route}", to: "pages#index", as: :page_index
9
+ get "/#{Lookbook.config.page_route}/*path", to: "pages#show", as: :page
10
+
11
+ get "/preview/*path", to: "previews#preview", as: :preview
12
+ get "/inspect/*path", to: "previews#show", as: :inspect
12
13
 
13
- get "/preview/*path", to: "previews#preview", as: :lookbook_preview
14
- get "/*path", to: "previews#show", as: :lookbook_inspect
14
+ get "/*path", to: "previews#show_legacy", as: :inspect_legacy
15
15
  end
@@ -1,19 +1,21 @@
1
1
  require "rouge"
2
2
  require "htmlbeautifier"
3
+ require 'htmlentities'
3
4
 
4
5
  module Lookbook
5
6
  module CodeFormatter
6
7
  class << self
7
- def highlight(source, language, opts = {})
8
- source&.strip! unless opts[:strip] == false
9
- source&.gsub!("&gt;", ">")&.gsub!("&lt;", "<")
10
- language ||= "ruby"
11
- formatter = Formatter.new(opts)
8
+ def highlight(source, **opts)
9
+ coder = HTMLEntities.new
10
+ source = coder.decode source
11
+ language = opts[:language] || "ruby"
12
+ formatter = Formatter.new(**opts)
12
13
  lexer = Rouge::Lexer.find(language.to_s) || Rouge::Lexer.find("plaintext")
13
14
  formatter.format(lexer.lex(source)).html_safe
14
15
  end
15
16
 
16
- def beautify(source, language = "html")
17
+ def beautify(source, **opts)
18
+ language = opts[:language] || "html"
17
19
  source = source.strip
18
20
  result = language.downcase == "html" ? HtmlBeautifier.beautify(source) : source
19
21
  result.strip.html_safe
@@ -22,23 +24,45 @@ module Lookbook
22
24
  end
23
25
 
24
26
  class Formatter < Rouge::Formatters::HTML
25
- def initialize(opts = {})
27
+ def initialize(**opts)
26
28
  @opts = opts
27
29
  @highlight_lines = opts[:highlight_lines].to_a || []
28
- @start_line = opts[:start_line] || 0
30
+ @start_line = opts[:start_line] || 1
31
+ @language = opts[:language]
29
32
  end
30
33
 
31
34
  def stream(tokens, &block)
32
- token_lines(tokens).each_with_index do |line_tokens, i|
33
- yield "<div class='line #{"highlighted-line" if @highlight_lines.include?(i + 1)}'>"
34
- yield "<span class='line-number'>#{@start_line + i}</span>" if @opts[:line_numbers]
35
- yield "<span class='line-content'>"
35
+ lines = token_lines(tokens)
36
+
37
+ yield "<div class='wrapper'>"
38
+
39
+ if @opts[:line_numbers]
40
+ yield "<div class='line-numbers'>"
41
+ lines.each.with_index do |line, i|
42
+ yield "<div class='line #{"highlighted" if highlighted?(i)}'><span class='line-number'>#{line_number(i)}</span></div>"
43
+ end
44
+ yield "</div>"
45
+ end
46
+
47
+ yield "<pre class='code highlight' data-lang='#{@language}'><code>"
48
+ lines.each.with_index do |line_tokens, i|
49
+ yield "<div class='line#{" highlighted" if highlighted?(i)}'>"
36
50
  line_tokens.each do |token, value|
37
51
  yield span(token, value)
38
52
  end
39
- yield "</span>"
40
53
  yield "</div>"
41
54
  end
55
+ yield "</code></pre>"
56
+
57
+ yield "</div>"
58
+ end
59
+
60
+ def highlighted?(i)
61
+ @highlight_lines.include?(i + 1)
62
+ end
63
+
64
+ def line_number(i)
65
+ @start_line + i
42
66
  end
43
67
  end
44
68
  end
@@ -1,23 +1,24 @@
1
1
  module Lookbook
2
- class Collection
3
- include Utils
2
+ class Collection < Entity
4
3
  include Enumerable
5
4
 
6
- attr_reader :path
7
5
  delegate :size, :each, to: :items
8
6
 
9
7
  def initialize(path = "", items = [])
8
+ @items = []
10
9
  if path.is_a?(Array)
11
- @items = path
10
+ items = path
12
11
  path = ""
13
- else
14
- @items = items
15
12
  end
13
+
16
14
  @path = path.delete_prefix("/").delete_suffix("/")
15
+ super(@path)
16
+
17
+ items.each { |item| add(item) }
17
18
  end
18
19
 
19
20
  def id
20
- generate_id(lookup_path || "root")
21
+ lookup_path.present? ? super : "root"
21
22
  end
22
23
 
23
24
  def name
@@ -28,26 +29,24 @@ module Lookbook
28
29
  name&.titleize
29
30
  end
30
31
 
31
- def lookup_path
32
- @lookup_path ||= to_lookup_path(@path)
33
- end
34
-
35
32
  def position
36
33
  @position ||= parse_position_prefix(basename).first
37
34
  end
38
35
 
39
- def hierarchy_depth
40
- @path ? @path.split("/").size : 0
41
- end
42
-
43
36
  def items
44
- @items.sort_by { |item| [item.hierarchy_depth, item.position, item.label] }
37
+ @items.sort_by { |item| [item.hierarchy_depth, item&.position, item.label] }
45
38
  end
46
39
 
47
40
  def visible_items
48
41
  reject { |i| i.hidden? }
49
42
  end
50
43
 
44
+ def non_empty_items
45
+ items.filter do |item|
46
+ !item.is_a?(Lookbook::Collection) || item.items.any?
47
+ end
48
+ end
49
+
51
50
  def add(item)
52
51
  @ordered_entities = nil
53
52
  @tree = nil
@@ -134,6 +133,10 @@ module Lookbook
134
133
  @tree
135
134
  end
136
135
 
136
+ def collapsible?
137
+ false
138
+ end
139
+
137
140
  def type
138
141
  :collection
139
142
  end
@@ -0,0 +1,125 @@
1
+ require "lookbook/markdown"
2
+
3
+ module Lookbook
4
+ class ConfigOptions < ActiveSupport::OrderedOptions
5
+ def initialize(**data)
6
+ super()
7
+ data.keys.each { |key| self[key.to_sym] = data[key] }
8
+ end
9
+
10
+ def [](key)
11
+ super(key.to_sym)
12
+ end
13
+
14
+ def []=(key, value)
15
+ super(key.to_sym, value)
16
+ end
17
+ end
18
+
19
+ class Config
20
+ def initialize
21
+ @store = ConfigOptions.new(**{
22
+ project_name: "Lookbook",
23
+ log_level: 2,
24
+ auto_refresh: true,
25
+
26
+ page_controller: "Lookbook::PageController",
27
+ page_route: "pages",
28
+ page_paths: ["test/components/docs"],
29
+ page_options: ConfigOptions.new,
30
+ markdown_options: ConfigOptions.new(**Markdown::DEFAULT_OPTIONS),
31
+
32
+ preview_paths: [],
33
+ preview_display_params: ConfigOptions.new,
34
+ preview_options: ConfigOptions.new,
35
+ preview_srcdoc: false,
36
+ sort_examples: true,
37
+
38
+ listen: Rails.env.development?,
39
+ listen_paths: [],
40
+ listen_use_polling: false,
41
+
42
+ cable_mount_path: "/lookbook-cable",
43
+ cable_logger: Lookbook.logger,
44
+
45
+ runtime_parsing: !Rails.env.production?,
46
+ parser_registry_path: "tmp/storage/.yardoc",
47
+
48
+ ui_theme: "indigo",
49
+ ui_theme_overrides: ConfigOptions.new,
50
+ experimental_features: false,
51
+ })
52
+ end
53
+
54
+ def ui_theme=(name)
55
+ name = name.to_s
56
+ if ["indigo", "zinc", "blue"].include?(name)
57
+ @store[:ui_theme] = name
58
+ else
59
+ Lookbook.logger.warn "'#{name}' is not a valid Lookbook theme. Falling back to default."
60
+ end
61
+ end
62
+
63
+ def ui_theme_overrides=(theme)
64
+ @store[:ui_theme_overrides] = ConfigOptions.new(theme)
65
+ end
66
+
67
+ def ui_theme_overrides(&block)
68
+ if block_given?
69
+ yield get(:ui_theme_overrides)
70
+ else
71
+ get(:ui_theme_overrides)
72
+ end
73
+ end
74
+
75
+ def [](key)
76
+ get(key.to_sym)
77
+ end
78
+
79
+ def []=(key, value)
80
+ @store[key.to_sym] = value
81
+ end
82
+
83
+ def to_h
84
+ @store.to_h
85
+ end
86
+
87
+ def to_json(*a)
88
+ to_h.to_json(*a)
89
+ end
90
+
91
+ protected
92
+
93
+ def get_project_name(name)
94
+ name == false ? nil : name
95
+ end
96
+
97
+ def normalize_paths(paths)
98
+ paths.map! { |path| absolute_path(path) }
99
+ paths.filter! { |path| Dir.exist?(path) }
100
+ paths
101
+ end
102
+
103
+ def absolute_path(path)
104
+ File.absolute_path(path.to_s, Rails.root)
105
+ end
106
+
107
+ alias_method :get_page_paths, :normalize_paths
108
+ alias_method :get_preview_paths, :normalize_paths
109
+ alias_method :get_listen_paths, :normalize_paths
110
+ alias_method :get_parser_registry_path, :absolute_path
111
+
112
+ def get(name)
113
+ getter_name = "get_#{name}".to_sym
114
+ respond_to?(getter_name, true) ? send(getter_name, @store[name]) : @store[name]
115
+ end
116
+
117
+ def set(name, *args)
118
+ @store.send(name, *args)
119
+ end
120
+
121
+ def method_missing(name, *args)
122
+ args.any? ? set(name, *args) : get(name)
123
+ end
124
+ end
125
+ end
@@ -4,68 +4,51 @@ require "action_cable/engine"
4
4
  require "listen"
5
5
 
6
6
  module Lookbook
7
+
8
+ autoload :Config, "lookbook/config"
9
+
7
10
  class << self
8
11
  def config
9
- @config ||= Engine.config.lookbook
12
+ @config ||= Config.new
10
13
  end
11
14
 
12
15
  def logger
13
- @logger ||= Rails.logger
16
+ @logger ||= Rails.env.development? ? Logger.new($stdout) : Rails.logger
14
17
  end
15
18
 
16
19
  def version
17
20
  Lookbook::VERSION
18
21
  end
22
+
23
+ def debug_data
24
+ {
25
+ version: version,
26
+ env: Rails.env.to_s,
27
+ config: config
28
+ }
29
+ end
30
+
31
+ def configure
32
+ yield(config)
33
+ end
19
34
  end
20
35
 
21
36
  class Engine < Rails::Engine
22
37
  isolate_namespace Lookbook
23
38
 
24
- config.lookbook = ActiveSupport::OrderedOptions.new
25
- config.lookbook.listen_paths ||= []
26
- config.lookbook.listen_extensions ||= []
27
- config.lookbook.preview_paths ||= []
28
- config.lookbook.page_paths ||= ["test/components/docs"]
29
-
30
- initializer "view_component.set_configs" do
31
- options = config.lookbook
32
- vc_options = config.view_component
33
-
34
- options.project_name ||= options.project_name == false ? nil : options.project_name || "Lookbook"
35
- options.auto_refresh = true if options.auto_refresh.nil?
36
- options.sort_examples = false if options.sort_examples.nil?
37
- options.debug = false unless options.debug == true
38
-
39
- options.preview_paths = options.preview_paths.map(&:to_s)
40
- options.preview_paths += vc_options.preview_paths
41
-
42
- options.page_paths = options.page_paths.map(&:to_s)
43
- options.page_controller = "Lookbook::PageController" if options.page_controller.nil?
44
- options.page_route ||= "pages"
45
- options.page_options ||= {}.with_indifferent_access
46
-
47
- options.markdown_options = Markdown::DEFAULT_OPTIONS.merge(options.markdown_options || {})
48
-
49
- options.preview_controller = vc_options.preview_controller if options.preview_controller.nil?
50
- options.preview_srcdoc = false if options.preview_srcdoc.nil?
51
- options.preview_display_params ||= {}.with_indifferent_access
52
-
53
- options.listen = Rails.env.development? if options.listen.nil?
54
- options.listen_paths = options.listen_paths.map(&:to_s)
55
- options.listen_paths += options.preview_paths
56
- options.listen_paths << (vc_options.view_component_path || Rails.root.join("app/components"))
57
- options.listen_paths.select! { |path| Dir.exist? path }
58
-
59
- options.listen_extensions += ["rb", "html.*"]
60
- options.listen_extensions.uniq!
39
+ config.lookbook = Lookbook.config
40
+ config.autoload_paths << File.expand_path(Lookbook::Engine.root.join("app/components"))
61
41
 
62
- options.cable_mount_path ||= "/lookbook-cable"
63
- options.cable_logger ||= Rails.logger
42
+ initializer "lookbook.viewcomponent.config" do
43
+ config.lookbook.preview_paths += config.view_component.preview_paths
44
+ config.lookbook.preview_controller ||= config.view_component.preview_controller
64
45
 
65
- options.runtime_parsing = !Rails.env.production? if options.runtime_parsing.nil?
66
- options.parser_registry_path ||= Rails.root.join("tmp/storage/.yardoc")
46
+ config.lookbook.listen_paths += config.lookbook.preview_paths
47
+ config.lookbook.listen_paths << (config.view_component.view_component_path.presence || "app/components")
48
+ end
67
49
 
68
- options.experimental_features = false unless options.experimental_features.present?
50
+ initializer "lookbook.logging.development" do
51
+ Lookbook.logger.level = Lookbook.config.log_level if Rails.env.development?
69
52
  end
70
53
 
71
54
  initializer "lookbook.parser.tags" do
@@ -84,30 +67,29 @@ module Lookbook
84
67
  @preview_controller.include(Lookbook::PreviewController)
85
68
 
86
69
  if config.lookbook.listen
87
- @preview_listener = Listen.to(*config.lookbook.listen_paths, only: /\.(#{config.lookbook.listen_extensions.join("|")})$/) do |modified, added, removed|
70
+ Listen.logger = Lookbook.logger
71
+ preview_listener = Listen.to(
72
+ *config.lookbook.listen_paths,
73
+ only: /\.(rb|html.*)$/,
74
+ force_polling: Lookbook.config.listen_use_polling
75
+ ) do |modified, added, removed|
88
76
  begin
89
77
  parser.parse
90
78
  rescue
91
79
  end
92
80
  Lookbook::Preview.clear_cache
93
- Lookbook::Engine.websocket&.broadcast("reload", {
94
- modified: modified,
95
- removed: removed,
96
- added: added
97
- })
81
+ Lookbook::Engine.websocket&.broadcast("reload", {})
98
82
  end
99
- @preview_listener.start
100
-
101
- if Lookbook::Features.enabled?(:pages)
102
- @page_listener = Listen.to(*config.lookbook.page_paths.select { |dir| Dir.exist? dir }, only: /\.(html.*|md.*)$/) do |modified, added, removed|
103
- Lookbook::Engine.websocket&.broadcast("reload", {
104
- modified: modified,
105
- removed: removed,
106
- added: added
107
- })
108
- end
109
- @page_listener.start
83
+ Lookbook::Engine.register_listener(preview_listener)
84
+
85
+ page_listener = Listen.to(
86
+ *config.lookbook.page_paths,
87
+ only: /\.(html.*|md.*)$/,
88
+ force_polling: Lookbook.config.listen_use_polling
89
+ ) do |modified, added, removed|
90
+ Lookbook::Engine.websocket&.broadcast("reload", {})
110
91
  end
92
+ Lookbook::Engine.register_listener(page_listener)
111
93
  end
112
94
 
113
95
  if config.lookbook.runtime_parsing
@@ -124,13 +106,16 @@ module Lookbook
124
106
  end
125
107
 
126
108
  at_exit do
127
- @preview_listener&.stop
128
- @page_listener&.stop
109
+ if config.lookbook.listen
110
+ Lookbook.logger.debug "Stopping listeners"
111
+ Lookbook::Engine.listeners.each do |listener|
112
+ listener.stop
113
+ end
114
+ end
129
115
  end
130
116
 
131
117
  class << self
132
118
  def websocket
133
- return @websocket unless @websocket.nil?
134
119
  if config.lookbook.auto_refresh
135
120
  cable = ActionCable::Server::Configuration.new
136
121
  cable.cable = {adapter: "async"}.with_indifferent_access
@@ -141,9 +126,9 @@ module Lookbook
141
126
  @websocket ||= if Rails.version.to_f >= 6.0
142
127
  ActionCable::Server::Base.new(config: cable)
143
128
  else
144
- @websocket ||= ActionCable::Server::Base.new
145
- @websocket.config = cable
146
- @websocket
129
+ websocket ||= ActionCable::Server::Base.new
130
+ websocket.config = cable
131
+ websocket
147
132
  end
148
133
  end
149
134
  end
@@ -160,6 +145,23 @@ module Lookbook
160
145
  @parser ||= Lookbook::Parser.new(config.lookbook.preview_paths, config.lookbook.parser_registry_path)
161
146
  end
162
147
 
148
+ def log_level
149
+ Lookbook.logger.level
150
+ end
151
+
152
+ def app_name
153
+ Rails.application.class.module_parent_name.underscore
154
+ end
155
+
156
+ def register_listener(listener)
157
+ listener.start
158
+ listeners << listener
159
+ end
160
+
161
+ def listeners
162
+ @listeners ||= []
163
+ end
164
+
163
165
  attr_reader :preview_controller
164
166
  end
165
167
  end