lookbook 1.5.0 → 2.0.0.beta.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +11 -21
- data/app/assets/lookbook/css/lookbook.css +9 -0
- data/app/assets/lookbook/css/themes/blue.css +7 -0
- data/app/assets/lookbook/css/themes/green.css +7 -0
- data/app/assets/lookbook/css/themes/indigo.css +7 -0
- data/app/assets/lookbook/css/themes/rose.css +7 -0
- data/app/assets/lookbook/css/themes/zinc.css +7 -0
- data/app/assets/lookbook/css/tooltip.css +9 -6
- data/app/assets/lookbook/img/lucide-sprite.svg +4960 -0
- data/app/assets/lookbook/js/app.js +22 -4
- data/app/assets/lookbook/js/helpers/dom.js +4 -7
- data/app/assets/lookbook/js/helpers/string.js +4 -11
- data/app/assets/lookbook/js/{embed.js → iframe.js} +0 -0
- data/app/assets/lookbook/js/index.js +61 -0
- data/app/assets/lookbook/js/lib/lookbook.js +113 -0
- data/app/assets/lookbook/js/lib/tippy.js +1 -0
- data/app/assets/lookbook/js/lookbook-core.js +1 -0
- data/app/assets/lookbook/js/lookbook.js +2 -61
- data/app/components/lookbook/base_component.rb +3 -1
- data/app/components/lookbook/button/component.html.erb +13 -24
- data/app/components/lookbook/button/component.js +13 -3
- data/app/components/lookbook/button/component.rb +16 -25
- data/app/components/lookbook/code/component.rb +0 -2
- data/app/components/lookbook/copy_button/component.html.erb +4 -4
- data/app/components/lookbook/copy_button/component.rb +6 -3
- data/app/components/lookbook/debug_menu/component.html.erb +1 -0
- data/app/components/lookbook/debug_menu/component.rb +12 -1
- data/app/components/lookbook/display_options/editor/component.html.erb +1 -1
- data/app/components/lookbook/display_options/field/component.css +0 -26
- data/app/components/lookbook/display_options/field/component.html.erb +1 -1
- data/app/components/lookbook/embed/component.html.erb +6 -51
- data/app/components/lookbook/embed/component.rb +17 -16
- data/app/components/lookbook/embed/inspector/component.html.erb +102 -0
- data/app/components/lookbook/embed/inspector/component.js +46 -0
- data/app/components/lookbook/embed/inspector/component.rb +64 -0
- data/app/components/lookbook/embed_code_dropdown/component.css +12 -0
- data/app/components/lookbook/embed_code_dropdown/component.html.erb +19 -0
- data/app/components/lookbook/embed_code_dropdown/component.js +26 -0
- data/app/components/lookbook/embed_code_dropdown/component.rb +41 -0
- data/app/components/lookbook/header/component.html.erb +7 -6
- data/app/components/lookbook/header/component.rb +5 -1
- data/app/components/lookbook/icon/component.html.erb +1 -1
- data/app/components/lookbook/icon_button/component.html.erb +20 -0
- data/app/components/lookbook/icon_button/component.rb +46 -0
- data/app/components/lookbook/nav/component.html.erb +0 -1
- data/app/components/lookbook/nav/entity/component.rb +2 -2
- data/app/components/lookbook/nav/item/component.rb +1 -1
- data/app/components/lookbook/params/editor/component.rb +1 -0
- data/app/components/lookbook/prose/component.rb +1 -3
- data/app/components/lookbook/tabs/component.html.erb +2 -2
- data/app/components/lookbook/tabs/component.js +1 -1
- data/app/components/lookbook/tag_component.rb +2 -1
- data/app/components/lookbook/text_button/component.html.erb +26 -0
- data/app/components/lookbook/text_button/component.rb +42 -0
- data/app/components/lookbook/toolbar/component.html.erb +1 -1
- data/app/components/lookbook/viewport/component.rb +0 -4
- data/app/controllers/concerns/lookbook/targetable_concern.rb +24 -19
- data/app/controllers/concerns/lookbook/with_panels_concern.rb +30 -0
- data/app/controllers/concerns/lookbook/with_preview_controller_concern.rb +4 -3
- data/app/controllers/lookbook/application_controller.rb +9 -11
- data/app/controllers/lookbook/embeds_controller.rb +148 -0
- data/app/controllers/lookbook/inspector_controller.rb +3 -22
- data/app/controllers/lookbook/page_controller.rb +7 -6
- data/app/controllers/lookbook/pages_controller.rb +3 -4
- data/app/controllers/lookbook/preview_controller.rb +17 -0
- data/app/controllers/lookbook/previews_controller.rb +25 -7
- data/app/helpers/lookbook/application_helper.rb +3 -19
- data/app/views/layouts/lookbook/application.html.erb +85 -60
- data/app/views/layouts/lookbook/embed.html.erb +16 -0
- data/app/views/layouts/lookbook/shell.html.erb +1 -1
- data/app/views/layouts/lookbook/skeleton.html.erb +13 -8
- data/app/views/lookbook/embeds/show.html.erb +12 -0
- data/app/views/lookbook/inspector/panels/_notes.html.erb +1 -1
- data/app/views/lookbook/inspector/panels/_output.html.erb +3 -3
- data/app/views/lookbook/inspector/panels/_source.html.erb +6 -6
- data/app/views/lookbook/inspector/show.html.erb +130 -123
- data/app/views/lookbook/pages/show.html.erb +81 -34
- data/app/views/lookbook/partials/_iframe_content_scripts.html.erb +1 -0
- data/app/views/lookbook/partials/_user_styles.html.erb +5 -0
- data/app/views/lookbook/{preview.html.erb → previews/group.html.erb} +7 -7
- data/app/views/lookbook/previews/preview.html.erb +5 -0
- data/app/views/lookbook/previews/show.html.erb +1 -0
- data/config/app.yml +31 -16
- data/config/panels.yml +23 -25
- data/config/routes.rb +4 -1
- data/config/tags.yml +6 -2
- data/lib/lookbook/cable/cable.rb +53 -0
- data/{app/channels/lookbook → lib/lookbook/cable}/connection.rb +0 -0
- data/{app/channels/lookbook → lib/lookbook/cable}/reload_channel.rb +0 -0
- data/lib/lookbook/engine.rb +109 -87
- data/lib/lookbook/entities/collections/entity_collection.rb +11 -6
- data/lib/lookbook/entities/collections/page_collection.rb +33 -8
- data/lib/lookbook/entities/collections/preview_collection.rb +42 -17
- data/lib/lookbook/entities/collections/render_target_collection.rb +4 -0
- data/lib/lookbook/entities/collections/scenario_collection.rb +4 -0
- data/lib/lookbook/entities/concerns/{annotatable.rb → annotatable_entity.rb} +7 -6
- data/lib/lookbook/entities/concerns/{inspectable.rb → inspectable_entity.rb} +2 -1
- data/lib/lookbook/entities/concerns/{locatable.rb → locatable_entity.rb} +8 -14
- data/lib/lookbook/entities/concerns/navigable_entity.rb +44 -0
- data/lib/lookbook/entities/entity.rb +7 -2
- data/lib/lookbook/entities/{page.rb → page_entity.rb} +10 -6
- data/lib/lookbook/entities/{page_section.rb → page_section_entity.rb} +1 -1
- data/lib/lookbook/entities/preview_entity.rb +99 -0
- data/lib/lookbook/entities/renderable_entity.rb +50 -0
- data/lib/lookbook/entities/rendered_scenario_entity.rb +53 -0
- data/lib/lookbook/entities/scenario_entity.rb +112 -0
- data/lib/lookbook/entities/scenario_group_entity.rb +53 -0
- data/lib/lookbook/error.rb +5 -5
- data/lib/lookbook/file_watcher.rb +19 -35
- data/lib/lookbook/helpers/class_names_helper.rb +28 -0
- data/lib/lookbook/helpers/page_helper.rb +18 -0
- data/{app/helpers/lookbook → lib/lookbook/helpers}/preview_helper.rb +3 -0
- data/lib/lookbook/helpers/ui_elements_helper.rb +115 -0
- data/lib/lookbook/preview.rb +79 -0
- data/lib/lookbook/preview_controller_actions.rb +50 -0
- data/lib/lookbook/preview_parser.rb +4 -2
- data/lib/lookbook/reloaders.rb +71 -0
- data/lib/lookbook/runtime_context.rb +49 -0
- data/lib/lookbook/services/data/resolvers/data_resolver.rb +4 -6
- data/lib/lookbook/services/entities/entity_tree_builder.rb +6 -6
- data/lib/lookbook/services/list_resolver.rb +35 -0
- data/lib/lookbook/services/markdown_renderer.rb +12 -2
- data/lib/lookbook/services/priority_prefix_parser.rb +16 -0
- data/lib/lookbook/services/urls/search_param_encoder.rb +16 -0
- data/lib/lookbook/services/urls/search_param_parser.rb +7 -6
- data/lib/lookbook/stores/config_store.rb +16 -16
- data/lib/lookbook/stores/input_store.rb +1 -3
- data/lib/lookbook/stores/panel_store.rb +28 -50
- data/lib/lookbook/support/deprecation.rb +5 -0
- data/lib/lookbook/support/errors/preview_template_error.rb +7 -0
- data/lib/lookbook/support/evented_file_update_checker.rb +69 -0
- data/lib/lookbook/support/null_websocket.rb +9 -0
- data/lib/lookbook/support/store.rb +9 -0
- data/lib/lookbook/support/tree_node.rb +7 -7
- data/lib/lookbook/support/utils/path_utils.rb +7 -1
- data/lib/lookbook/support/utils/utils.rb +8 -0
- data/lib/lookbook/tags/{position_tag.rb → priority_tag.rb} +4 -4
- data/lib/lookbook/tags/renders_tag.rb +4 -0
- data/lib/lookbook/tags/tag_provider.rb +3 -0
- data/lib/lookbook/tags/type_tag.rb +7 -0
- data/lib/lookbook/tags/yard_tag.rb +1 -2
- data/lib/lookbook/version.rb +1 -1
- data/lib/lookbook/websocket.rb +6 -53
- data/lib/lookbook.rb +179 -53
- data/public/lookbook-assets/css/lookbook.css +141 -83
- data/public/lookbook-assets/css/lookbook.css.map +1 -1
- data/public/lookbook-assets/css/themes/blue.css +7 -0
- data/public/lookbook-assets/css/themes/blue.css.map +1 -1
- data/public/lookbook-assets/css/themes/green.css +7 -0
- data/public/lookbook-assets/css/themes/green.css.map +1 -1
- data/public/lookbook-assets/css/themes/indigo.css +7 -0
- data/public/lookbook-assets/css/themes/indigo.css.map +1 -1
- data/public/lookbook-assets/css/themes/rose.css +7 -0
- data/public/lookbook-assets/css/themes/rose.css.map +1 -1
- data/public/lookbook-assets/css/themes/zinc.css +7 -0
- data/public/lookbook-assets/css/themes/zinc.css.map +1 -1
- data/public/lookbook-assets/img/lucide-sprite.svg +4960 -0
- data/public/lookbook-assets/js/embed.js +1363 -841
- data/public/lookbook-assets/js/embed.js.map +1 -1
- data/public/lookbook-assets/js/iframe.js +906 -0
- data/public/lookbook-assets/js/iframe.js.map +1 -0
- data/public/lookbook-assets/js/index.js +13567 -0
- data/public/lookbook-assets/js/index.js.map +1 -0
- data/public/lookbook-assets/js/lookbook-core.js +85 -0
- data/public/lookbook-assets/js/lookbook-core.js.map +1 -0
- data/public/lookbook-assets/js/lookbook.js +164 -12638
- data/public/lookbook-assets/js/lookbook.js.map +1 -1
- data/public/lookbook-assets/lookbook-esm.js +1427 -0
- data/public/lookbook-assets/lookbook-esm.js.map +1 -0
- data/public/lookbook-assets/lookbook-global.js +1427 -0
- data/public/lookbook-assets/lookbook-global.js.map +1 -0
- data/public/lookbook-assets/lookbook.js +1427 -0
- data/public/lookbook-assets/lookbook.js.map +1 -0
- metadata +80 -72
- data/app/components/lookbook/embed/component.js +0 -39
- data/app/helpers/lookbook/component_helper.rb +0 -84
- data/app/helpers/lookbook/output_helper.rb +0 -19
- data/app/helpers/lookbook/page_helper.rb +0 -34
- data/app/views/layouts/lookbook/inspector.html.erb +0 -7
- data/app/views/layouts/lookbook/page.html.erb +0 -53
- data/app/views/layouts/lookbook/standalone.html.erb +0 -5
- data/lib/lookbook/entities/collections/component_collection.rb +0 -4
- data/lib/lookbook/entities/collections/preview_example_collection.rb +0 -4
- data/lib/lookbook/entities/component.rb +0 -31
- data/lib/lookbook/entities/concerns/navigable.rb +0 -43
- data/lib/lookbook/entities/preview.rb +0 -87
- data/lib/lookbook/entities/preview_example.rb +0 -100
- data/lib/lookbook/entities/preview_group.rb +0 -48
- data/lib/lookbook/preview_actions.rb +0 -43
- data/lib/lookbook/process.rb +0 -21
- data/lib/lookbook/rendered_example.rb +0 -37
- data/lib/lookbook/services/position_prefix_parser.rb +0 -16
- data/lib/lookbook/services/urls/search_param_builder.rb +0 -13
- data/lib/lookbook/tags/component_tag.rb +0 -13
@@ -0,0 +1,112 @@
|
|
1
|
+
module Lookbook
|
2
|
+
# Represents a preview scenario method within a preview class
|
3
|
+
#
|
4
|
+
# @ignore methods
|
5
|
+
# @api public
|
6
|
+
class ScenarioEntity < Entity
|
7
|
+
include InspectableEntity
|
8
|
+
include AnnotatableEntity
|
9
|
+
include NavigableEntity
|
10
|
+
|
11
|
+
delegate :group, to: :code_object
|
12
|
+
|
13
|
+
attr_reader :preview
|
14
|
+
|
15
|
+
def initialize(code_object, preview, priority: nil)
|
16
|
+
@code_object = code_object
|
17
|
+
@preview = preview
|
18
|
+
@default_priority = priority
|
19
|
+
@lookup_path = "#{parent.lookup_path}/#{name}"
|
20
|
+
end
|
21
|
+
|
22
|
+
def id
|
23
|
+
@_id ||= Utils.id(fetch_config(:id) { "#{parent.id}-#{code_object.name}" })
|
24
|
+
end
|
25
|
+
|
26
|
+
def name
|
27
|
+
@_name ||= Utils.name(code_object.name)
|
28
|
+
end
|
29
|
+
|
30
|
+
def display_options
|
31
|
+
parent.display_options.merge(fetch_config(:display_options, {}))
|
32
|
+
end
|
33
|
+
|
34
|
+
def render_targets
|
35
|
+
@_render_targets ||= RenderTargetCollection.new(load_render_targets)
|
36
|
+
end
|
37
|
+
|
38
|
+
def render_target
|
39
|
+
render_targets.first
|
40
|
+
end
|
41
|
+
|
42
|
+
def scenarios
|
43
|
+
[self]
|
44
|
+
end
|
45
|
+
|
46
|
+
def template_source(template_path)
|
47
|
+
source_path = template_file_path(template_path)
|
48
|
+
source_path ? File.read(source_path) : nil
|
49
|
+
end
|
50
|
+
|
51
|
+
def template_lang(template_path)
|
52
|
+
path = template_file_path(template_path)
|
53
|
+
Lookbook::Lang.guess(path) || Lookbook::Lang.find(:html)
|
54
|
+
end
|
55
|
+
|
56
|
+
def search_terms
|
57
|
+
[parent.label, label]
|
58
|
+
end
|
59
|
+
|
60
|
+
def url_path
|
61
|
+
lookbook_inspect_path(lookup_path)
|
62
|
+
end
|
63
|
+
|
64
|
+
def type
|
65
|
+
:scenario
|
66
|
+
end
|
67
|
+
|
68
|
+
alias_method :parent, :preview
|
69
|
+
alias_method :components, :render_targets
|
70
|
+
alias_method :component, :render_target
|
71
|
+
alias_method :lang, :source_lang
|
72
|
+
alias_method :examples, :scenarios
|
73
|
+
|
74
|
+
deprecate lang: :source_lang, deprecator: Deprecation
|
75
|
+
deprecate examples: :scenarios, deprecator: Deprecation
|
76
|
+
|
77
|
+
protected
|
78
|
+
|
79
|
+
def sort_handler(other_entity)
|
80
|
+
if Lookbook.config.preview_sort_scenarios
|
81
|
+
label <=> other_entity.label
|
82
|
+
else
|
83
|
+
[priority, label] <=> [other_entity.priority, other_entity.label]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def format_source(source)
|
88
|
+
source.sub(/^def \w+\s?(\([^)]+\))?/m, "").split("\n")[0..-2].join("\n")
|
89
|
+
end
|
90
|
+
|
91
|
+
def template_file_path(template_path)
|
92
|
+
return full_template_path(template_path) if respond_to?(:full_template_path, true)
|
93
|
+
|
94
|
+
search_dirs = [*Engine.preview_paths, *Engine.view_paths]
|
95
|
+
template_path = "#{template_path.to_s.sub(/\..*$/, "")}.html.*"
|
96
|
+
PathUtils.determine_full_path(template_path, search_dirs)
|
97
|
+
end
|
98
|
+
|
99
|
+
def load_render_targets
|
100
|
+
render_target_identifiers = [*fetch_config(:renders, []), *preview.send(:fetch_config, :renders, [])]
|
101
|
+
render_target_identifiers = preview.guess_render_targets if render_target_identifiers.empty?
|
102
|
+
|
103
|
+
render_targets = render_target_identifiers.map do |render_target|
|
104
|
+
RenderableEntity.new(render_target)
|
105
|
+
rescue NameError
|
106
|
+
Lookbook.logger.warn "#{render_target} was not found"
|
107
|
+
nil
|
108
|
+
end
|
109
|
+
render_targets.compact.uniq(&:lookup_path)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Lookbook
|
2
|
+
# Represents a group of preview scenarios within a preview class
|
3
|
+
#
|
4
|
+
# @ignore methods
|
5
|
+
# @api public
|
6
|
+
class ScenarioGroupEntity < Entity
|
7
|
+
include NavigableEntity
|
8
|
+
|
9
|
+
attr_reader :scenarios, :preview
|
10
|
+
|
11
|
+
def initialize(name, scenarios, preview)
|
12
|
+
@name = Utils.name(name)
|
13
|
+
@scenarios = ScenarioCollection.new(scenarios)
|
14
|
+
@preview = preview
|
15
|
+
@lookup_path = "#{parent.lookup_path}/#{@name}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def display_options
|
19
|
+
merged = {}
|
20
|
+
scenarios.to_a.reverse.map do |scenario|
|
21
|
+
merged.merge!(scenario.display_options)
|
22
|
+
end
|
23
|
+
merged
|
24
|
+
end
|
25
|
+
|
26
|
+
def render_targets
|
27
|
+
@_render_targets ||= RenderTargetCollection.new(scenarios.flat_map(&:render_targets).uniq(&:path))
|
28
|
+
end
|
29
|
+
|
30
|
+
def search_terms
|
31
|
+
[parent.label, label]
|
32
|
+
end
|
33
|
+
|
34
|
+
def tags(tag_name = nil)
|
35
|
+
scenarios.flat_map { |scenario| scenario.tags(tag_name) }
|
36
|
+
end
|
37
|
+
|
38
|
+
def tag(tag_name = nil)
|
39
|
+
tags(tag_name).first
|
40
|
+
end
|
41
|
+
|
42
|
+
def url_path
|
43
|
+
lookbook_inspect_path(lookup_path)
|
44
|
+
end
|
45
|
+
|
46
|
+
def type
|
47
|
+
:group
|
48
|
+
end
|
49
|
+
|
50
|
+
alias_method :parent, :preview
|
51
|
+
alias_method :components, :render_targets
|
52
|
+
end
|
53
|
+
end
|
data/lib/lookbook/error.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Lookbook
|
2
2
|
class Error < StandardError
|
3
|
-
delegate :full_message, :backtrace, :to_s, to: :
|
3
|
+
delegate :full_message, :backtrace, :to_s, to: :original
|
4
4
|
|
5
5
|
LINES_AROUND = 3
|
6
6
|
|
@@ -59,11 +59,11 @@ module Lookbook
|
|
59
59
|
end
|
60
60
|
|
61
61
|
def title
|
62
|
-
@title ||
|
62
|
+
@title || original.class.to_s
|
63
63
|
end
|
64
64
|
|
65
65
|
def message
|
66
|
-
(@message ||
|
66
|
+
(@message || original.message).gsub("(<unknown>):", "").strip.upcase_first
|
67
67
|
end
|
68
68
|
|
69
69
|
def file_name
|
@@ -80,7 +80,7 @@ module Lookbook
|
|
80
80
|
else
|
81
81
|
@file_path.presence || nil
|
82
82
|
end
|
83
|
-
path
|
83
|
+
path.nil? ? nil : path.to_s.delete_prefix("#{Rails.root}/")
|
84
84
|
end
|
85
85
|
|
86
86
|
def line_number
|
@@ -101,7 +101,7 @@ module Lookbook
|
|
101
101
|
|
102
102
|
protected
|
103
103
|
|
104
|
-
def
|
104
|
+
def original
|
105
105
|
@original.presence || self
|
106
106
|
end
|
107
107
|
|
@@ -1,46 +1,30 @@
|
|
1
1
|
module Lookbook
|
2
2
|
class FileWatcher
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
if paths.any?
|
14
|
-
opts = opts.to_h
|
15
|
-
opts[:only] = /\.(#{Array(extensions).join("|")})$/
|
16
|
-
|
17
|
-
listeners << init_listener(paths, opts, &block)
|
3
|
+
class << self
|
4
|
+
def new(...)
|
5
|
+
if evented?
|
6
|
+
Lookbook.logger.debug "Using `EventedFileUpdateChecker` for file watching"
|
7
|
+
else
|
8
|
+
Lookbook.logger.debug "The 'listen' gem was not found. Using `FileUpdateChecker` for file watching"
|
9
|
+
end
|
10
|
+
|
11
|
+
file_watcher.new(...)
|
18
12
|
end
|
19
|
-
end
|
20
13
|
|
21
|
-
|
22
|
-
|
23
|
-
Lookbook.logger.debug "Starting listeners"
|
24
|
-
listeners.each { |l| l.start }
|
14
|
+
def evented?
|
15
|
+
!(file_watcher <= ActiveSupport::FileUpdateChecker)
|
25
16
|
end
|
26
|
-
end
|
27
17
|
|
28
|
-
|
29
|
-
if listeners.any?
|
30
|
-
Lookbook.logger.debug "Stopping listeners"
|
31
|
-
listeners.each { |l| l.stop }
|
32
|
-
end
|
33
|
-
end
|
18
|
+
protected
|
34
19
|
|
35
|
-
|
20
|
+
def file_watcher
|
21
|
+
@_file_watcher ||= begin
|
22
|
+
require_relative "./support/evented_file_update_checker"
|
36
23
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
force_polling: force_polling
|
42
|
-
) do |modified, added, removed|
|
43
|
-
block.call({modified: modified, added: added, removed: removed})
|
24
|
+
EventedFileUpdateChecker
|
25
|
+
rescue LoadError
|
26
|
+
ActiveSupport::FileUpdateChecker
|
27
|
+
end
|
44
28
|
end
|
45
29
|
end
|
46
30
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Lookbook
|
2
|
+
module ClassNamesHelper
|
3
|
+
def build_tag_values(*args)
|
4
|
+
tag_values = []
|
5
|
+
|
6
|
+
args.each do |tag_value|
|
7
|
+
case tag_value
|
8
|
+
when Hash
|
9
|
+
tag_value.each do |key, val|
|
10
|
+
tag_values << key.to_s if val && key.present?
|
11
|
+
end
|
12
|
+
when Array
|
13
|
+
tag_values.concat build_tag_values(*tag_value)
|
14
|
+
else
|
15
|
+
tag_values << tag_value.to_s if tag_value.present?
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
tag_values
|
20
|
+
end
|
21
|
+
|
22
|
+
def class_names(*args)
|
23
|
+
tokens = build_tag_values(*args).flat_map { |value| value.to_s.split(/\s+/) }.uniq
|
24
|
+
|
25
|
+
safe_join(tokens, " ")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Lookbook
|
2
|
+
# Helpers for documentation page templates
|
3
|
+
#
|
4
|
+
# @api public
|
5
|
+
module PageHelper
|
6
|
+
# Returns the URL path to a page.
|
7
|
+
#
|
8
|
+
# @param id [String, PageEntity] The id or PageEntity instance to generate a URL path for
|
9
|
+
def page_path(id)
|
10
|
+
page = id.is_a?(PageEntity) ? id : Engine.pages.find_by_id(id)
|
11
|
+
if page.present?
|
12
|
+
lookbook_page_path page.lookup_path
|
13
|
+
else
|
14
|
+
Lookbook.logger.warn "Could not find page with id ':#{id}'"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
module Lookbook
|
2
|
+
# Helpers for rendering UI elements.
|
3
|
+
#
|
4
|
+
# These are available for use in documentation page templates
|
5
|
+
# and custom preview inspector panel templates.
|
6
|
+
#
|
7
|
+
# @api public
|
8
|
+
module UiElementsHelper
|
9
|
+
# Render an icon.
|
10
|
+
#
|
11
|
+
# Lookbook uses icons from the [Lucide Icons](https://lucide.dev/) set and
|
12
|
+
# a full list of available icon names can be found on that site.
|
13
|
+
#
|
14
|
+
# @example
|
15
|
+
# <%= icon :trash %>
|
16
|
+
# <%= icon :camera, size: 6, style: "color: red;" %>
|
17
|
+
#
|
18
|
+
# @param name [Symbol, String] Name of the icon
|
19
|
+
# @param opts [Hash] Options hash
|
20
|
+
def icon(name, **opts)
|
21
|
+
lookbook_render :icon, name: name, **opts
|
22
|
+
end
|
23
|
+
|
24
|
+
# Display a syntax-highlighted block of code.
|
25
|
+
#
|
26
|
+
# An alternative to using markdown code blocks for templates that have
|
27
|
+
# markdown parsing disabled, or for when more control is required.
|
28
|
+
#
|
29
|
+
# @param language [Symbol] Which language the code is written in
|
30
|
+
# @param opts [Hash] Options hash
|
31
|
+
# @param block [Proc] Code block
|
32
|
+
def code(language = :html, **opts, &block)
|
33
|
+
opts[:language] ||= language
|
34
|
+
lookbook_render :code, **opts, &block
|
35
|
+
end
|
36
|
+
|
37
|
+
# Render a 'live' embed of a component preview.
|
38
|
+
#
|
39
|
+
# If no scenario name is provided then the default (first) preview
|
40
|
+
# scenario will be rendered in the embed.
|
41
|
+
#
|
42
|
+
# @param preview [String] Name of the preview class to embed
|
43
|
+
# @param scenario [String] Example method name
|
44
|
+
# @param opts [Hash] Options hash
|
45
|
+
def embed(preview, scenario = nil, **opts)
|
46
|
+
preview_entity = if preview.is_a?(Symbol)
|
47
|
+
Engine.previews.find_by_path(preview)
|
48
|
+
else
|
49
|
+
Engine.previews.find_by_preview_class(preview)
|
50
|
+
end
|
51
|
+
scenario_entity = scenario ? preview_entity&.scenario(scenario) : preview_entity&.default_scenario
|
52
|
+
opts[:actions] ||= ["inspect", "open"]
|
53
|
+
|
54
|
+
lookbook_render Embed::Component.new(
|
55
|
+
scenario: scenario_entity,
|
56
|
+
params: opts.fetch(:params, {}),
|
57
|
+
options: opts.except(:params)
|
58
|
+
)
|
59
|
+
end
|
60
|
+
|
61
|
+
# @api private
|
62
|
+
def prose(**opts, &block)
|
63
|
+
lookbook_render :prose, **opts, &block
|
64
|
+
end
|
65
|
+
|
66
|
+
# @api private
|
67
|
+
def lookbook_tag(tag = :div, **attrs, &block)
|
68
|
+
lookbook_render :tag, tag: tag, **attrs, &block
|
69
|
+
end
|
70
|
+
|
71
|
+
# @api private
|
72
|
+
def lookbook_render(ref, **attrs, &block)
|
73
|
+
comp = if ref.is_a? ViewComponent::Base
|
74
|
+
ref
|
75
|
+
else
|
76
|
+
klass = component_class(ref)
|
77
|
+
attrs.key?(:content) ? klass.new(**attrs.except(:content)).with_content(attrs[:content]) : klass.new(**attrs)
|
78
|
+
end
|
79
|
+
if block && !attrs.key?(:content)
|
80
|
+
public_send render_method_name, comp, &block
|
81
|
+
else
|
82
|
+
public_send render_method_name, comp
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
COMPONENT_CLASSES = {} # cache for constantized references
|
89
|
+
|
90
|
+
# @api private
|
91
|
+
def render_method_name
|
92
|
+
if Rails.application.config.view_component.render_monkey_patch_enabled || Rails.version.to_f >= 6.1
|
93
|
+
:render
|
94
|
+
else
|
95
|
+
:render_component
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# @api private
|
100
|
+
def component_class(ref)
|
101
|
+
klass = COMPONENT_CLASSES[ref]
|
102
|
+
if klass.nil?
|
103
|
+
ref = ref.to_s.tr("-", "_")
|
104
|
+
class_namespace = ref.camelize
|
105
|
+
begin
|
106
|
+
klass = "Lookbook::#{class_namespace}::Component".constantize
|
107
|
+
rescue
|
108
|
+
klass = "Lookbook::#{class_namespace}Component".constantize
|
109
|
+
end
|
110
|
+
COMPONENT_CLASSES[ref] = klass
|
111
|
+
end
|
112
|
+
klass
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Lookbook
|
2
|
+
class Preview
|
3
|
+
include ActionView::Helpers::TagHelper
|
4
|
+
include ActionView::Helpers::AssetTagHelper
|
5
|
+
|
6
|
+
def render(component = nil, **args, &block)
|
7
|
+
if component.nil?
|
8
|
+
{
|
9
|
+
type: :view,
|
10
|
+
template: args[:template] || Lookbook.config.preview_template,
|
11
|
+
args: args,
|
12
|
+
locals: args[:locals] || {},
|
13
|
+
block: block
|
14
|
+
}
|
15
|
+
else
|
16
|
+
{
|
17
|
+
type: component.is_a?(String) ? :view : :component,
|
18
|
+
args: args,
|
19
|
+
block: block,
|
20
|
+
component: component,
|
21
|
+
locals: {},
|
22
|
+
template: Lookbook.config.preview_template
|
23
|
+
}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def render_with_template(template: nil, locals: nil)
|
28
|
+
{
|
29
|
+
type: :template,
|
30
|
+
template: template,
|
31
|
+
locals: locals.to_h
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
alias_method :render_component, :render
|
36
|
+
|
37
|
+
class << self
|
38
|
+
# Returns the arguments for rendering of the component in its layout
|
39
|
+
def render_args(scenario, params: {})
|
40
|
+
scenario_params_names = instance_method(scenario).parameters.map(&:last)
|
41
|
+
provided_params = params.slice(*scenario_params_names).to_h.symbolize_keys
|
42
|
+
result = provided_params.empty? ? new.public_send(scenario) : new.public_send(scenario, **provided_params)
|
43
|
+
result ||= {}
|
44
|
+
result[:template] = scenario_template_path(scenario) if result[:template].nil?
|
45
|
+
@layout = nil unless defined?(@layout)
|
46
|
+
result.merge(layout: @layout)
|
47
|
+
end
|
48
|
+
|
49
|
+
# rubocop:disable Style/TrivialAccessors
|
50
|
+
def layout(layout_name)
|
51
|
+
@layout = layout_name
|
52
|
+
end
|
53
|
+
# rubocop:enable Style/TrivialAccessors
|
54
|
+
|
55
|
+
# Returns the relative path (from preview_path) to the scenario template if the template exists
|
56
|
+
def scenario_template_path(scenario)
|
57
|
+
preview_name = name.chomp("Preview").underscore
|
58
|
+
preview_path =
|
59
|
+
Engine.preview_paths.detect do |path|
|
60
|
+
Dir["#{path}/#{preview_name}_preview/#{scenario}.html.*"].first
|
61
|
+
end
|
62
|
+
|
63
|
+
if preview_path.nil?
|
64
|
+
raise(
|
65
|
+
PreviewTemplateError,
|
66
|
+
"A preview template for scenario #{scenario} doesn't exist.\n\n" \
|
67
|
+
"To fix this issue, create a template for the scenario."
|
68
|
+
)
|
69
|
+
end
|
70
|
+
|
71
|
+
path = Dir["#{preview_path}/#{preview_name}_preview/#{scenario}.html.*"].first
|
72
|
+
Pathname.new(path)
|
73
|
+
.relative_path_from(Pathname.new(preview_path))
|
74
|
+
.to_s
|
75
|
+
.sub(/\..*$/, "")
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Lookbook
|
2
|
+
module PreviewControllerActions
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
helper PreviewHelper
|
7
|
+
prepend_view_path Engine.root.join("app/views")
|
8
|
+
before_action :permit_embeds
|
9
|
+
|
10
|
+
def render_scenario_to_string(preview, scenario_name)
|
11
|
+
prepend_application_view_paths
|
12
|
+
prepend_preview_examples_view_path
|
13
|
+
|
14
|
+
@preview = preview
|
15
|
+
@scenario_name = scenario_name
|
16
|
+
@render_args = @preview.render_args(@scenario_name, params: params.permit!)
|
17
|
+
template = @render_args[:template]
|
18
|
+
locals = @render_args[:locals]
|
19
|
+
opts = {}
|
20
|
+
opts[:layout] = nil
|
21
|
+
opts[:locals] = locals if locals.present?
|
22
|
+
|
23
|
+
with_optional_action_view_annotations do
|
24
|
+
render html: render_to_string(template, **opts)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def render_in_layout_to_string(template, locals, opts = {})
|
29
|
+
with_optional_action_view_annotations do
|
30
|
+
html = render_to_string(template, locals: locals, **determine_layout(opts[:layout]))
|
31
|
+
if opts[:append_html].present?
|
32
|
+
html += opts[:append_html]
|
33
|
+
end
|
34
|
+
render html: html
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
protected
|
39
|
+
|
40
|
+
def with_optional_action_view_annotations(&block)
|
41
|
+
disable = Lookbook.config.preview_disable_action_view_annotations
|
42
|
+
ActionViewAnnotationsHandler.call(disable_annotations: disable, &block)
|
43
|
+
end
|
44
|
+
|
45
|
+
def permit_embeds
|
46
|
+
headers["X-Frame-Options"] = Lookbook.config.preview_embeds.policy
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -12,12 +12,14 @@ module Lookbook
|
|
12
12
|
YARD::Parser::SourceParser.after_parse_list { run_callbacks }
|
13
13
|
end
|
14
14
|
|
15
|
-
def parse(&block)
|
15
|
+
def parse(files = nil, &block)
|
16
16
|
unless @parsing
|
17
17
|
@parsing = true
|
18
18
|
@after_parse_once_callbacks << block if block
|
19
|
+
files_list = files ? files.select { |file| file.to_s.end_with?(".rb") } : paths
|
20
|
+
|
19
21
|
YARD::Registry.clear
|
20
|
-
YARD.parse(
|
22
|
+
YARD.parse(files_list)
|
21
23
|
end
|
22
24
|
end
|
23
25
|
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require "active_support"
|
2
|
+
|
3
|
+
module Lookbook
|
4
|
+
class Reloaders
|
5
|
+
attr_reader :reloaders
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@reloaders = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def add(name, directories, extensions, &callback)
|
12
|
+
reloader = Reloader.new(name, directories, extensions, &callback)
|
13
|
+
reloaders.push(reloader)
|
14
|
+
|
15
|
+
if Engine.reloading?
|
16
|
+
Rails.application.reloaders << reloader
|
17
|
+
Rails.application.reloader.to_run { reloader.execute_if_updated }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def execute
|
22
|
+
reloaders.each { |reloader| reloader.execute }
|
23
|
+
end
|
24
|
+
|
25
|
+
def register_changes(changes)
|
26
|
+
reloader = reloaders.find { |reloader| reloader.watching?(changes) }
|
27
|
+
reloader.last_changes = changes if reloader
|
28
|
+
end
|
29
|
+
|
30
|
+
class Reloader
|
31
|
+
delegate :execute, :execute_if_updated, :updated?, to: :file_watcher
|
32
|
+
|
33
|
+
attr_reader :name, :directories, :extensions, :callback
|
34
|
+
attr_accessor :last_changes
|
35
|
+
|
36
|
+
def initialize(name, directories, extensions, &callback)
|
37
|
+
@name = name.to_sym
|
38
|
+
@directories = directories
|
39
|
+
@extensions = extensions
|
40
|
+
@callback = callback
|
41
|
+
@last_changes = nil
|
42
|
+
end
|
43
|
+
|
44
|
+
def watching?(changes)
|
45
|
+
file_paths = changes.to_h.values.flatten
|
46
|
+
!!file_paths.find do |file_path|
|
47
|
+
file_path = Pathname(file_path).expand_path
|
48
|
+
directories.find do |dir_path|
|
49
|
+
matcher = File.expand_path(File.join(dir_path, "**"))
|
50
|
+
file_path.fnmatch?(matcher)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
protected
|
56
|
+
|
57
|
+
def file_watcher
|
58
|
+
return @_file_watcher if @_file_watcher
|
59
|
+
|
60
|
+
to_watch = directories.each_with_object({}) do |directory, result|
|
61
|
+
result[directory] = extensions
|
62
|
+
end
|
63
|
+
|
64
|
+
@_file_watcher ||= FileWatcher.new([], to_watch) do
|
65
|
+
callback.call(@last_changes)
|
66
|
+
@last_changes = nil
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|