lookbook 0.6.1 → 0.7.2.beta.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +233 -5
  3. data/app/assets/lookbook/css/app.css +15 -3
  4. data/app/assets/lookbook/js/app.js +8 -0
  5. data/app/assets/lookbook/js/components/app.js +55 -0
  6. data/app/assets/lookbook/js/components/embed.js +89 -0
  7. data/app/assets/lookbook/js/components/filter.js +13 -2
  8. data/app/assets/lookbook/js/components/nav-group.js +3 -6
  9. data/app/assets/lookbook/js/components/nav-item.js +3 -1
  10. data/app/assets/lookbook/js/components/nav.js +8 -15
  11. data/app/assets/lookbook/js/components/page.js +19 -41
  12. data/app/assets/lookbook/js/components/sidebar.js +16 -1
  13. data/app/assets/lookbook/js/components/splitter.js +1 -1
  14. data/app/assets/lookbook/js/components/tabs.js +3 -1
  15. data/app/assets/lookbook/js/embed.js +1 -0
  16. data/app/assets/lookbook/js/lib/split.js +0 -6
  17. data/app/assets/lookbook/js/stores/layout.js +3 -0
  18. data/app/assets/lookbook/js/stores/pages.js +5 -0
  19. data/app/assets/lookbook/js/stores/sidebar.js +2 -1
  20. data/app/controllers/lookbook/application_controller.rb +28 -0
  21. data/app/controllers/lookbook/page_controller.rb +20 -0
  22. data/app/controllers/lookbook/pages_controller.rb +47 -0
  23. data/app/controllers/lookbook/{app_controller.rb → previews_controller.rb} +39 -70
  24. data/app/helpers/lookbook/application_helper.rb +8 -61
  25. data/app/helpers/lookbook/component_helper.rb +40 -0
  26. data/app/helpers/lookbook/output_helper.rb +15 -0
  27. data/app/helpers/lookbook/page_helper.rb +46 -0
  28. data/app/helpers/lookbook/preview_helper.rb +1 -1
  29. data/app/views/layouts/lookbook/application.html.erb +30 -0
  30. data/app/views/layouts/lookbook/basic.html.erb +7 -0
  31. data/app/views/layouts/lookbook/preview.html.erb +5 -1
  32. data/app/views/layouts/lookbook/skeleton.html.erb +28 -0
  33. data/app/views/lookbook/components/_branding.html.erb +8 -0
  34. data/app/views/lookbook/components/_code.html.erb +2 -2
  35. data/app/views/lookbook/components/_copy_button.html.erb +11 -0
  36. data/app/views/lookbook/components/_drawer.html.erb +4 -16
  37. data/app/views/lookbook/components/_embed.html.erb +39 -0
  38. data/app/views/lookbook/components/_errors.html.erb +13 -0
  39. data/app/views/lookbook/components/_filter.html.erb +8 -5
  40. data/app/views/lookbook/components/_header.html.erb +2 -4
  41. data/app/views/lookbook/components/_icon.html.erb +2 -2
  42. data/app/views/lookbook/components/_nav.html.erb +7 -8
  43. data/app/views/lookbook/components/_nav_group.html.erb +1 -1
  44. data/app/views/lookbook/components/_nav_item.html.erb +4 -3
  45. data/app/views/lookbook/components/_nav_page.html.erb +22 -0
  46. data/app/views/lookbook/components/_nav_preview.html.erb +1 -1
  47. data/app/views/lookbook/components/_not_found.html.erb +11 -0
  48. data/app/views/lookbook/components/_param.html.erb +1 -1
  49. data/app/views/lookbook/components/_preview.html.erb +17 -11
  50. data/app/views/lookbook/components/_sidebar.html.erb +69 -0
  51. data/app/views/lookbook/error.html.erb +31 -1
  52. data/app/views/lookbook/pages/not_found.html.erb +15 -0
  53. data/app/views/lookbook/pages/show.html.erb +71 -0
  54. data/app/views/lookbook/previews/error.html.erb +1 -0
  55. data/app/views/lookbook/{inputs → previews/inputs}/_select.html.erb +0 -0
  56. data/app/views/lookbook/{inputs → previews/inputs}/_text.html.erb +0 -0
  57. data/app/views/lookbook/{inputs → previews/inputs}/_textarea.html.erb +0 -0
  58. data/app/views/lookbook/{inputs → previews/inputs}/_toggle.html.erb +0 -0
  59. data/app/views/lookbook/{not_found.html.erb → previews/not_found.html.erb} +2 -2
  60. data/app/views/lookbook/{panels → previews/panels}/_notes.html.erb +2 -2
  61. data/app/views/lookbook/{panels → previews/panels}/_output.html.erb +0 -0
  62. data/app/views/lookbook/{panels → previews/panels}/_params.html.erb +0 -0
  63. data/app/views/lookbook/{panels → previews/panels}/_preview.html.erb +0 -0
  64. data/app/views/lookbook/{panels → previews/panels}/_source.html.erb +0 -0
  65. data/app/views/lookbook/{show.html.erb → previews/show.html.erb} +0 -0
  66. data/config/routes.rb +9 -4
  67. data/lib/lookbook/code_formatter.rb +22 -1
  68. data/lib/lookbook/code_inspector.rb +73 -0
  69. data/lib/lookbook/collection.rb +110 -8
  70. data/lib/lookbook/engine.rb +63 -37
  71. data/lib/lookbook/error.rb +52 -0
  72. data/lib/lookbook/features.rb +1 -1
  73. data/lib/lookbook/markdown.rb +31 -0
  74. data/lib/lookbook/page.rb +158 -0
  75. data/lib/lookbook/page_collection.rb +11 -0
  76. data/lib/lookbook/parser.rb +2 -0
  77. data/lib/lookbook/preview.rb +111 -57
  78. data/lib/lookbook/preview_collection.rb +15 -0
  79. data/lib/lookbook/preview_example.rb +27 -29
  80. data/lib/lookbook/preview_group.rb +12 -6
  81. data/lib/lookbook/utils.rb +74 -0
  82. data/lib/lookbook/version.rb +1 -1
  83. data/lib/lookbook.rb +19 -1
  84. data/public/lookbook-assets/css/app.css +1 -1
  85. data/public/lookbook-assets/css/app.css.map +1 -1
  86. data/public/lookbook-assets/js/app.js +1 -1
  87. data/public/lookbook-assets/js/app.js.map +1 -1
  88. data/public/lookbook-assets/js/embed.js +2 -0
  89. data/public/lookbook-assets/js/embed.js.map +1 -0
  90. metadata +48 -19
  91. data/app/assets/lookbook/js/lib/utils.js +0 -3
  92. data/app/views/layouts/lookbook/app.html.erb +0 -60
  93. data/lib/lookbook/taggable.rb +0 -46
