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,87 +1,44 @@
1
1
  module Lookbook
2
2
  class Nav::Item::Component < Lookbook::BaseComponent
3
- ICONS = {
4
- page: :file,
5
- page_collection: :folder,
6
- preview_collection: :folder,
7
- preview: :layers,
8
- example: :eye,
9
- group: :eye,
10
- collection: :folder
11
- }.freeze
3
+ delegate :label, :depth, to: :node
12
4
 
13
- delegate :label, to: :@item
5
+ attr_reader :node, :nav_id
14
6
 
15
- def initialize(
16
- item,
17
- nav_id:,
18
- depth: 1,
19
- collapse_singles: false,
20
- **html_attrs
21
- )
7
+ def initialize(node, nav_id:, **html_attrs)
8
+ @node = node
22
9
  @nav_id = nav_id
23
- @item = item
24
- @depth = depth
25
- @collapse_singles = collapse_singles
26
10
  super(**html_attrs)
27
11
  end
28
12
 
29
13
  def id
30
- "#{@nav_id}-#{@item.id}"
14
+ "#{nav_id}-#{node.id}"
31
15
  end
32
16
 
33
17
  def left_pad
34
- ((@depth - 1) * 12) + 24
35
- end
36
-
37
- def href
38
- if collapsed?
39
- item.url_path
40
- elsif !collection?
41
- item.url_path
42
- end
18
+ depth * 12
43
19
  end
44
20
 
45
21
  def children
46
- @children ||= if collection? && !collapsed?
47
- item.non_empty_items.map do |item|
48
- lookbook_render Lookbook::Nav::Item::Component.new item,
49
- nav_id: @nav_id,
50
- depth: (@depth + 1),
51
- collapse_singles: @collapse_singles
52
- end
53
- else
54
- []
55
- end
56
- end
57
-
58
- def item
59
- collapsed? ? @item.first : @item
22
+ @children ||= node.sort.map { |node| render_item(node) }
60
23
  end
61
24
 
62
25
  def nav_icon
63
- ICONS[@item.type] || :file
64
- end
65
-
66
- def collection?
67
- @item.is_a? Lookbook::Collection
26
+ :folder
68
27
  end
69
28
 
70
29
  def children?
71
- children.any? if collection? && !collapsed?
30
+ children.any?
72
31
  end
73
32
 
74
- def collapsed?
75
- @collapse_singles == true && collection? && @item.collapsible? && @item.one?
33
+ def render_item(node)
34
+ item_class = (node.type == :directory) ? Nav::Directory::Component : Nav::Entity::Component
35
+ lookbook_render item_class.new node, nav_id: nav_id
76
36
  end
77
37
 
78
38
  protected
79
39
 
80
40
  def alpine_data
81
- alpine_encode({
82
- id: @item.id,
83
- matchers: item.is_a?(Lookbook::Collection) ? nil : item.matchers
84
- })
41
+ alpine_encode({id: node.id, matchers: []})
85
42
  end
86
43
 
87
44
  def alpine_component
@@ -1,5 +1,5 @@
1
- <%= render_component_tag class: "p-4 overflow-hidden" do %>
2
- <div class="bg-white border border-lookbook-divider rounded-md overflow-x-auto">
1
+ <%= render_component_tag class: "overflow-hidden" do %>
2
+ <div class="bg-lookbook-params-editor-bg border border-lookbook-divider rounded-md overflow-x-auto">
3
3
  <table class="border-collapse w-full" :class="narrow && 'linear'">
4
4
  <thead>
5
5
  <tr>
@@ -1,20 +1,13 @@
1
1
  module Lookbook
2
2
  module Params
3
3
  class Editor::Component < Lookbook::BaseComponent
4
- renders_many :fields, ->(input:, description: nil, **attrs) do
4
+ renders_many :fields, ->(**attrs) do
5
5
  @field_count += 1
6
- @descriptions = true if description.present?
7
- input_config = @inputs[input.tr("-", "_").to_sym]
8
- Lookbook::Params::Field::Component.new(input: input,
9
- description: description,
10
- index: @field_count,
11
- config: input_config, **attrs)
6
+ Lookbook::Params::Field::Component.new(**attrs, index: @field_count)
12
7
  end
