lookbook 1.0.8 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/lookbook/css/lookbook.css +76 -2
  3. data/app/assets/lookbook/js/app.js +20 -2
  4. data/app/{components/lookbook/params_editor/field/component.js → assets/lookbook/js/components/params_input.js} +1 -14
  5. data/app/assets/lookbook/js/lookbook.js +2 -1
  6. data/app/components/lookbook/base_component.rb +2 -3
  7. data/app/components/lookbook/button/component.html.erb +2 -2
  8. data/app/components/lookbook/button/component.rb +6 -1
  9. data/app/components/lookbook/button_group/component.rb +3 -4
  10. data/app/components/lookbook/copy_button/component.html.erb +1 -1
  11. data/app/components/lookbook/debug_menu/component.html.erb +51 -0
  12. data/app/components/lookbook/debug_menu/component.rb +18 -0
  13. data/app/components/lookbook/embed/component.html.erb +3 -3
  14. data/app/components/lookbook/header/component.html.erb +19 -44
  15. data/app/components/lookbook/inspector_panel/component.html.erb +5 -3
  16. data/app/components/lookbook/inspector_panel/component.rb +6 -18
  17. data/app/components/lookbook/nav/component.js +4 -0
  18. data/app/components/lookbook/nav/component.rb +1 -1
  19. data/app/components/lookbook/nav/item/component.html.erb +4 -4
  20. data/app/components/lookbook/nav/item/component.rb +1 -1
  21. data/app/components/lookbook/page_tabs/component.html.erb +4 -4
  22. data/app/components/lookbook/page_tabs/component.rb +1 -1
  23. data/app/components/lookbook/params/editor/component.html.erb +21 -0
  24. data/app/components/lookbook/{params_editor → params/editor}/component.js +1 -1
  25. data/app/components/lookbook/params/editor/component.rb +40 -0
  26. data/app/components/lookbook/params/field/component.css +76 -0
  27. data/app/components/lookbook/params/field/component.html.erb +27 -0
  28. data/app/components/lookbook/params/field/component.js +7 -0
  29. data/app/components/lookbook/params/field/component.rb +101 -0
  30. data/app/components/lookbook/tabs/component.html.erb +2 -2
  31. data/app/components/lookbook/tag_component.rb +1 -0
  32. data/app/components/lookbook/viewport/component.css +1 -1
  33. data/app/components/lookbook/viewport/component.html.erb +11 -0
  34. data/app/controllers/lookbook/previews_controller.rb +1 -1
  35. data/app/helpers/lookbook/component_helper.rb +45 -26
  36. data/app/helpers/lookbook/page_helper.rb +1 -1
  37. data/app/views/layouts/lookbook/application.html.erb +18 -4
  38. data/app/views/layouts/lookbook/page.html.erb +4 -4
  39. data/app/views/layouts/lookbook/shell.html.erb +4 -4
  40. data/app/views/layouts/lookbook/skeleton.html.erb +0 -6
  41. data/app/views/lookbook/error.html.erb +1 -1
  42. data/app/views/lookbook/pages/show.html.erb +2 -2
  43. data/app/views/lookbook/previews/inputs/_color.html.erb +5 -0
  44. data/app/views/lookbook/previews/inputs/_range.html.erb +15 -0
  45. data/app/views/lookbook/previews/inputs/_select.html.erb +5 -0
  46. data/app/views/lookbook/previews/inputs/_text.html.erb +5 -0
  47. data/app/views/lookbook/previews/inputs/_textarea.html.erb +5 -0
  48. data/app/views/lookbook/previews/inputs/_toggle.html.erb +20 -0
  49. data/app/views/lookbook/previews/panels/_content.html.erb +2 -2
  50. data/app/views/lookbook/previews/panels/_notes.html.erb +2 -2
  51. data/app/views/lookbook/previews/panels/_output.html.erb +1 -1
  52. data/app/views/lookbook/previews/panels/_params.html.erb +3 -3
  53. data/app/views/lookbook/previews/panels/_preview.html.erb +1 -1
  54. data/app/views/lookbook/previews/panels/_source.html.erb +2 -2
  55. data/app/views/lookbook/previews/show.html.erb +13 -19
  56. data/lib/lookbook/config.rb +16 -1
  57. data/lib/lookbook/engine.rb +16 -6
  58. data/lib/lookbook/markdown.rb +1 -1
  59. data/lib/lookbook/panels.rb +14 -4
  60. data/lib/lookbook/params.rb +66 -35
  61. data/lib/lookbook/parser.rb +1 -0
  62. data/lib/lookbook/preview.rb +10 -4
  63. data/lib/lookbook/preview_controller.rb +7 -19
  64. data/lib/lookbook/preview_example.rb +1 -1
  65. data/lib/lookbook/source_inspector.rb +10 -4
  66. data/lib/lookbook/tag.rb +13 -3
  67. data/lib/lookbook/tag_options.rb +111 -0
  68. data/lib/lookbook/tags.rb +6 -2
  69. data/lib/lookbook/template_parser.rb +72 -0
  70. data/lib/lookbook/theme.rb +1 -1
  71. data/lib/lookbook/utils.rb +23 -0
  72. data/lib/lookbook/version.rb +1 -1
  73. data/lib/lookbook.rb +2 -0
  74. data/public/lookbook-assets/css/lookbook.css +369 -126
  75. data/public/lookbook-assets/css/lookbook.css.map +1 -1
  76. data/public/lookbook-assets/js/embed.js +13 -13
  77. data/public/lookbook-assets/js/embed.js.map +1 -1
  78. data/public/lookbook-assets/js/lookbook.js +706 -621
  79. data/public/lookbook-assets/js/lookbook.js.map +1 -1
  80. metadata +42 -10
  81. data/app/components/lookbook/params_editor/component.html.erb +0 -3
  82. data/app/components/lookbook/params_editor/component.rb +0 -11
  83. data/app/components/lookbook/params_editor/field/component.html.erb +0 -49
  84. data/app/components/lookbook/params_editor/field/component.rb +0 -44