@@ -1,47 +1,25 @@
1
- import createSocket from "../lib/socket";
2
-
3
- const morphOpts = {
4
- key(el) {
5
- return el.getAttribute("key") ? el.getAttribute("key") : el.id;
6
- },
7
- updating(el, toEl, childrenOnly, skip) {
8
- if (
9
- el.getAttribute &&
10
- el.getAttribute("data-morph-strategy") === "replace"
11
- ) {
12
- el.innerHTML = toEl.innerHTML;
13
- return skip();
14
- }
15
- },
16
- lookahead: true,
17
- };
18
-
19
1
  export default function page() {
20
2
  return {
21
- init() {
22
- const socket = createSocket(window.SOCKET_PATH);
23
- socket.addListener("Lookbook::ReloadChannel", () => this.refresh());
24
- },
25
- async update() {
26
- const response = await fetch(window.document.location);
27
- if (!response.ok) return window.location.reload();
28
- const html = await response.text();
29
- const newDoc = new DOMParser().parseFromString(html, "text/html");
30
- this.morph(newDoc);
31
- document.title = newDoc.title;
32
- },
33
- setLocation(loc) {
34
- const path = loc instanceof Event ? loc.currentTarget.href : loc;
35
- history.pushState({}, null, path);
36
- this.$dispatch("popstate");
3
+ init() {},
4
+ scrollToTop() {
5
+ this.$refs.scroller.scrollTop = 0;
37
6
  },
38
- refresh() {
39
- this.$dispatch("popstate");
40
- },
41
- morph(dom) {
42
- const pageHtml = dom.getElementById(this.$root.id).outerHTML;
43
- Alpine.morph(this.$root, pageHtml, morphOpts);
44
- this.$dispatch("page:morphed");
7
+ checkForNavigation(event) {
8
+ const link = event.target.closest("a[href]");
9
+ if (
10
+ link &&
11
+ !isExternalLink(link.href) &&
12
+ link.getAttribute("target") !== "_blank"
13
+ ) {
14
+ event.preventDefault();
15
+ this.setLocation(link.href);
16
+ }
45
17
  },
46
18
  };
47
19
  }
20
+
21
+ function isExternalLink(url) {
22
+ const tmp = document.createElement("a");
23
+ tmp.href = url;
24
+ return tmp.host !== window.location.host;
25
+ }
@@ -1,3 +1,18 @@
1
1
  export default function sidebar() {
2
- return {};
2
+ return {
3
+ init() {
4
+ this.$nextTick(() => this.setActiveNavItem());
5
+ },
6
+ setActiveNavItem() {
7
+ const target = this.$el.querySelector(
8
+ `[data-path="${window.location.pathname}"]`
9
+ );
10
+ this.$store.nav.active = target ? target.id : "";
11
+ },
12
+ setSplits(splits) {
13
+ if (splits.length) {
14
+ this.$store.sidebar.panelSplits = [splits[0] || 1.0, splits[2] || 1.0];
15
+ }
16
+ },
17
+ };
3
18
  }
@@ -11,7 +11,7 @@ export default function splitter(direction, props = {}) {
11
11
  minSize: props.minSize || 0,
12
12
  writeStyle() {},
13
13
  onDrag: (dir, track, style) => {
14
- this.splits = style.split(" ").map((num) => parseInt(num));
14
+ this.splits = style.split(" ").map((num) => parseFloat(num, 10));
15
15
  },
16
16
  onDragStart: () => {
17
17
  this.$store.layout.reflowing = true;
@@ -19,7 +19,9 @@ export default function tabs() {
19
19
  });
20
20
  },
21
21
  get tabs() {
22
- return Array.from(this.$refs.tabs.querySelectorAll(":scope > a"));
22
+ return Array.from(
23
+ this.$refs.tabs ? this.$refs.tabs.querySelectorAll(":scope > a") : []
24
+ );
23
25
  },
24
26
  get visibleTabCount() {
25
27
  let cumulativeWidth = 0;
@@ -0,0 +1 @@
1
+ import "iframe-resizer/js/iframeResizer.contentWindow";
@@ -11,11 +11,5 @@ export default function (element, props) {
11
11
  const splits = style.split(" ").map((num) => parseInt(num));
12
12
  props.onDrag(splits);
13
13
  },
14
- // onDragStart() {
15
- // this.reflowing = true;
16
- // },
17
- // onDragEnd() {
18
- // this.reflowing = false;
19
- // },
20
14
  });
21
15
  }