13
8
 
14
- def initialize(inputs: nil, **html_attrs)
15
- @inputs = inputs.to_h
9
+ def initialize(**html_attrs)
16
10
  @field_count = -1
17
- @descriptions = false
18
11
  @@input_styles = {}
19
12
  super(**html_attrs)
20
13
  end
@@ -3,7 +3,7 @@
3
3
  table:not(.linear) {
4
4
  thead {
5
5
  tr {
6
- @apply border-b border-gray-300;
6
+ @apply border-b border-lookbook-divider;
7
7
  }
8
8
 
9
9
  th {
@@ -21,7 +21,7 @@
21
21
 
22
22
  tbody {
23
23
  tr + tr td {
24
- @apply border-t border-gray-200;
24
+ @apply border-t border-lookbook-divider;
25
25
  }
26
26
 
27
27
  td {
@@ -53,7 +53,7 @@
53
53
  }
54
54
 
55
55
  tr:not(:last-child) {
56
- @apply border-b border-gray-200;
56
+ @apply border-b border-lookbook-divider;
57
57
  }
58
58
 
59
59
  tr {
@@ -1,27 +1,27 @@
1
1
  <%= render_component_tag :tr, "@keydown.stop": true do %>
2
2
  <td class="param-label">
3
- <label for="param-<%= @name %>">
4
- <span class="mr-0.5"><%== @label %></span>
5
- <% if hint? %>
3
+ <label for="param-<%= param.name %>">
4
+ <span class="mr-0.5"><%== param.label %></span>
5
+ <% if param.hint.present? %>
6
6
  <span x-data="tooltipComponent" class="inline-block cursor-help relative top-[2px]">
7
7
  <%= icon :help_circle, size: 3.5, class: "opacity-40 hover:opacity-100 transition" %>
8
8
  <div class="hidden" x-ref="tooltip">
9
- <%= @hint %>
9
+ <%= param.hint %>
10
10
  </div>
11
11
  </span>
12
12
  <% end %>
13
13
  </label>
14
14
  </td>
15
- <td class="param-description <%= "param-description-empty" unless description? %>">
16
- <% if description? %>
17
- <p class="opacity-70"><%= @description %></p>
15
+ <td class="param-description <%= "param-description-empty" unless param.description %>">
16
+ <% if param.description %>
17
+ <p class="opacity-70"><%= param.description %></p>
18
18
  <% else %>
19
19
  <p class="italic opacity-40">&mdash;</p>
20
20
  <% end %>
21
21
  </td>
22
22
  <td class="param-input">
23
23
  <div class="param-input-wrapper">
24
- <%= input %>
24
+ <%= rendered_input %>
25
25
  </div>
26
26
  </td>
27
27
  <% end %>
@@ -1,92 +1,41 @@
1
1
  module Lookbook
2
2
  module Params
3
3
  class Field::Component < Lookbook::BaseComponent
4
- def initialize(name:, input:, index:, label: nil, hint: nil, description: nil, value: nil, value_default: nil, value_type: nil, input_options: {}, config: nil, **html_attrs)
5
- @input_name = input
6
- @name = name
7
- @label = label || name.titleize
8
- @hint = hint
9
- @description = description
10
- @value = value
4
+ attr_reader :param, :rendered_input
5
+
6
+ def initialize(param:, index:, **html_attrs)
7
+ @param = param
11
8
  @index = index
12
- @input_options = input_options
13
- @value_default = value_default
14
- @value_type = value_type
15
- @config = config || {}
16
9
  @rendered_input = nil
17
10
  super(**html_attrs)
18
11
  end
19
12
 
20
- def hint?
21
- @hint.present?
22
- end
23
-
24
- def description?
25
- @description.present?
26
- end
27
-
28
- def input
29
- @rendered_input
30
- end
31
-
32
13
  def before_render
33
- tpl = TemplateParser.new(render_input)
34
- Editor::Component.add_styles(@input_name, tpl.styles)
14
+ styles, html = StylesExtractor.call(render_input)
15
+ Editor::Component.add_styles(param.input, styles)
35
16
 
17
+ escaped_value = json_escape(param.value.to_s)
36
18
  wrapper_attrs = {
37
- data: {"param-input": @input_name},
38
- "x-data": "paramsInputComponent({name: '#{@name}', value: #{escaped_value}})"
19
+ data: {"param-input": param.input},
20
+ "x-data": "paramsInputComponent({name: '#{param.name}', value: '#{escaped_value}'})"
39
21
  }
40
-
41
- @rendered_input = tag.div(**wrapper_attrs) do
42
- tpl.content
43
- end
22
+ @rendered_input = tag.div(**wrapper_attrs) { html.html_safe }
44
23
  end
45
24
 
46
25
  protected
47
26
 
48
- def input_error(error)
49
- tag.div error, class: "p-2 text-red-500 italic"
50
- end
51
-
52
- def value
53
- val = @value.presence || @value_default
54
- @value_type.downcase == "boolean" ? val == "true" || val == true : val
55
- end
56
-
57
- def escaped_value
58
- json_escape(value.to_json)
59
- end
60
-
61
- def input_options
62
- config_options = @config.fetch(:opts, {})
63
- opts = config_options.merge(@input_options).symbolize_keys
64
- opts[:id] = "param-#{@name}"
65
- opts
66
- end
67
-
68
- def render_props
69
- {
70
- name: @name,
71
- input: @input_name,
72
- value: value,
73
- value_type: @value_type,
74
- value_default: @value_default,
75
- input_options: input_options.except(:choices),
76
- choices: input_options[:choices]
77
- }
78
- end
79
-
80
27
  def render_input
81
- target = @config[:partial]
82
- if target
83
- render(target, **render_props)
84
- else
85
- input_error "No param input defined for input type '#{@input_name}'."
86
- end
87
- rescue ::ActionView::MissingTemplate => exception
88
- Lookbook.logger.error exception
89
- input_error "Param input partial '#{@config[:partial]}' could not be found."
28
+ input_options = param.input_options.to_h
29
+ input_options[:id] = "param-#{param.name}"
30
+
31
+ render(param.input_partial,
32
+ name: param.name,
33
+ input: param.input,
34
+ value: param.value.to_s,
35
+ value_type: param.value_type,
36
+ value_default: param.value_default,
37
+ input_options: input_options.except(:choices, :opts),
38
+ choices: input_options[:choices])
90
39
  end
91
40
 
92
41
  def alpine_component
@@ -2,7 +2,7 @@
2
2
  <% panes.each.with_index(1) do |pane, i| %>
3
3
  <%= pane %>
4
4
  <% if i < panes.size %>
5
- <div class="bg-lookbook-divider relative" x-init="registerGutter">
5
+ <div class="bg-lookbook-divider relative z-50" x-init="registerGutter">
6
6
  <div class="absolute z-10 bg-transparent hover:bg-lookbook-draggable-hint transition-all" :class="{
7
7
  'w-[9px] h-full -translate-x-1/2 cursor-[col-resize]': vertical,
8
8
  'h-[9px] w-full -translate-y-1/2 cursor-[row-resize]': horizontal
@@ -13,7 +13,7 @@
13
13
  <%= lookbook_render :button, icon: :menu, class: "-ml-3", "x-show": "visibleTabsCount === 0" %>
14
14
  </div>
15
15
  <div class="hidden">
16
- <div x-ref="tabsDropdown" data-cloak>
16
+ <div x-ref="tabsDropdown" x-cloak>
17
17
  <%= safe_join(dropdown_tabs) %>
18
18
  </div>
19
19
  </div>
@@ -33,6 +33,10 @@ export default function tabsComponent(store) {
33
33
 
34
34
  init() {
35
35
  this.$nextTick(() => {
36
+ if (this.$root.parentElement.offsetWidth === this.$root.offsetWidth) {
37
+ this.visibleTabsCount = this.tabs.length;
38
+ }
39
+
36
40
  dropdown = tippy(this.$refs.dropdownTrigger, {
37
41
  content: this.$refs.tabsDropdown,
38
42
  theme: "menu",
@@ -19,7 +19,7 @@ module Lookbook
19
19
  end
20
20
 
21
21
  def self.escape_attribute_key
22
- @escape_attribute_key ||= Gem::Version.new(Rails.version) < Gem::Version.new("6.1.5.1") ? :escape_attributes : :escape
22
+ @escape_attribute_key ||= (Gem::Version.new(Rails.version) < Gem::Version.new("6.1.5.1")) ? :escape_attributes : :escape
23
23
  end
24
24
  end
25
25
  end
@@ -4,7 +4,7 @@
4
4
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3E%3Cg fill='%23f3f3f3' fill-opacity='1'%3E%3Cpath fill-rule='evenodd' d='M0 0h4v4H0V0zm4 4h4v4H4V4z'/%3E%3C/g%3E%3C/svg%3E");
5
5
 
6
6
  .resize-handle {
7
- @apply flex items-center justify-center h-full w-full border-lookbook-divider bg-lookbook-viewport-handle hover:bg-lookbook-draggable-hint hover:bg-opacity-20;
7
+ @apply flex items-center justify-center h-full w-full border-lookbook-divider bg-lookbook-viewport-handle hover:bg-lookbook-viewport-handle-hover hover:bg-opacity-20;
8
8
  @apply text-lookbook-viewport-handle-icon-stroke hover:text-lookbook-viewport-handle-icon-stroke-hover transition select-none touch-none;
9
9
  }
10
10
  }
@@ -27,7 +27,7 @@
27
27
  style="width: calc(100% + 2px); height: calc(100% + 1px); <%= "max-height: #{@max_height}px;" if @max_height.present? %>">
28
28
  <iframe seamless<%= " id=#{@iframe_id}" if @iframe_id.present? %>
29
29
  x-ref="iframe"
30
- class="h-full w-full border border-lookbook-divider"
30
+ class="bg-lookbook-page-bg h-full w-full border border-lookbook-divider"
31
31
  :class="{ 'pointer-events-none': reflowing }"
32
32
  style="<%= "max-height: #{@max_height}px;" if @max_height.present? %>"
33
33
  src="<%= @src %>"
@@ -10,7 +10,7 @@ module Lookbook
10
10
  end
11
11
 
12
12
  def generate_id(*args)
13
- args.map { |args| args.delete_prefix("/").tr("&?=/_\-", "-") }.join("-")
13
+ args.map { |args| args.delete_prefix("/").tr("&?=/_-", "-") }.join("-")
14
14
  end
15
15
 
16
16
  protected
@@ -0,0 +1,131 @@
1
+ module Lookbook
2
+ module TargetableConcern
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ before_action :lookup_entities, only: [:show]
7
+ before_action :set_title
8
+ before_action :set_display_options
9
+ before_action :set_params
10
+ end
11
+
12
+ def set_title
13
+ @title = @target.present? ? [@target&.label, @preview&.label].compact.join(" :: ") : "Not found"
14
+ end
15
+
16
+ def lookup_entities
17
+ @target = Lookbook.previews.find_example_by_path(params[:path])
18
+ if @target.present?
19
+ @preview = @target.preview
20
+ if params[:path] == @preview&.path
21
+ redirect_to lookbook_inspect_path("#{params[:path]}/#{@preview.default_example.name}", params.permit!)
22
+ end
23
+ else
24
+ @preview = Lookbook.previews.find_by_path(params[:path])
25
+ if @preview.present?
26
+ default_example = @preview.default_example
27
+ redirect_to lookbook_inspect_path(default_example.path, params.permit!) if default_example
28
+ else
29
+ @preview = Lookbook.previews.find_by_path(path_segments.slice(0, path_segments.size - 1).join("/"))
30
+ end
31
+ end
32
+ end
33
+
34
+ def set_display_options
35
+ @dynamic_display_options = []
36
+ @static_display_options = []
37
+
38
+ if @target.present?
39
+ opts = @target.display_options
40
+ @dynamic_display_options = opts.select { _2.is_a?(Array) || _2.is_a?(Hash) }
41
+ @static_display_options = opts.except(*@dynamic_display_options.keys)
42
+
43
+ if params[:_display]
44
+ display_params = SearchParamParser.call(params[:_display])
45
+ display_params.each do |name, value|
46
+ if @dynamic_display_options.key?(name)
47
+ cookies["lookbook-display-#{name}"] = value
48
+ end
49
+ end
50
+ end
51
+
52
+ @dynamic_display_options.each do |name, opts|
53
+ choices = opts.is_a?(Hash) ? opts[:choices].to_a : opts
54
+ @static_display_options[name] ||= cookies.fetch("lookbook-display-#{name}", choices.first)
55
+ end
56
+
57
+ unless params[:_display]
58
+ display_params = @dynamic_display_options.map do |name, opts|
59
+ [name, @static_display_options[name]]
60
+ end.to_h
61
+ request.query_parameters[:_display] = SearchParamBuilder.call(display_params)
62
+ end
63
+ end
64
+ end
65
+
66
+ def set_params
67
+ @params = []
68
+
69
+ if @target
70
+ @params = @target.tags("param").map do |param_tag|
71
+ Param.from_tag(
72
+ param_tag,
73
+ value: preview_controller.params[param_tag.name]
74
+ )
75
+ end
76
+
77
+ # cast known param values to correct type
78
+ @params.each do |param|
79
+ if preview_controller.params.key?(param.name)
80
+ preview_controller.params[param.name] = param.cast_value
81
+ end
82
+ end
83
+
84
+ # set display and data params for use in preview layouts
85
+ preview_controller.params[:lookbook] = {
86
+ display: @static_display_options,
87
+ data: Lookbook.data
88
+ }
89
+ end
90
+
91
+ preview_controller.params.permit!
92
+ end
93
+
94
+ def inspector_data
95
+ return @inspector_data if @inspector_data.present?
96
+
97
+ rendered_examples = @target.examples.map do |example|
98
+ output = preview_controller.process(:render_example_to_string, @preview, example.name)
99
+ RenderedExample.new(example, output, preview_controller.params)
100
+ end
101
+
102
+ @inspector_data ||= Lookbook::Store.new({
103
+ context: Store.new({params: @params, path: params[:path]}),
104
+ preview: @preview,
105
+ examples: rendered_examples,
106
+ target: @target,
107
+ data: Lookbook.data,
108
+ app: Lookbook
109
+ })
110
+ end
111
+
112
+ def show_404(layout: nil)
113
+ locals = if @preview
114
+ {
115
+ message: "Example not found",
116
+ description: "The '#{@preview.label}' preview does not have an example named '#{path_segments.last}'."
117
+ }
118
+ else
119
+ {
120
+ message: "Not found",
121
+ description: "Looked for '#{params[:path]}'.<br>The preview may have been renamed or deleted."
122
+ }
123
+ end
124
+ render_in_layout "lookbook/404", layout: layout, **locals
125
+ end
126
+
127
+ def path_segments
128
+ params[:path].split("/")
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,13 @@
1
+ module Lookbook
2
+ module WithPreviewControllerConcern
3
+ extend ActiveSupport::Concern
4
+
5
+ def preview_controller
6
+ return @preview_controller if @preview_controller
7
+ controller = Lookbook::Engine.preview_controller.new
8
+ controller.request = request
9
+ controller.response = response
10
+ @preview_controller ||= controller
11
+ end
12
+ end
13
+ end
@@ -1,8 +1,6 @@
1
1
  module Lookbook
2
2
  class ApplicationController < ActionController::Base
3
- if respond_to?(:content_security_policy)
4
- content_security_policy false, if: -> { Rails.env.development? }
5
- end
3
+ content_security_policy(false) if respond_to?(:content_security_policy)
6
4
 
7
5
  protect_from_forgery with: :exception
8
6
 
@@ -11,16 +9,16 @@ module Lookbook
11
9
  helper Lookbook::ComponentHelper
12
10
 
13
11
  before_action :generate_theme_overrides
14
- before_action :assign_collections
12
+ before_action :assign_instance_vars
15
13
 
16
14
  def self.controller_path
17
15
  "lookbook"
18
16
  end
19
17
 
20
18
  def index
21
- landing = Lookbook.pages.find(&:landing) || Lookbook.pages.first
19
+ landing = Lookbook.pages.find(&:landing?) || Lookbook.pages.first
22
20
  if landing.present?
23
- redirect_to lookbook_page_path(landing.lookup_path)
21
+ redirect_to lookbook_page_path(landing.path)
24
22
  else
25
23
  render "lookbook/index"
26
24
  end
@@ -32,9 +30,11 @@ module Lookbook
32
30
  @theme_overrides ||= Lookbook.theme.to_css
33
31
  end
34
32
 
35
- def assign_collections
36
- @previews = Preview.all
37
- @pages = Page.all
33
+ def assign_instance_vars
34
+ @previews = Lookbook.previews
35
+ @pages = Lookbook.pages
36
+ @config = Lookbook.config
37
+ @engine = Lookbook.engine
38
38
  end
39
39
 
40
40
  def feature_enabled?(feature)
@@ -45,5 +45,17 @@ module Lookbook
45
45
  @error = locals[:error]
46
46
  render path, layout: layout.presence || (params[:lookbook_embed] ? "lookbook/basic" : "lookbook/application"), locals: locals
47
47
  end
48
+
49
+ def prettify_error(exception)
50
+ error_params = {}
51
+ if exception.is_a?(ViewComponent::PreviewTemplateError)
52
+ error_params = {
53
+ file_path: @preview&.file_path,
54
+ line_number: 0,
55
+ source_code: @target&.source
56
+ }
57
+ end
58
+ Lookbook::Error.new(exception, **error_params)
59
+ end
48
60
  end
49
61
  end
@@ -0,0 +1,45 @@
1
+ module Lookbook
2
+ class InspectorController < ApplicationController
3
+ include TargetableConcern
4
+ include WithPreviewControllerConcern
5
+
6
+ layout "lookbook/inspector"
7
+ helper Lookbook::PreviewHelper
8
+
9
+ def self.controller_path
10
+ "lookbook/inspector"
11
+ end
12
+
13
+ def show
14
+ if @target
15
+ begin
16
+ @main_panels = main_panels
17
+ @drawer_panels = drawer_panels
18
+ rescue => exception
19
+ render_in_layout "lookbook/error", layout: "lookbook/inspector", error: prettify_error(exception)
20
+ end
21
+ else
22
+ show_404
23
+ end
24
+ end
25
+
26
+ def show_legacy
27
+ Lookbook.logger.warn("Legacy URL path detected. These paths are deprecated and will be removed in a future version")
28
+ redirect_to lookbook_inspect_path params[:path]
29
+ end
30
+
31
+ private
32
+
33
+ def main_panels
34
+ Engine.panels.in_group(:main).map do |config|
35
+ PanelStore.resolve_config(config, inspector_data)
36
+ end
37
+ end
38
+
39
+ def drawer_panels
40
+ Engine.panels.in_group(:drawer).map do |config|
41
+ PanelStore.resolve_config(config, inspector_data)
42
+ end
43
+ end
44
+ end
45
+ end
@@ -11,15 +11,19 @@ module Lookbook
11
11
  def render_page(page, locals = {})
12
12
  @page = page
13
13
  @pages = Lookbook.pages
14
- @next_page = @pages.find_next(@page)
15
- @previous_page = @pages.find_previous(@page)
16
- content = render_to_string inline: @page.content, locals: {
17
- page: @page,
18
- next_page: @next_page,
19
- previous_page: @previous_page,
20
- pages: @pages
21
- }
22
- @page.markdown? ? Lookbook::Markdown.render(content) : content
14
+ @next_page = @pages.next(@page)
15
+ @previous_page = @pages.previous(@page)
16
+
17
+ content = ActionViewAnnotationsHandler.call(disable_annotations: true) do
18
+ render_to_string inline: @page.content, locals: {
19
+ page: @page,
20
+ next_page: @next_page,
21
+ previous_page: @previous_page,
22
+ pages: @pages
23
+ }
24
+ end
25
+
26
+ @page.markdown? ? MarkdownRenderer.call(content, Lookbook.config.markdown_options) : content
23
27
  end
24
28
  end
25
29
  end