@@ -6,13 +6,13 @@
6
6
  <h6 class="italic font-mono mb-4 opacity-40">
7
7
  # <%= item.label %>
8
8
  </h6>
9
- <%= render_component :prose, content: item.notes %>
9
+ <%= lookbook_render :prose, content: item.notes %>
10
10
  </div>
11
11
  <% end %>
12
12
  </div>
13
13
  <% else %>
14
14
  <div class="px-4 py-6 bg-lookbook-prose-bg w-full h-full">
15
- <%= render_component :prose do %>
15
+ <%= lookbook_render :prose do %>
16
16
  <%== items.any? ? items.first.notes : "<em class='opacity-50'>No notes provided.</em>" %>
17
17
  <% end %>
18
18
  </div>
@@ -1,4 +1,4 @@
1
- <%= render_component :code, line_numbers: true, full_height: true do -%>
1
+ <%= lookbook_render :code, line_numbers: true, full_height: true do -%>
2
2
  <% if examples.many? -%>
3
3
  <% examples.each do |example| -%><%== "<!-- #{example.label} -->\n#{beautify(example.output)}\n\n" -%><% end %>
4
4
  <%- else -%>
@@ -1,12 +1,12 @@
1
1
  <% if preview.params.none? %>
2
2
  <div class="p-4 w-full h-full bg-lookbook-prose-bg">
3
- <%= render_component :prose do %>
3
+ <%= lookbook_render :prose do %>
4
4
  <em class='opacity-50'>No params configured.</em>
5
5
  <% end %>
6
6
  </div>
7
7
  <% else %>
8
- <div class="py-3 h-full">
9
- <%= render_component :params_editor do |editor| %>
8
+ <div class="h-full overflow-x-hidden">
9
+ <%= lookbook_render "params/editor", inputs: Lookbook::Params.inputs do |editor| %>
10
10
  <% preview.params.each do |param| %>
11
11
  <% editor.field **param, value: params[param[:name]] %>
12
12
  <% end %>
@@ -1,4 +1,4 @@
1
- <%= render_component :viewport,
1
+ <%= lookbook_render :viewport,
2
2
  src: lookbook_preview_path(request.query_parameters.merge(lookbook_timestamp: Time.now)),