@@ -8,5 +8,8 @@ export default function createLayoutStore() {
8
8
  reflowing: false,
9
9
  desktop: true,
10
10
  desktopWidth: config.desktopWidth,
11
+ get mobile() {
12
+ return !this.desktop;
13
+ },
11
14
  };
12
15
  }
@@ -0,0 +1,5 @@
1
+ export default function createNavStore(Alpine) {
2
+ return {
3
+ embeds: {},
4
+ };
5
+ }
@@ -3,8 +3,9 @@ import config from "../config";
3
3
  export default function createSidebarStore(Alpine) {
4
4
  const { defaultWidth, minWidth, maxWidth } = config.sidebar;
5
5
  return {
6
- open: Alpine.$persist(false).as("sidebar-open"),
6
+ open: Alpine.$persist(true).as("sidebar-open"),
7
7
  width: Alpine.$persist(defaultWidth).as("sidebar-width"),
8
+ panelSplits: Alpine.$persist([1.0, 1.0]).as(`sidebar-panel-splits`),
8
9
  minWidth,
9
10
  maxWidth,
10
11
  toggle() {
@@ -0,0 +1,28 @@
1
+ module Lookbook
2
+ class ApplicationController < ActionController::Base
3
+ protect_from_forgery with: :exception
4
+
5
+ helper Lookbook::ApplicationHelper
6
+ helper Lookbook::OutputHelper
7
+ helper Lookbook::ComponentHelper
8
+
9
+ def self.controller_path
10
+ "lookbook"
11
+ end
12
+
13
+ def index
14
+ if feature_enabled? :pages
15
+ landing = Lookbook.pages.find(&:landing) || Lookbook.pages.first
16
+ if landing.present?
17
+ redirect_to page_path(landing.lookup_path)
18
+ end
19
+ end
20
+ end
21
+
22
+ protected
23
+
24
+ def feature_enabled?(feature)
25
+ Lookbook::Features.enabled?(feature)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,20 @@
1
+ module Lookbook
2
+ class PageController < ActionController::Base
3
+ helper Lookbook::ComponentHelper
4
+ helper Lookbook::PageHelper
5
+ helper Lookbook::OutputHelper
6
+
7
+ Lookbook.config.page_paths.each do |path|
8
+ prepend_view_path Rails.root.join(path)
9
+ end
10
+
11
+ def render_page(page, locals = {})
12
+ @page = page
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
17
+ @page.markdown? ? Lookbook::Markdown.render(content) : content
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,47 @@
1
+ module Lookbook
2
+ class PagesController < ApplicationController
3
+ def self.controller_path
4
+ "lookbook/pages"
5
+ end
6
+
7
+ def index
8
+ landing = Lookbook.pages.find(&:landing) || Lookbook.pages.first
9
+ if landing.present?
10
+ redirect_to page_path landing.lookup_path
11
+ else
12
+ @title = "Not found"
13
+ render "not_found"
14
+ end
15
+ end
16
+
17
+ def show
18
+ @pages = Lookbook.pages
19
+ @page = @pages.find_by_path(params[:path])
20
+ if @page
21
+ if @page.errors.any?
22
+ render "lookbook/error", locals: {error: @page.errors.first}
23
+ else
24
+ begin
25
+ @page_content = page_controller.render_page(@page)
26
+ @next_page = @pages.find_next(@page)
27
+ @previous_page = @pages.find_previous(@page)
28
+ @title = @page.title
29
+ rescue => exception
30
+ render "lookbook/error", locals: {error: exception}
31
+ end
32
+ end
33
+ else
34
+ render "not_found"
35
+ end
36
+ end
37
+
38
+ protected
39
+
40
+ def page_controller
41
+ controller_class = Lookbook.config.page_controller.constantize
42
+ controller = controller_class.new
43
+ controller.request = request
44
+ controller
45
+ end
46
+ end
47
+ end
@@ -1,24 +1,22 @@
1
1
  module Lookbook
2
- class AppController < ActionController::Base
3
- EXCEPTIONS = [ViewComponent::PreviewTemplateError, ViewComponent::ComponentError, ViewComponent::TemplateError, ActionView::Template::Error]
4
-
5
- protect_from_forgery with: :exception
6
- helper Lookbook::ApplicationHelper
7
-
8
- before_action :find_preview, only: [:preview, :show]
9
- before_action :find_example, only: [:preview, :show]
10
- before_action :build_nav
11
-
2
+ class PreviewsController < ApplicationController
12
3
  def self.controller_path
13
- "lookbook"
4
+ "lookbook/previews"
14
5
  end
15
6
 
7
+ before_action :lookup_entities, only: [:preview, :show]
8
+ before_action :set_title
9
+
16
10
  def preview
17
11
  if @example
18
12
  set_params
19
- render html: render_examples(examples_data)
13
+ begin
14
+ render html: render_examples(examples_data)
15
+ rescue => exception
16
+ render_in_layout "lookbook/error", layout: "lookbook/basic", error: exception, disable_header: true
17
+ end
20
18
  else
21
- render "not_found"
19
+ render_in_layout "not_found"
22
20
  end
23
21
  end
24
22
 
@@ -29,34 +27,30 @@ module Lookbook
29
27
  @examples = examples_data
30
28
  @drawer_panels = drawer_panels.filter { |name, panel| panel[:show] }
31
29
  @preview_panels = preview_panels.filter { |name, panel| panel[:show] }
32
- rescue *EXCEPTIONS
33
- render "error"
30
+ rescue => exception
31
+ render_in_layout "lookbook/error", error: exception
34
32
  end
35
33
  else
36
- render "not_found"
34
+ render_in_layout "not_found"
37
35
  end
38
36
  end
39
37
 
40
38
  private
41
39
 
42
- def find_preview
43
- candidates = []
44
- params[:path].to_s.scan(%r{/|$}) { candidates << $` }
45
- match = candidates.reverse.detect { |candidate| Lookbook::Preview.exists?(candidate) }
46
- @preview = match ? Lookbook::Preview.find(match) : nil
47
- end
48
-
49
- def find_example
50
- @example = if @preview
51
- if params[:path] == @preview.lookbook_path
52
- redirect_to show_path "#{params[:path]}/#{@preview.lookbook_examples.first.name}"
53
- else
54
- @example_name = File.basename(params[:path])
55
- @preview.lookbook_example(@example_name)
40
+ def lookup_entities
41
+ @example = Lookbook.previews.find_example(params[:path])
42
+ if @example
43
+ @preview = @example.preview
44
+ if params[:path] == @preview&.lookup_path
45
+ redirect_to show_path "#{params[:path]}/#{@preview.default_example.name}"
56
46
  end
57
47
  end
58
48
  end
59
49
 
50
+ def set_title
51
+ @title = @example.present? ? [@example&.label, @preview&.label].compact.join(" :: ") : "Not found"
52
+ end
53
+
60
54
  def examples_data
61
55
  @examples_data ||= (@example.type == :group ? @example.examples : [@example]).map do |example|
62
56
  example_data(example)
@@ -72,23 +66,22 @@ module Lookbook
72
66
  html: preview_controller.render_example_to_string(@preview, example.name),
73
67
  source: has_template ? example.template_source(render_args[:template]) : example.method_source,
74
68
  source_lang: has_template ? example.template_lang(render_args[:template]) : example.source_lang,
75
- params: enabled?(:params) ? example.params : []
69
+ params: example.params
76
70
  }
77
71
  end
78
72
 
79
73
  def render_examples(examples)
80
- preview_controller.render_in_layout_to_string("layouts/lookbook/preview", {examples: examples}, @preview.lookbook_layout)
74
+ preview_controller.render_in_layout_to_string("layouts/lookbook/preview", {examples: examples}, @preview.layout)
81
75
  end
82
76
 
83
77
  def set_params
84
- if enabled?(:params)
85
- # cast known params to type
86
- @example.params.each do |param|
87
- if preview_controller.params.key?(param[:name])
88
- preview_controller.params[param[:name]] = Lookbook::Params.cast(preview_controller.params[param[:name]], param[:type])
89
- end
78
+ # cast known params to type
79
+ @example.params.each do |param|
80
+ if preview_controller.params.key?(param[:name])
81
+ preview_controller.params[param[:name]] = Lookbook::Params.cast(preview_controller.params[param[:name]], param[:type])
90
82
  end
91
83
  end
84
+
92
85
  # set display params
93
86
  preview_controller.params.merge!({
94
87
  lookbook: {
@@ -97,31 +90,11 @@ module Lookbook
97
90
  })
98
91
  end
99
92
 
100
- def build_nav
101
- @nav = Collection.new
102
- previews.reject { |p| p.hidden? }.each do |preview|
103
- current = @nav
104
- if preview.hierarchy_depth == 1
105
- current.add(preview)
106
- else
107
- preview.lookbook_parent_collections.each.with_index(1) do |name, i|
108
- target = current.get_or_create(name)
109
- if preview.hierarchy_depth == i + 1
110
- target.add(preview)
111
- else
112
- current = target
113
- end
114
- end
115
- end
116
- end
117
- @nav
118
- end
119
-
120
93
  def preview_panels
121
94
  {
122
95
  preview: {
123
96
  label: "Preview",
124
- template: "lookbook/panels/preview",
97
+ template: "lookbook/previews/panels/preview",
125
98
  srcdoc: Lookbook.config.preview_srcdoc ? render_examples(examples_data).gsub("\"", "&quot;") : nil,
126
99
  hotkey: "v",
127
100
  show: true,
@@ -130,7 +103,7 @@ module Lookbook
130
103
  },
131
104
  output: {
132
105
  label: "HTML",
133
- template: "lookbook/panels/output",
106
+ template: "lookbook/previews/panels/output",
134
107
  hotkey: "o",
135
108
  show: true,
136
109
  disabled: false,
@@ -143,7 +116,7 @@ module Lookbook
143
116
  {
144
117
  source: {
145
118
  label: "Source",
146
- template: "lookbook/panels/source",
119
+ template: "lookbook/previews/panels/source",
147
120
  hotkey: "s",
148
121
  show: true,
149
122
  disabled: false,
@@ -151,25 +124,21 @@ module Lookbook
151
124
  },
152
125
  notes: {
153
126
  label: "Notes",
154
- template: "lookbook/panels/notes",
127
+ template: "lookbook/previews/panels/notes",
155
128
  hotkey: "n",
156
129
  show: true,
157
130
  disabled: @examples.filter { |e| e[:notes].present? }.none?
158
131
  },
159
132
  params: {
160
133
  label: "Params",
161
- template: "lookbook/panels/params",
134
+ template: "lookbook/previews/panels/params",
162
135
  hotkey: "p",
163
- show: enabled?(:params),
136
+ show: true,
164
137
  disabled: @example.type == :group || @example.params.none?
165
138
  }
166
139
  }
167
140
  end
168
141
 
169
- def previews
170
- Lookbook::Preview.all.sort_by(&:label)
171
- end
172
-
173
142
  def preview_controller
174
143
  return @preview_controller if @preview_controller.present?
175
144
  controller_class = Lookbook.config.preview_controller.constantize
@@ -180,8 +149,8 @@ module Lookbook
180
149
  @preview_controller ||= controller
181
150
  end
182
151
 
183
- def enabled?(feature)
184
- Lookbook::Features.enabled?(feature)
152
+ def render_in_layout(path, layout: nil, **locals)
153
+ render path, layout: layout.presence || (params[:lookbook_embed] ? "lookbook/basic" : "lookbook/application"), locals: locals
185
154
  end
186
155
  end
187
156
  end
@@ -1,73 +1,20 @@
1
- require "redcarpet"
2
- require "rouge"
3
- require "htmlbeautifier"
4
-
5
1
  module Lookbook
6
2
  module ApplicationHelper
7
3
  def config
8
4
  Lookbook::Engine.config.lookbook
9
5
  end
10
6
 
11
- def markdown(text)
12
- markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML, {
13
- tables: true,
14
- fenced_code_blocks: true,
15
- disable_indented_code_blocks: true
16
- })
17
- markdown.render(text).html_safe
18
- end
19
-
20
- def highlight(source, language, opts = {})
21
- formatter = Lookbook::CodeFormatter.new(opts)
22
- lexer = Rouge::Lexer.find(language)
23
- formatter.format(lexer.lex(source)).html_safe
24
- end
25
-
26
- def beautify(source, language = "html")
27
- source = source.strip
28
- result = language.downcase == "html" ? HtmlBeautifier.beautify(source) : source
29
- result.strip.html_safe
30
- end
31
-
32
- def icon(name = nil, size: 4, **attrs)
33
- render "lookbook/components/icon",
34
- name: name,
35
- size: size,
36
- classes: class_names(attrs[:class]),
37
- **attrs.except(:class)
7
+ def feature_enabled?(name)
8
+ Lookbook::Features.enabled?(name)
38
9
  end
39
10
 
40
- def component(name, **attrs, &block)
41
- render "lookbook/components/#{name}",
42
- classes: class_names(attrs[:class]),
43
- **attrs.except(:class),
44
- &block
45
- end
46
-
47
- if Rails.version.to_f < 6.1
48
- def class_names(*args)
49
- tokens = build_tag_values(*args).flat_map { |value| value.to_s.split(/\s+/) }.uniq
50
- safe_join(tokens, " ")
51
- end
52
- end
53
-
54
- private
55
-
56
- def build_tag_values(*args)
57
- tag_values = []
58
- args.each do |tag_value|
59
- case tag_value
60
- when Hash
61
- tag_value.each do |key, val|
62
- tag_values << key.to_s if val && key.present?
63
- end
64
- when Array
65
- tag_values.concat build_tag_values(*tag_value)
66
- else
67
- tag_values << tag_value.to_s if tag_value.present?
68
- end
11
+ def landing_path
12
+ landing = feature_enabled?(:pages) ? Lookbook.pages.find(&:landing) || Lookbook.pages.first : nil
13
+ if landing.present?
14
+ page_path(landing.lookup_path)
15
+ else
16
+ home_path
69
17
  end
70
- tag_values
71
18
  end
72
19
  end
73
20
  end
@@ -0,0 +1,40 @@
1
+ module Lookbook
2
+ module ComponentHelper
3
+ def render_component(name, **attrs, &block)
4
+ attrs[:classes] = class_names(attrs[:class])
5
+ render "lookbook/components/#{name.underscore}", **attrs.except(:class), &block
6
+ end
7
+
8
+ def icon(name, size: 4, **attrs)
9
+ render_component "icon", name: name, size: size, **attrs
10
+ end
11
+
12
+ if Rails.version.to_f < 6.1
13
+ def class_names(*args)
14
+ tokens = build_tag_values(*args).flat_map { |value| value.to_s.split(/\s+/) }.uniq
15
+ safe_join(tokens, " ")
16
+ end
17
+ end
18
+
19
+ alias_method :component, :render_component
20
+
21
+ private
22
+
23
+ def build_tag_values(*args)
24
+ tag_values = []
25
+ args.each do |tag_value|
26
+ case tag_value
27
+ when Hash
28
+ tag_value.each do |key, val|
29
+ tag_values << key.to_s if val && key.present?
30
+ end
31
+ when Array
32
+ tag_values.concat build_tag_values(*tag_value)
33
+ else
34
+ tag_values << tag_value.to_s if tag_value.present?
35
+ end
36
+ end
37
+ tag_values
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,15 @@
1
+ module Lookbook
2
+ module OutputHelper
3
+ def markdown(text = nil, &block)
4
+ Lookbook::Markdown.render(block ? capture(&block) : text)
5
+ end
6
+
7
+ def highlight(source, language, opts = {})
8
+ Lookbook::CodeFormatter.highlight(source, language, opts)
9
+ end
10
+
11
+ def beautify(source, language = "html")
12
+ Lookbook::CodeFormatter.beautify(source, language)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,46 @@
1
+ module Lookbook
2
+ module PageHelper
3
+ include Utils
4
+
5
+ def page_path(id)
6
+ page = id.is_a?(Page) ? id : Lookbook.pages.find(id)
7
+ lookbook.page_path page.lookup_path
8
+ end
9
+
10
+ def code(language = "ruby", line_numbers: false, &block)
11
+ render_component "code", language: language, line_numbers: line_numbers, &block
12
+ end
13
+
14
+ def embed(*args, params: {}, type: :preview, **opts)
15
+ return unless args.any?
16
+
17
+ @embed_counter ||= 0
18
+
19
+ preview_lookup = args.first.is_a?(Symbol) ? args.first : preview_class_name(args.first)
20
+ preview = Lookbook.previews.find(preview_lookup)
21
+
22
+ example = args[1] ? preview&.example(args[1]) : preview&.default_example
23
+
24
+ if example
25
+ @embed_counter += 1
26
+ render_component "embed", {
27
+ id: generate_id("embed", url_for, example.lookup_path, @embed_counter - 1),
28
+ example: example,
29
+ params: params,
30
+ opts: opts
31
+ }
32
+ else
33
+ embed_not_found
34
+ end
35
+ end
36
+
37
+ protected
38
+
39
+ def embed_not_found
40
+ render_component "not_found", {
41
+ title: "Preview not found",
42
+ text: "The preview may have been renamed or deleted."
43
+ }
44
+ end
45
+ end
46
+ end
@@ -1,7 +1,7 @@
1
1
  module Lookbook
2
2
  module PreviewHelper
3
3
  def lookbook_display(key, fallback = nil)
4
- params[:lookbook][:display][key.to_sym] || fallback
4
+ params.dig(:lookbook, :display, key.to_sym) || fallback
5
5
  end
6
6
  end
7
7
  end