3
3
  alpine_data: "$store.inspector.main",
4
4
  class: "-inset-px relative",
@@ -1,11 +1,11 @@
1
1
  <div class="h-full">
2
2
  <% if examples.many? %>
3
- <%= render_component :code, language: examples.first.source_lang[:name], line_numbers: true, full_height: true do -%>
3
+ <%= lookbook_render :code, language: examples.first.source_lang[:name], line_numbers: true, full_height: true do -%>
4
4
  <%- examples.each.with_index(1) do |example, i| -%>
5
5
  <%== "#{sprintf example.source_lang[:comment], example.label}\n#{example.source}\n#{"\n" if i < examples.size}" %><% end %>
6
6
  <% end %>
7
7
  <% else %>
8
8
  <% example = examples.first %>
9
- <%= render_component :code, language: example.source_lang[:name], line_numbers: true, full_height: true do %><%== example.source %><% end %>
9
+ <%= lookbook_render :code, language: example.source_lang[:name], line_numbers: true, full_height: true do %><%== example.source %><% end %>
10
10
  <% end %>
11
11
  </div>
@@ -1,13 +1,13 @@
1
- <%= render_component :split_layout,
1
+ <%= lookbook_render :split_layout,
2
2
  alpine_data: "$store.layout.inspector",
3
3
  ":class": "($store.inspector.drawer.hidden || #{@drawer_panels.none?}) && '!grid-rows-[1fr] !grid-cols-[1fr]'" do |layout| %>
4
4
 
5
5
  <%= layout.pane class: "flex flex-col h-full overflow-hidden",
6
6
  "x-effect": "forceOrientation = (layoutWidth < $store.inspector.minVerticalSplitWidth) ? 'horizontal' : null" do %>
7
7
 
8
- <%= render_component :toolbar do |toolbar| %>
8
+ <%= lookbook_render :toolbar do |toolbar| %>
9
9
  <% toolbar.section ":class": "layoutResizing && 'overflow-hidden'" do %>
10
- <%= render_component :tabs, alpine_data: "$store.inspector.main" do |tabs| %>
10
+ <%= lookbook_render :tabs, alpine_data: "$store.inspector.main" do |tabs| %>
11
11
  <%= @main_panels.each do |panel| %>
12
12
  <% tabs.tab name: panel.name,
13
13
  label: panel.label,
@@ -17,19 +17,13 @@
17
17
  <% end %>
18
18
  <% end %>
19
19
 
20
- <% toolbar.section align: :right, padded: true, class: "flex-none min-w-[140px]", ":class": "{invisible: $store.inspector.main.activeTab !== 'preview'}" do %>
21
- <%= render_component :dimensions_display,
22
- target: "[data-component=viewport] iframe",
23
- class: "ml-auto opacity-30 hover:opacity-100" %>
24
- <% end %>
25
-
26
- <% toolbar.section divide: :left, class: "flex-none relative z-10" do %>
27
- <%= render_component :button_group do |group| %>
20
+ <% toolbar.section divide: :left, align: :right, class: "flex-none relative z-10" do %>
21
+ <%= lookbook_render :button_group do |group| %>
28
22
  <% if @pages.any? %>
29
23
  <% group.button icon: :code,
30
24
  tooltip: "Copy page embed code",
31
25
  copy: true do %>
32
- &lt;%= embed <%= @preview.preview_class %>, :<%= @target.name %>, params: <%= request.query_parameters.deep_symbolize_keys.to_s %> %&gt;
26
+ &lt;%= embed <%= @preview.preview_class_name %>, :<%= @target.name %>, params: <%= request.query_parameters.deep_symbolize_keys.to_s %> %&gt;
33
27
  <% end %>
34
28
  <% end %>
35
29
 
@@ -53,7 +47,7 @@
53
47
  <% end %>
54
48
 
55
49
  <div class="h-full relative overflow-auto">
56
- <%= render_component :tab_panels, alpine_data: "$store.inspector.main" do |tabs| %>
50
+ <%= lookbook_render :tab_panels, alpine_data: "$store.inspector.main" do |tabs| %>
57
51
  <% @main_panels.each do |panel| %>
58
52
  <% tabs.panel id: panel.id, name: panel.name, class: panel.panel_classes do %>
59
53
  <%= render panel.partial, **@inspector_data, panel: panel, **panel.locals %>
@@ -66,9 +60,9 @@
66
60
  <%= layout.pane class: "flex flex-col h-full overflow-hidden bg-lookbook-drawer-bg",
67
61
  "x-show": "!$store.inspector.drawer.hidden && #{@drawer_panels.any?}" do %>
68
62
 
69
- <%= render_component :toolbar do |toolbar| %>
63
+ <%= lookbook_render :toolbar do |toolbar| %>
70
64
  <% toolbar.section ":class": "layoutResizing && 'overflow-hidden'" do %>
71
- <%= render_component :tabs, alpine_data: "$store.inspector.drawer" do |tabs| %>
65
+ <%= lookbook_render :tabs, alpine_data: "$store.inspector.drawer" do |tabs| %>
72
66
  <%= @drawer_panels.each do |panel| %>
73
67
  <% tabs.tab name: panel.name,
74
68
  label: panel.label,
@@ -79,7 +73,7 @@
79
73
  <% end %>
80
74
 
81
75
  <% toolbar.section align: :right, class: "flex-none relative z-10" do %>
82
- <%= render_component :button_group do |group| %>
76
+ <%= lookbook_render :button_group do |group| %>
83
77
  <%= @drawer_panels.select { |p| !p.disabled && p.copy }.each do |panel| %>
84
78
  <% group.button icon: :copy,
85
79
  tooltip: "Copy panel contents",
@@ -93,7 +87,7 @@
93
87
  <% end %>
94
88
 
95
89
  <% toolbar.section divide: :left, class: "flex-none relative z-10" do %>
96
- <%= render_component :button_group do |group| %>
90
+ <%= lookbook_render :button_group do |group| %>
97
91
 
98
92
  <% group.button icon: :corner_up_right,
99
93
  tooltip: "Move drawer to right",
@@ -121,10 +115,10 @@
121
115
  <% end %>
122
116
 
123
117
  <div class="h-full overflow-auto">
124
- <%= render_component :tab_panels, alpine_data: "$store.inspector.drawer" do |tabs| %>
118
+ <%= lookbook_render :tab_panels, alpine_data: "$store.inspector.drawer" do |tabs| %>
125
119
  <% @drawer_panels.each do |panel| %>
126
120
  <% tabs.panel id: panel.id, name: panel.name, class: panel.panel_classes do %>
127
- <%= render_component :inspector_panel, **panel.slice(:id, :name, :system) do %>
121
+ <%= lookbook_render :inspector_panel, **panel.slice(:id, :name, :system) do %>
128
122
  <%= render panel.partial, **@inspector_data, panel: panel, **panel.locals %>
129
123
  <% end %>
130
124
  <% end %>
@@ -10,6 +10,7 @@ module Lookbook
10
10
  @options.set({
11
11
  project_name: "Lookbook",
12
12
  log_level: 2,
13
+ log_use_rails_logger: true,
13
14
  auto_refresh: true,
14
15
 
15
16
  components_path: "app/components",
@@ -25,6 +26,21 @@ module Lookbook
25
26
  preview_srcdoc: nil,
26
27
  preview_tags: {},
27
28
  preview_disable_action_view_annotations: true,
29
+ preview_param_inputs: {
30
+ select: "lookbook/previews/inputs/select",
31
+ textarea: "lookbook/previews/inputs/textarea",
32
+ toggle: "lookbook/previews/inputs/toggle",
33
+ color: "lookbook/previews/inputs/color",
34
+ range: "lookbook/previews/inputs/range",
35
+ text: "lookbook/previews/inputs/text",
36
+ email: "lookbook/previews/inputs/text",
37
+ number: "lookbook/previews/inputs/text",
38
+ tel: "lookbook/previews/inputs/text",
39
+ url: "lookbook/previews/inputs/text",
40
+ date: "lookbook/previews/inputs/text",
41
+ datetime_local: "lookbook/previews/inputs/text"
42
+ },
43
+ preview_params_options_eval: false,
28
44
  sort_examples: false,
29
45
 
30
46
  listen: Rails.env.development?,
@@ -33,7 +49,6 @@ module Lookbook
33
49
  listen_use_polling: false,
34
50
 
35
51
  cable_mount_path: "/cable",
36
- cable_logger: Lookbook.logger,
37
52
 
38
53
  parser_registry_path: "tmp/storage/.yardoc",
39
54
 
@@ -28,7 +28,13 @@ module Lookbook
28
28
  end
29
29
 
30
30
  def logger
31
- @logger ||= Rails.env.development? ? Logger.new($stdout) : Rails.logger
31
+ @logger ||= if Rails.logger.present? && config.log_use_rails_logger
32
+ Rails.logger
33
+ else
34
+ logger = Logger.new($stdout)
35
+ logger.level = config.log_level
36
+ logger
37
+ end
32
38
  end
33
39
 
34
40
  def debug_data
@@ -54,6 +60,13 @@ module Lookbook
54
60
  def theme
55
61
  @theme ||= Lookbook::Theme.new(config.ui_theme, config.ui_theme_overrides)
56
62
  end
63
+
64
+ def define_param_input(input, partial, input_options = nil)
65
+ config.preview_param_inputs[input.to_sym] = {
66
+ partial: partial,
67
+ input_options: input_options || {}
68
+ }
69
+ end
57
70
  end
58
71
 
59
72
  class Engine < Rails::Engine
@@ -72,10 +85,6 @@ module Lookbook
72
85
  config.lookbook.listen_paths << config.lookbook.components_path
73
86
  end
74
87
 
75
- initializer "lookbook.logging.development" do
76
- Lookbook.logger.level = Lookbook.config.log_level if Rails.env.development?
77
- end
78
-
79
88
  initializer "lookbook.parser.tags" do
80
89
  Lookbook::Parser.define_tags(Lookbook.config.preview_tags)
81
90
  end
@@ -131,6 +140,7 @@ module Lookbook
131
140
  if listen_paths.any?
132
141
  preview_listener = Listen.to(*listen_paths,
133
142
  only: /\.(#{config.listen_extensions.join("|")})$/,
143
+ wait_for_delay: 0.5,
134
144
  force_polling: config.listen_use_polling) do |modified, added, removed|
135
145
  parser.parse do
136
146
  run_hooks(:after_change, {modified: modified, added: added, removed: removed})
@@ -162,7 +172,7 @@ module Lookbook
162
172
  cable.cable = {adapter: "async"}.with_indifferent_access
163
173
  cable.mount_path = nil
164
174
  cable.connection_class = -> { Lookbook::Connection }
165
- cable.logger = config.cable_logger
175
+ cable.logger = Lookbook.logger
166
176
 
167
177
  @websocket ||= if Gem::Version.new(Rails.version) >= Gem::Version.new(6.0)
168
178
  ActionCable::Server::Base.new(config: cable)
@@ -13,7 +13,7 @@ module Lookbook
13
13
  }
14
14
 
15
15
  def self.render(text)
16
- text&.gsub!(/<!-- (BEGIN|END) (.*) -->/, "")
16
+ Utils.strip_action_view_annotations!(text)
17
17
  markdown = Redcarpet::Markdown.new(Renderer, Lookbook.config.markdown_options)
18
18
  markdown.render(text).html_safe
19
19
  end
@@ -1,15 +1,25 @@
1
1
  module Lookbook
2
2
  module Panels
3
- def define_panel(name, opts = {})
4
- Lookbook.config.define_inspector_panel(name, opts)
3
+ def define_panel(name, *args)
4
+ Lookbook.config.define_inspector_panel(name, extract_opts(args))
5
5
  end
6
6
 
7
- def amend_panel(name, opts = {})
8
- Lookbook.amend_inspector_panel(name, opts)
7
+ def amend_panel(name, *args)
8
+ Lookbook.amend_inspector_panel(name, extract_opts(args))
9
9
  end
10
10
 
11
11
  def remove_panel(name)
12
12
  Lookbook.remove_inspector_panel(name)
13
13
  end
14
+
15
+ def extract_opts(args)
16
+ if args.many?
17
+ opts = args[1]
18
+ opts[:partial] = args[0]
19
+ opts
20
+ elsif args.any?
21
+ args[0].is_a?(String) ? {partial: args[0]} : args[0]
22
+ end
23
+ end
14
24
  end
15
25
  end
@@ -2,32 +2,68 @@ require "active_model"
2
2
 
3
3
  module Lookbook
4
4
  module Params
5
+ VALUE_TYPE_MATCH_REGEXP = /^(\[\s?([A-Z]{1}\w+)\s?\])/
6
+ DESCRIPTION_MATCH_REGEXP = /"(.*[^\\])"$/
7
+
8
+ PARAM_OPTION_KEYS = %i[name input label hint description value_type value_default].freeze
9
+
5
10
  class << self
6
- def build_param(param, default: nil, **opts)
7
- input, options_str = param.text.present? ? param.text.split(" ", 2) : [nil, ""]
8
- type = param.types&.first
9
- options = if options_str.present? && options_str.end_with?(".json")
10
- json_path = if options_str.start_with?(".")
11
- File.expand_path(options_str, File.dirname(param.object.files.first[0]))
12
- else
13
- Rails.root.join(options_str)
14
- end
15
- JSON.parse(File.read(json_path))
16
- else
17
- YAML.safe_load(options_str || "~")
11
+ def build_param(param, default: nil, eval_scope: nil)
12
+ input, value_type, options_str, description = parse_param_tag_text(param.text)
13
+
14
+ tag_options = Lookbook::TagOptions.new(options_str,
15
+ base_dir: (File.dirname(param.object.files.first[0]) if param.object.files.any?),
16
+ eval_scope: eval_scope).options
17
+
18
+ if tag_options.is_a? Array
19
+ # handle special case legacy situation for selects where
20
+ # options are an array of choices rather than a Hash
21
+ tag_options = {choices: tag_options}
18
22
  end
19
- input ||= guess_input(type, default)
20
- type ||= guess_type(input, default)
23
+
24
+ param_options = tag_options.select { |key| PARAM_OPTION_KEYS.include? key }
25
+ input_options = tag_options.except(*PARAM_OPTION_KEYS)
26
+
27
+ value_type ||= param_options[:value_type]
28
+ input ||= param_options[:input] || guess_input(value_type, default)
29
+ name = param.name.to_s
30
+
21
31
  {
22
- name: param.name,
23
- input: input_text?(input) ? "text" : input,
24
- input_type: (input if input_text?(input)),
25
- options: options,
26
- type: type,
27
- default: default
32
+ name: name,
33
+ label: param_options[:label] || name.titleize,
34
+ hint: param_options[:hint],
35
+ description: description || param_options[:description],
36
+ input: input.to_s.tr("_", "-"),
37
+ input_options: input_options,
38
+ value: nil,
39
+ value_type: value_type || guess_value_type(input, default),
40
+ value_default: default
28
41
  }
29
42
  end
30
43
 
44
+ # Parses param tag strings with the format: `[<value_type>] <input> <description?> <opts?>`
45
+ def parse_param_tag_text(text)
46
+ text = (text.presence || "").strip
47
+
48
+ value_type = nil
49
+ text.match(VALUE_TYPE_MATCH_REGEXP) do |m|
50
+ value_type = m[2]
51
+ text.gsub!(VALUE_TYPE_MATCH_REGEXP, "").strip!
52
+ end
53
+
54
+ text, options_str = Lookbook::TagOptions.extract_options(text)
55
+
56
+ description = nil
57
+ text.match(DESCRIPTION_MATCH_REGEXP) do |m|
58
+ description = m[1]
59
+ text.gsub!(DESCRIPTION_MATCH_REGEXP, "").strip!
60
+ end
61
+
62
+ input, rest = text.split(" ", 2)
63
+
64
+ [input, value_type, options_str, description, rest]
65
+ end
66
+
31
67
  def parse_method_param_str(param_str)
32
68
  return nil if param_str[0].nil? || param_str[1].nil?
33
69
  name = param_str[0].chomp(":")
@@ -81,17 +117,24 @@ module Lookbook
81
117
  end
82
118
  end
83
119
 
120
+ def inputs
121
+ @inputs ||= Lookbook.config.preview_param_inputs.map do |name, config|
122
+ config = {partial: config} if config.is_a?(String)
123
+ [name, {input_options: {}}.merge(config)]
124
+ end.to_h
125
+ end
126
+
84
127
  private
85
128
 
86
- def guess_input(type, default)
87
- if type&.downcase == "boolean" || (type.blank? && boolean?(default))
129
+ def guess_input(value_type, default)
130
+ if value_type&.downcase == "boolean" || (value_type.blank? && boolean?(default))
88
131
  "toggle"
89
132
  else
90
133
  "text"
91
134
  end
92
135
  end
93
136
 
94
- def guess_type(input, default)
137
+ def guess_value_type(input, default)
95
138
  if input&.downcase == "toggle"
96
139
  "Boolean"
97
140
  elsif input&.downcase == "number"
@@ -107,18 +150,6 @@ module Lookbook
107
150
  end
108
151
  end
109
152
 
110
- def input_text?(input)
111
- [
112
- "date",
113
- "datetime-local",
114
- "email",
115
- "number",
116
- "tel",
117
- "text",
118
- "url"
119
- ].include? input.to_s
120
- end
121
-
122
153
  def safe_parse_yaml(value, fallback)
123
154
  value.present? ? YAML.safe_load(value) : fallback
124
155
  rescue Psych::SyntaxError
@@ -39,6 +39,7 @@ module Lookbook
39
39
  YARD::Tags::Library.define_tag("Position", :position)
40
40
  YARD::Tags::Library.define_tag("ID", :id)
41
41
  YARD::Tags::Library.define_tag("Component", :component)
42
+ YARD::Tags::Library.define_tag("Param", :param, :with_name)
42
43
  custom.each do |name, opts|
43
44
  YARD::Tags::Library.define_tag(name.to_s.titleize, name)
44
45
  end
@@ -7,7 +7,7 @@ module Lookbook
7
7
 
8
8
  def initialize(preview, code_object)
9
9
  @preview = preview
10
- @preview_inspector = SourceInspector.new(code_object)
10
+ @preview_inspector = SourceInspector.new(code_object, eval_scope: preview_class.new)
11
11
  super(preview_class_path(@preview.name))
12
12
  end
13
13
 
@@ -15,10 +15,14 @@ module Lookbook
15
15
  @preview_inspector&.id || generate_id(lookup_path)
16
16
  end
17
17
 
18
- def preview_class
18
+ def preview_class_name
19
19
  @preview.name
20
20
  end
21
21
 
22
+ def preview_class
23
+ @preview
24
+ end
25
+
22
26
  def label
23
27
  @preview_inspector&.label&.presence || lookup_path.split("/").last.titleize
24
28
  end
@@ -143,10 +147,12 @@ module Lookbook
143
147
 
144
148
  sorted_previews = previews.compact.sort_by { |preview| [preview.position, preview.label] }
145
149
  @previews = PreviewCollection.new(sorted_previews)
146
- else
150
+ @previews
151
+ elsif !@preview_objects.present?
147
152
  PreviewCollection.new([])
153
+ else
154
+ @previews
148
155
  end
149
- @previews
150
156
  end
151
157
 
152
158
  def errors
@@ -12,32 +12,20 @@ module Lookbook
12
12
  opts[:layout] = nil
13
13
  opts[:locals] = locals if locals.present?
14
14
 
15
- with_optional_annotations do
15
+ Utils.with_optional_action_view_annotations do
16
16
  render html: render_to_string(template, **opts)
17
17
  end
18
18
  end
19
19
 
20
20
  def render_in_layout_to_string(template, locals, opts = {})
21
21
  append_view_path Lookbook::Engine.root.join("app/views")
22
- html = render_to_string(template, locals: locals, **determine_layout(opts[:layout]))
23
- if opts[:append_html].present?
24
- html += opts[:append_html]
25
- end
26
- render html: html
27
- end
28
-
29
- def with_optional_annotations
30
- if ActionView::Base.respond_to?(:annotate_rendered_view_with_filenames) && Lookbook.config.preview_disable_action_view_annotations
31
- original_value = ActionView::Base.annotate_rendered_view_with_filenames
32
- ActionView::Base.annotate_rendered_view_with_filenames = false
33
-
34
- res = yield
35
-
36
- ActionView::Base.annotate_rendered_view_with_filenames = original_value
37
22
 
38
- res
39
- else
40
- yield
23
+ Utils.with_optional_action_view_annotations do
24
+ html = render_to_string(template, locals: locals, **determine_layout(opts[:layout]))
25
+ if opts[:append_html].present?
26
+ html += opts[:append_html]
27
+ end
28
+ render html: html
41
29
  end
42
30
  end
43
31
  end
@@ -6,7 +6,7 @@ module Lookbook
6
6
  def initialize(name, preview, code_object)
7
7
  @name = name
8
8
  @preview = preview
9
- @example_inspector = SourceInspector.new(code_object)
9
+ @example_inspector = SourceInspector.new(code_object, eval_scope: @preview.preview_class.new)
10
10
  super("#{@preview.path}/#{name}")
11
11
  end
12
12
 
@@ -5,8 +5,9 @@ module Lookbook
5
5
  attr_reader :code_object
6
6
  delegate :groups, :source, to: :@code_object, allow_nil: true
7
7
 
8
- def initialize(code_object)
8
+ def initialize(code_object, eval_scope: nil)
9
9
  @code_object = code_object
10
+ @eval_scope = eval_scope
10
11
  end
11
12
 
12
13
  def hidden?
@@ -74,8 +75,11 @@ module Lookbook
74
75
  end
75
76
 
76
77
  def params
77
- code_object&.tags("param")&.map do |param|
78
- Lookbook::Params.build_param(param, default: parameter_defaults[param.name])
78
+ @params ||= {}
79
+ @params[:param] ||= code_object&.tags("param")&.map do |param|
80
+ Lookbook::Params.build_param(param,
81
+ default: parameter_defaults[param.name],
82
+ eval_scope: @eval_scope)
79
83
  end
80
84
  end
81
85
 
@@ -85,7 +89,9 @@ module Lookbook
85
89
 
86
90
  def tags(name = nil)
87
91
  tag_objects = code_object&.tags(name).presence || []
88
- Lookbook::Tags.process_tags(tag_objects)
92
+ Lookbook::Tags.process_tags(tag_objects,
93
+ eval_scope: @eval_scope,
94
+ file: (code_object.files.first[0] if code_object.files.any?))
89
95
  end
90
96
 
91
97
  def tag(name = nil)
data/lib/lookbook/tag.rb CHANGED
@@ -5,13 +5,15 @@ module Lookbook
5
5
  attr_reader :data, :arg_names
6
6
  attr_accessor :args, :opts
7
7
 
8
- def initialize(tag_object, arg_names = nil, parser: nil, **options)
8
+ def initialize(tag_object, arg_names = nil, parser: nil, eval_scope: nil, file: nil, **options)
9
9
  @tag_object = tag_object
10
10
  @arg_names = arg_names
11
11
  @args = {}
12
12
  @opts = {}
13
13
  @options = options
14
14
  @parser = parser
15
+ @eval_scope = eval_scope
16
+ @file = file
15
17
  @data = Store.new
16
18
  run_parser
17
19
  end
@@ -43,8 +45,16 @@ module Lookbook
43
45
 
44
46
  def parse_opts
45
47
  return @opts if @options[:parse_options] == false
46
- parsed_opts = parse_yaml(opts_str)
47
- @opts = parsed_opts.is_a?(Hash) ? parsed_opts.with_indifferent_access : {}
48
+ tag_opts = Lookbook::TagOptions.new(opts_str,
49
+ eval_scope: @eval_scope,
50
+ base_dir: File.dirname(@file))
51
+ options = tag_opts.resolve || {}
52
+ @opts = if options.is_a?(Hash)
53
+ options.with_indifferent_access
54
+ else
55
+ Lookbook.logger.warn "'@#{tag_name}' tag options should resolve to a Hash"
56
+ options
57
+ end
48
58
  end
49
59
 
50
60
  def run_parser