lookbook 0.9.0 → 1.0.0.beta.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (211) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +90 -824
  3. data/app/assets/lookbook/css/lookbook.css +55 -0
  4. data/app/assets/lookbook/css/themes/blue.css +42 -0
  5. data/app/assets/lookbook/css/themes/indigo.css +42 -0
  6. data/app/assets/lookbook/css/themes/zinc.css +42 -0
  7. data/app/assets/lookbook/css/{tooltip_theme.css → tooltip.css} +14 -8
  8. data/app/assets/lookbook/js/app.js +64 -63
  9. data/app/assets/lookbook/js/components/clipboard.js +47 -0
  10. data/app/assets/lookbook/js/components/tooltip.js +30 -0
  11. data/app/assets/lookbook/js/config.js +7 -4
  12. data/app/assets/lookbook/js/helpers/build.js +22 -0
  13. data/app/assets/lookbook/js/helpers/dom.js +45 -0
  14. data/app/assets/lookbook/js/helpers/layout.js +21 -0
  15. data/app/assets/lookbook/js/helpers/request.js +16 -0
  16. data/app/assets/lookbook/js/helpers/string.js +11 -0
  17. data/app/assets/lookbook/js/lib/socket.js +4 -3
  18. data/app/assets/lookbook/js/lib/tippy.js +8 -0
  19. data/app/assets/lookbook/js/lookbook.js +61 -0
  20. data/app/assets/lookbook/js/plugins/logger.js +39 -0
  21. data/app/assets/lookbook/js/stores/filter.js +2 -2
  22. data/app/assets/lookbook/js/stores/inspector.js +23 -17
  23. data/app/assets/lookbook/js/stores/layout.js +101 -5
  24. data/app/assets/lookbook/js/stores/nav.js +17 -16
  25. data/app/assets/lookbook/js/stores/pages.js +4 -2
  26. data/app/assets/lookbook/js/stores/settings.js +7 -0
  27. data/app/assets/lookbook/js/stores/workbench.js +29 -0
  28. data/app/components/lookbook/button/component.html.erb +28 -0
  29. data/app/components/lookbook/button/component.js +55 -0
  30. data/app/components/lookbook/button/component.rb +39 -0
  31. data/app/components/lookbook/button_group/component.html.erb +3 -0
  32. data/app/components/lookbook/button_group/component.rb +18 -0
  33. data/app/components/lookbook/code/component.css +57 -0
  34. data/app/components/lookbook/code/component.html.erb +10 -0
  35. data/app/components/lookbook/code/component.js +3 -0
  36. data/app/components/lookbook/code/component.rb +56 -0
  37. data/app/components/lookbook/code/highlight_github_light.css +217 -0
  38. data/app/components/lookbook/component.rb +41 -0
  39. data/app/components/lookbook/copy_button/component.html.erb +11 -0
  40. data/app/components/lookbook/copy_button/component.js +16 -0
  41. data/app/components/lookbook/copy_button/component.rb +23 -0
  42. data/app/components/lookbook/dimensions_display/component.html.erb +11 -0
  43. data/app/components/lookbook/dimensions_display/component.js +37 -0
  44. data/app/components/lookbook/dimensions_display/component.rb +18 -0
  45. data/app/components/lookbook/embed/component.html.erb +50 -0
  46. data/app/components/lookbook/embed/component.js +39 -0
  47. data/app/components/lookbook/embed/component.rb +22 -0
  48. data/app/components/lookbook/filter/component.html.erb +17 -0
  49. data/app/components/lookbook/filter/component.js +21 -0
  50. data/app/components/lookbook/filter/component.rb +15 -0
  51. data/app/components/lookbook/header/component.html.erb +79 -0
  52. data/app/components/lookbook/header/component.rb +9 -0
  53. data/app/components/lookbook/icon/component.css +11 -0
  54. data/app/components/lookbook/icon/component.html.erb +5 -0
  55. data/app/components/lookbook/icon/component.js +5 -0
  56. data/app/components/lookbook/icon/component.rb +23 -0
  57. data/app/components/lookbook/nav/component.html.erb +33 -0
  58. data/app/components/lookbook/nav/component.js +52 -0
  59. data/app/components/lookbook/nav/component.rb +37 -0
  60. data/app/components/lookbook/nav/item/component.html.erb +23 -0
  61. data/app/components/lookbook/nav/item/component.js +66 -0
  62. data/app/components/lookbook/nav/item/component.rb +84 -0
  63. data/app/components/lookbook/page_tabs/component.html.erb +18 -0
  64. data/app/components/lookbook/page_tabs/component.rb +19 -0
  65. data/app/components/lookbook/params_editor/component.html.erb +3 -0
  66. data/app/components/lookbook/params_editor/component.js +12 -0
  67. data/app/components/lookbook/params_editor/component.rb +11 -0
  68. data/app/components/lookbook/params_editor/field/component.html.erb +50 -0
  69. data/app/components/lookbook/params_editor/field/component.js +36 -0
  70. data/app/components/lookbook/params_editor/field/component.rb +41 -0
  71. data/app/components/lookbook/prose/component.css +12 -0
  72. data/app/components/lookbook/prose/component.html.erb +3 -0
  73. data/app/components/lookbook/prose/component.rb +26 -0
  74. data/app/components/lookbook/split_layout/component.html.erb +13 -0
  75. data/app/components/lookbook/split_layout/component.js +151 -0
  76. data/app/components/lookbook/split_layout/component.rb +11 -0
  77. data/app/components/lookbook/tab_panels/component.html.erb +5 -0
  78. data/app/components/lookbook/tab_panels/component.js +25 -0
  79. data/app/components/lookbook/tab_panels/component.rb +20 -0
  80. data/app/components/lookbook/tab_panels/panel/component.html.erb +8 -0
  81. data/app/components/lookbook/tab_panels/panel/component.rb +9 -0
  82. data/app/components/lookbook/tabs/component.css +8 -0
  83. data/app/components/lookbook/tabs/component.html.erb +20 -0
  84. data/app/components/lookbook/tabs/component.js +115 -0
  85. data/app/components/lookbook/tabs/component.rb +28 -0
  86. data/app/components/lookbook/tabs/dropdown_tab/component.html.erb +20 -0
  87. data/app/components/lookbook/tabs/dropdown_tab/component.rb +17 -0
  88. data/app/components/lookbook/tabs/tab/component.html.erb +24 -0
  89. data/app/components/lookbook/tabs/tab/component.rb +17 -0
  90. data/app/components/lookbook/tag_component.rb +29 -0
  91. data/app/components/lookbook/toolbar/component.css +16 -0
  92. data/app/components/lookbook/toolbar/component.html.erb +5 -0
  93. data/app/components/lookbook/toolbar/component.rb +26 -0
  94. data/app/components/lookbook/viewport/component.css +11 -0
  95. data/app/components/lookbook/viewport/component.html.erb +57 -0
  96. data/app/{assets/lookbook/js/components/preview-window.js → components/lookbook/viewport/component.js} +57 -14
  97. data/app/components/lookbook/viewport/component.rb +21 -0
  98. data/app/controllers/lookbook/application_controller.rb +16 -5
  99. data/app/controllers/lookbook/pages_controller.rb +17 -8
  100. data/app/controllers/lookbook/previews_controller.rb +130 -89
  101. data/app/helpers/lookbook/application_helper.rb +6 -2
  102. data/app/helpers/lookbook/component_helper.rb +22 -10
  103. data/app/helpers/lookbook/output_helper.rb +8 -4
  104. data/app/helpers/lookbook/page_helper.rb +13 -21
  105. data/app/views/layouts/lookbook/application.html.erb +76 -28
  106. data/app/views/layouts/lookbook/inspector.html.erb +7 -0
  107. data/app/views/layouts/lookbook/page.html.erb +53 -0
  108. data/app/views/layouts/lookbook/shell.html.erb +64 -0
  109. data/app/views/layouts/lookbook/skeleton.html.erb +27 -10
  110. data/app/views/layouts/lookbook/standalone.html.erb +5 -0
  111. data/app/views/lookbook/404.html.erb +15 -0
  112. data/app/views/lookbook/error.html.erb +34 -34
  113. data/app/views/lookbook/index.html.erb +11 -6
  114. data/app/views/lookbook/pages/show.html.erb +37 -91
  115. data/app/views/{layouts/lookbook → lookbook}/preview.html.erb +6 -8
  116. data/app/views/lookbook/previews/panels/_content.html.erb +13 -0
  117. data/app/views/lookbook/previews/panels/_notes.html.erb +19 -25
  118. data/app/views/lookbook/previews/panels/_output.html.erb +7 -18
  119. data/app/views/lookbook/previews/panels/_params.html.erb +13 -15
  120. data/app/views/lookbook/previews/panels/_preview.html.erb +6 -52
  121. data/app/views/lookbook/previews/panels/_source.html.erb +6 -15
  122. data/app/views/lookbook/previews/show.html.erb +133 -24
  123. data/config/routes.rb +5 -5
  124. data/lib/lookbook/code_formatter.rb +37 -13
  125. data/lib/lookbook/collection.rb +19 -16
  126. data/lib/lookbook/config.rb +180 -0
  127. data/lib/lookbook/engine.rb +66 -59
  128. data/lib/lookbook/entity.rb +47 -0
  129. data/lib/lookbook/error.rb +1 -2
  130. data/lib/lookbook/features.rb +1 -1
  131. data/lib/lookbook/markdown.rb +3 -4
  132. data/lib/lookbook/page.rb +40 -34
  133. data/lib/lookbook/page_collection.rb +8 -0
  134. data/lib/lookbook/page_section.rb +31 -0
  135. data/lib/lookbook/params.rb +14 -3
  136. data/lib/lookbook/preview.rb +16 -7
  137. data/lib/lookbook/preview_collection.rb +8 -0
  138. data/lib/lookbook/preview_controller.rb +6 -2
  139. data/lib/lookbook/preview_example.rb +5 -6
  140. data/lib/lookbook/preview_group.rb +4 -9
  141. data/lib/lookbook/{code_inspector.rb → source_inspector.rb} +2 -2
  142. data/lib/lookbook/store.rb +36 -0
  143. data/lib/lookbook/theme.rb +29 -0
  144. data/lib/lookbook/utils.rb +10 -2
  145. data/lib/lookbook/version.rb +1 -1
  146. data/lib/lookbook.rb +6 -1
  147. data/lib/tasks/lookbook_tasks.rake +12 -0
  148. data/public/lookbook-assets/css/app.css +2340 -1
  149. data/public/lookbook-assets/css/app.css.map +11 -1
  150. data/public/lookbook-assets/css/lookbook.css +3069 -0
  151. data/public/lookbook-assets/css/lookbook.css.map +1 -0
  152. data/public/lookbook-assets/css/themes/blue.css +44 -0
  153. data/public/lookbook-assets/css/themes/blue.css.map +1 -0
  154. data/public/lookbook-assets/css/themes/indigo.css +44 -0
  155. data/public/lookbook-assets/css/themes/indigo.css.map +1 -0
  156. data/public/lookbook-assets/css/themes/zinc.css +44 -0
  157. data/public/lookbook-assets/css/themes/zinc.css.map +1 -0
  158. data/public/lookbook-assets/js/app.js +10861 -1
  159. data/public/lookbook-assets/js/app.js.map +2571 -1
  160. data/public/lookbook-assets/js/embed.js +895 -1
  161. data/public/lookbook-assets/js/embed.js.map +1 -1
  162. data/public/lookbook-assets/js/lookbook.js +13541 -0
  163. data/public/lookbook-assets/js/lookbook.js.map +1 -0
  164. metadata +133 -116
  165. data/app/assets/lookbook/css/app.css +0 -161
  166. data/app/assets/lookbook/css/code_theme.css +0 -214
  167. data/app/assets/lookbook/js/components/app.js +0 -55
  168. data/app/assets/lookbook/js/components/code.js +0 -5
  169. data/app/assets/lookbook/js/components/copy.js +0 -20
  170. data/app/assets/lookbook/js/components/embed.js +0 -89
  171. data/app/assets/lookbook/js/components/filter.js +0 -35
  172. data/app/assets/lookbook/js/components/inspector.js +0 -66
  173. data/app/assets/lookbook/js/components/nav-group.js +0 -47
  174. data/app/assets/lookbook/js/components/nav-item.js +0 -29
  175. data/app/assets/lookbook/js/components/nav.js +0 -28
  176. data/app/assets/lookbook/js/components/page-tabs.js +0 -9
  177. data/app/assets/lookbook/js/components/page.js +0 -25
  178. data/app/assets/lookbook/js/components/param.js +0 -34
  179. data/app/assets/lookbook/js/components/sidebar.js +0 -18
  180. data/app/assets/lookbook/js/components/sizes.js +0 -16
  181. data/app/assets/lookbook/js/components/splitter.js +0 -25
  182. data/app/assets/lookbook/js/components/tabs.js +0 -52
  183. data/app/assets/lookbook/js/lib/split.js +0 -15
  184. data/app/assets/lookbook/js/stores/sidebar.js +0 -26
  185. data/app/views/layouts/lookbook/basic.html.erb +0 -7
  186. data/app/views/lookbook/components/_branding.html.erb +0 -8
  187. data/app/views/lookbook/components/_code.html.erb +0 -17
  188. data/app/views/lookbook/components/_copy_button.html.erb +0 -11
  189. data/app/views/lookbook/components/_drawer.html.erb +0 -112
  190. data/app/views/lookbook/components/_embed.html.erb +0 -39
  191. data/app/views/lookbook/components/_errors.html.erb +0 -13
  192. data/app/views/lookbook/components/_filter.html.erb +0 -18
  193. data/app/views/lookbook/components/_header.html.erb +0 -6
  194. data/app/views/lookbook/components/_icon.html.erb +0 -5
  195. data/app/views/lookbook/components/_nav.html.erb +0 -16
  196. data/app/views/lookbook/components/_nav_collection.html.erb +0 -5
  197. data/app/views/lookbook/components/_nav_group.html.erb +0 -14
  198. data/app/views/lookbook/components/_nav_item.html.erb +0 -24
  199. data/app/views/lookbook/components/_nav_page.html.erb +0 -22
  200. data/app/views/lookbook/components/_nav_preview.html.erb +0 -13
  201. data/app/views/lookbook/components/_not_found.html.erb +0 -11
  202. data/app/views/lookbook/components/_param.html.erb +0 -21
  203. data/app/views/lookbook/components/_preview.html.erb +0 -77
  204. data/app/views/lookbook/components/_sidebar.html.erb +0 -69
  205. data/app/views/lookbook/pages/not_found.html.erb +0 -15
  206. data/app/views/lookbook/previews/error.html.erb +0 -1
  207. data/app/views/lookbook/previews/inputs/_select.html.erb +0 -7
  208. data/app/views/lookbook/previews/inputs/_text.html.erb +0 -8
  209. data/app/views/lookbook/previews/inputs/_textarea.html.erb +0 -8
  210. data/app/views/lookbook/previews/inputs/_toggle.html.erb +0 -13
  211. data/app/views/lookbook/previews/not_found.html.erb +0 -23
@@ -1,34 +1,53 @@
1
- export default function preview() {
1
+ export default function viewportComponent(store) {
2
+ store = store || { width: "100%", height: "100%" };
3
+ store.resizing = false;
4
+
2
5
  return {
3
- get store() {
4
- return this.$store.inspector.preview;
5
- },
6
+ store,
7
+
6
8
  get maxWidth() {
7
- return this.store.width === "100%" ? "100%" : `${this.store.width}px`;
9
+ return this.store.width === "100%" ? "100%" : `${store.width}px`;
8
10
  },
11
+
9
12
  get maxHeight() {
10
- return this.store.height === "100%" ? "100%" : `${this.store.height}px`;
13
+ return this.store.height === "100%" ? "100%" : `${store.height}px`;
11
14
  },
15
+
12
16
  get parentWidth() {
13
- return Math.round(this.$root.parentElement.clientWidth);
17
+ return Math.round(this.$root.clientWidth);
14
18
  },
19
+
15
20
  get parentHeight() {
16
- return Math.round(this.$root.parentElement.clientHeight);
21
+ return Math.round(this.$root.clientHeight);
22
+ },
23
+
24
+ get reflowing() {
25
+ return this.$store.layout.reflowing;
26
+ },
27
+
28
+ reloadIframe() {
29
+ this.$refs.iframe.contentWindow.location.reload();
17
30
  },
31
+
18
32
  start() {
33
+ this.$dispatch("viewport:resize-start", this._resizeData);
19
34
  this.$store.layout.reflowing = true;
20
35
  this.store.resizing = true;
21
36
  },
37
+
22
38
  end() {
23
39
  this.$store.layout.reflowing = false;
24
40
  this.store.resizing = false;
41
+ this.$dispatch("viewport:resize-complete", this._resizeData);
25
42
  },
43
+
26
44
  onResizeStart(e) {
27
45
  this.onResizeWidthStart(e);
28
46
  this.onResizeHeightStart(e);
29
47
  },
48
+
30
49
  toggleFullSize() {
31
- const { height, width } = this.store;
50
+ const { height, width } = store;
32
51
  if (height === "100%" && width === "100%") {
33
52
  this.toggleFullHeight();
34
53
  this.toggleFullWidth();
@@ -37,6 +56,7 @@ export default function preview() {
37
56
  if (width !== "100%") this.toggleFullWidth();
38
57
  }
39
58
  },
59
+
40
60
  onResizeWidth(e) {
41
61
  const width =
42
62
  this.resizeStartWidth - (this.resizeStartPositionX - e.pageX) * 2;
@@ -46,30 +66,37 @@ export default function preview() {
46
66
  );
47
67
  this.store.width =
48
68
  boundedWidth === this.parentWidth ? "100%" : boundedWidth;
69
+ this.$dispatch("viewport:resize-progress", this._resizeData);
49
70
  },
71
+
50
72
  onResizeWidthStart(e) {
51
73
  this.start();
52
74
  this.onResizeWidth = this.onResizeWidth.bind(this);
53
75
  this.onResizeWidthEnd = this.onResizeWidthEnd.bind(this);
54
76
  this.resizeStartPositionX = e.pageX;
55
- this.resizeStartWidth = this.$root.clientWidth;
77
+ this.resizeStartWidth = this.$refs.wrapper.clientWidth;
56
78
  window.addEventListener("pointermove", this.onResizeWidth);
57
79
  window.addEventListener("pointerup", this.onResizeWidthEnd);
58
80
  },
81
+
59
82
  onResizeWidthEnd() {
60
83
  window.removeEventListener("pointermove", this.onResizeWidth);
61
84
  window.removeEventListener("pointerup", this.onResizeWidthEnd);
62
85
  this.end();
63
86
  },
87
+
64
88
  toggleFullWidth() {
65
- const { width, lastWidth } = this.store;
89
+ this.$dispatch("viewport:resize-start", this._resizeData);
90
+ const { width, lastWidth } = store;
66
91
  if (width === "100%" && lastWidth) {
67
92
  this.store.width = lastWidth;
68
93
  } else {
69
94
  this.store.lastWidth = width;
70
95
  this.store.width = "100%";
71
96
  }
97
+ this.$dispatch("viewport:resize-complete", this._resizeData);
72
98
  },
99
+
73
100
  onResizeHeight(e) {
74
101
  const height =
75
102
  this.resizeStartHeight - (this.resizeStartPositionY - e.pageY);
@@ -77,31 +104,47 @@ export default function preview() {
77
104
  Math.max(Math.round(height), 200),
78
105
  this.parentHeight
79
106
  );
80
- this.$store.inspector.preview.height =
107
+ this.store.height =
81
108
  boundedHeight === this.parentHeight ? "100%" : boundedHeight;
109
+ this.$dispatch("viewport:resize-progress", this._resizeData);
82
110
  },
111
+
83
112
  onResizeHeightStart(e) {
84
113
  this.start();
85
114
  this.onResizeHeight = this.onResizeHeight.bind(this);
86
115
  this.onResizeHeightEnd = this.onResizeHeightEnd.bind(this);
87
116
  this.resizeStartPositionY = e.pageY;
88
- this.resizeStartHeight = this.$root.clientHeight;
117
+ this.resizeStartHeight = this.$refs.wrapper.clientHeight;
89
118
  window.addEventListener("pointermove", this.onResizeHeight);
90
119
  window.addEventListener("pointerup", this.onResizeHeightEnd);
91
120
  },
121
+
92
122
  onResizeHeightEnd() {
93
123
  window.removeEventListener("pointermove", this.onResizeHeight);
94
124
  window.removeEventListener("pointerup", this.onResizeHeightEnd);
95
125
  this.end();
96
126
  },
127
+
97
128
  toggleFullHeight() {
98
- const { height, lastHeight } = this.store;
129
+ this.$dispatch("viewport:resize-start", this._resizeData);
130
+ const { height, lastHeight } = store;
99
131
  if (height === "100%" && lastHeight) {
100
132
  this.store.height = lastHeight;
101
133
  } else {
102
134
  this.store.lastHeight = height;
103
135
  this.store.height = "100%";
104
136
  }
137
+ this.$dispatch("viewport:resize-complete", this._resizeData);
138
+ },
139
+
140
+ // protected
141
+
142
+ get _resizeData() {
143
+ return {
144
+ width: this.store.width,
145
+ height: this.store.height,
146
+ viewport: this,
147
+ };
105
148
  },
106
149
  };
107
150
  }
@@ -0,0 +1,21 @@
1
+ module Lookbook
2
+ class Viewport::Component < Lookbook::Component
3
+ def initialize(src:, resize_height: true, resize_width: true, max_height: nil, **html_attrs)
4
+ @src = src
5
+ @resize_height = resize_height
6
+ @resize_width = resize_width
7
+ @max_height = max_height
8
+ super(**html_attrs)
9
+ end
10
+
11
+ def generate_id(*args)
12
+ args.map { |args| args.delete_prefix("/").tr("&?=/_\-", "-") }.join("-")
13
+ end
14
+
15
+ protected
16
+
17
+ def alpine_component
18
+ "viewportComponent"
19
+ end
20
+ end
21
+ end
@@ -6,23 +6,34 @@ module Lookbook
6
6
  helper Lookbook::OutputHelper
7
7
  helper Lookbook::ComponentHelper
8
8
 
9
+ before_action :generate_theme_overrides
10
+
9
11
  def self.controller_path
10
12
  "lookbook"
11
13
  end
12
14
 
13
15
  def index
14
- if feature_enabled? :pages
15
- landing = Lookbook.pages.find(&:landing) || Lookbook.pages.first
16
- if landing.present?
17
- redirect_to lookbook_page_path(landing.lookup_path)
18
- end
16
+ landing = Lookbook.pages.find(&:landing) || Lookbook.pages.first
17
+ if landing.present?
18
+ redirect_to lookbook_page_path(landing.lookup_path)
19
+ else
20
+ render "lookbook/index", layout: Lookbook.previews.any? ? "lookbook/application" : "lookbook/shell"
19
21
  end
20
22
  end
21
23
 
22
24
  protected
23
25
 
26
+ def generate_theme_overrides
27
+ @theme_overrides ||= Lookbook::Theme.new(Lookbook.config.ui_theme_overrides).to_css
28
+ end
29
+
24
30
  def feature_enabled?(feature)
25
31
  Lookbook::Features.enabled?(feature)
26
32
  end
33
+
34
+ def render_in_layout(path, layout: nil, **locals)
35
+ @error = locals[:error]
36
+ render path, layout: layout.presence || (params[:lookbook_embed] ? "lookbook/basic" : "lookbook/application"), locals: locals
37
+ end
27
38
  end
28
39
  end
@@ -1,5 +1,6 @@
1
1
  module Lookbook
2
2
  class PagesController < ApplicationController
3
+ layout "lookbook/page"
3
4
  helper_method :page_controller
4
5
 
5
6
  def self.controller_path
@@ -11,8 +12,7 @@ module Lookbook
11
12
  if landing.present?
12
13
  redirect_to lookbook_page_path landing.lookup_path
13
14
  else
14
- @title = "Not found"
15
- render "not_found"
15
+ show_404
16
16
  end
17
17
  end
18
18
 
@@ -20,27 +20,36 @@ module Lookbook
20
20
  @pages = Lookbook.pages
21
21
  @page = @pages.find_by_path(params[:path])
22
22
  if @page
23
+ @next_page = @pages.find_next(@page)
24
+ @previous_page = @pages.find_previous(@page)
23
25
  if @page.errors.any?
24
- render "lookbook/error", locals: {error: @page.errors.first}
26
+ render_in_layout "lookbook/error",
27
+ layout: "lookbook/page",
28
+ error: @page.errors.first
25
29
  else
26
30
  begin
27
31
  @page_content = page_controller.render_page(@page)
28
- @next_page = @pages.find_next(@page)
29
- @previous_page = @pages.find_previous(@page)
30
32
  @title = @page.title
31
33
  rescue => exception
32
- render "lookbook/error", locals: {
34
+ render_in_layout "lookbook/error",
35
+ layout: "lookbook/page",
33
36
  error: Lookbook::Error.new(exception, file_path: @page.full_path, source_code: @page.content)
34
- }
35
37
  end
36
38
  end
37
39
  else
38
- render "not_found"
40
+ show_404
39
41
  end
40
42
  end
41
43
 
42
44
  protected
43
45
 
46
+ def show_404
47
+ render "lookbook/404", locals: {
48
+ message: "Page not found",
49
+ description: "The page may have been removed or renamed."
50
+ }
51
+ end
52
+
44
53
  def page_controller
45
54
  controller_class = Lookbook.config.page_controller.constantize
46
55
  controller = controller_class.new
@@ -1,148 +1,189 @@
1
1
  module Lookbook
2
2
  class PreviewsController < ApplicationController
3
+ layout "lookbook/inspector"
4
+
3
5
  def self.controller_path
4
6
  "lookbook/previews"
5
7
  end
6
8
 
7
9
  before_action :lookup_entities, only: [:preview, :show]
8
10
  before_action :set_title
11
+ before_action :set_params
9
12
 
10
13
  def preview
11
14
  if @example
12
- set_params
13
15
  begin
14
- render html: render_examples(examples_data)
16
+ opts = {layout: @preview.layout}
17
+ if params[:lookbook_embed] == "true"
18
+ opts[:append_html] = "<script src=\"/lookbook-assets/js/embed.js?v=#{Lookbook.version}\"></script>".html_safe
19
+ end
20
+ preview_html = preview_controller.process(:render_in_layout_to_string, "lookbook/preview", inspector_data, **opts)
21
+ render html: preview_html
15
22
  rescue => exception
16
23
  render_in_layout "lookbook/error",
17
- layout: "lookbook/basic",
18
- error: prettify_error(exception),
19
- disable_header: true
24
+ layout: "lookbook/standalone",
25
+ error: prettify_error(exception)
20
26
  end
21
27
  else
22
- render_in_layout "not_found"
28
+ show_404 layout: "lookbook/standalone"
23
29
  end
24
30
  end
25
31
 
26
32
  def show
27
33
  if @example
28
34
  begin
29
- set_params
30
- @examples = examples_data
31
- @drawer_panels = drawer_panels.select { |name, panel| panel[:show] }
32
- @preview_panels = preview_panels.select { |name, panel| panel[:show] }
35
+ @main_panels = main_panels
36
+ @drawer_panels = drawer_panels
33
37
  rescue => exception
34
- render_in_layout "lookbook/error", error: prettify_error(exception)
38
+ render_in_layout "lookbook/error", layout: "lookbook/inspector", error: prettify_error(exception)
35
39
  end
36
40
  else
37
- render_in_layout "not_found"
41
+ show_404
38
42
  end
39
43
  end
40
44
 
45
+ def show_legacy
46
+ Lookbook.logger.warn("Legacy URL path detected. These paths are deprecated and will be removed in a future version")
47
+ redirect_to lookbook_inspect_path params[:path]
48
+ end
49
+
41
50
  private
42
51
 
43
52
  def lookup_entities
44
53
  @example = Lookbook.previews.find_example(params[:path])
45
- if @example
54
+ if @example.present?
46
55
  @preview = @example.preview
47
56
  if params[:path] == @preview&.lookup_path
48
57
  redirect_to lookbook_inspect_path "#{params[:path]}/#{@preview.default_example.name}"
49
58
  end
50
59
  else
51
- first_example = Lookbook.previews.find(params[:path])&.examples&.first
52
- redirect_to lookbook_inspect_path(first_example.lookup_path) if first_example
60
+ @preview = Lookbook.previews.find(params[:path])
61
+ if @preview.present?
62
+ first_example = @preview.examples.first
63
+ redirect_to lookbook_inspect_path(first_example.lookup_path) if first_example
64
+ else
65
+ @preview = Lookbook.previews.find(path_segments.slice(0, path_segments.size - 1).join("/"))
66
+ end
53
67
  end
54
68
  end
55
69
 
70
+ def show_404(layout: nil)
71
+ locals = if @preview
72
+ {
73
+ message: "Example not found",
74
+ description: "The '#{@preview.label}' preview does not have an example named '#{path_segments.last}'."
75
+ }
76
+ else
77
+ {
78
+ message: "Not found",
79
+ description: "Looked for '#{params[:path]}'.<br>The preview may have been renamed or deleted."
80
+ }
81
+ end
82
+ render_in_layout "lookbook/404", layout: layout, **locals
83
+ end
84
+
85
+ def target_examples
86
+ @example.type == :group ? @example.examples : [@example]
87
+ end
88
+
56
89
  def set_title
57
90
  @title = @example.present? ? [@example&.label, @preview&.label].compact.join(" :: ") : "Not found"
58
91
  end
59
92
 
60
- def examples_data
61
- @examples_data ||= (@example.type == :group ? @example.examples : [@example]).map do |example|
62
- example_data(example)
93
+ def set_params
94
+ if @example
95
+ # cast known params to type
96
+ @example.params.each do |param|
97
+ if preview_controller.params.key?(param[:name])
98
+ preview_controller.params[param[:name]] = Lookbook::Params.cast(preview_controller.params[param[:name]], param[:type])
99
+ end
100
+ end
101
+ # set display params
102
+ preview_controller.params.merge!({
103
+ lookbook: {
104
+ display: @example.display_params
105
+ }
106
+ })
63
107
  end
64
108
  end
65
109
 
66
- def example_data(example)
67
- render_args = @preview.render_args(example.name, params: preview_controller.params.permit!)
68
- has_template = render_args[:template] != "view_components/preview"
69
- {
70
- label: example.label,
71
- notes: example.notes,
72
- html: preview_controller.process(:render_example_to_string, @preview, example.name),
73
- source: has_template ? example.template_source(render_args[:template]) : example.method_source,
74
- source_lang: has_template ? example.template_lang(render_args[:template]) : example.source_lang,
75
- params: example.params
76
- }
110
+ def preview_params
111
+ preview_controller.params.permit!
112
+ preview_controller.params.to_h.select do |key, value|
113
+ !!@example.params.find { |param| param[:name] == key }
114
+ end
77
115
  end
78
116
 
79
- def render_examples(examples)
80
- preview_controller.process(:render_in_layout_to_string, "layouts/lookbook/preview", {examples: examples}, @preview.layout)
81
- end
117
+ def inspector_data
118
+ return @inspector_data if @inspector_data.present?
82
119
 
83
- def set_params
84
- # cast known params to type
85
- @example.params.each do |param|
86
- if preview_controller.params.key?(param[:name])
87
- preview_controller.params[param[:name]] = Lookbook::Params.cast(preview_controller.params[param[:name]], param[:type])
120
+ request_data = {
121
+ preview_params: preview_params,
122
+ path: params[:path],
123
+ query_parameters: request.query_parameters,
124
+ original: request
125
+ }
126
+
127
+ preview_data = {
128
+ relative_path: @preview.full_path.relative_path_from(Rails.root.to_s),
129
+ full_path: @preview.full_path,
130
+ example_label: @example.label,
131
+ params: @example.params,
132
+ }
133
+ [:id, :label, :notes, :lookup_path, :full_path].each do |prop|
134
+ preview_data[prop] = @preview.public_send(prop)
135
+ end
136
+
137
+ examples_data = target_examples.map do |example|
138
+ render_args = @preview.render_args(example.name, params: preview_controller.params)
139
+ has_template = render_args[:template] != "view_components/preview"
140
+ example_data = Lookbook::Store.new({
141
+ output: preview_controller.process(:render_example_to_string, @preview, example.name),
142
+ source: has_template ? example.template_source(render_args[:template]) : example.method_source,
143
+ source_lang: has_template ? example.template_lang(render_args[:template]) : example.source_lang,
144
+ })
145
+ [:id, :label, :notes, :lookup_path, :params, :display_params].each do |prop|
146
+ example_data[prop] = example.public_send(prop)
88
147
  end
148
+ example_data
89
149
  end
90
150
 
91
- # set display params
92
- preview_controller.params.merge!({
93
- lookbook: {
94
- display: @example.display_params
95
- }
151
+ @inspector_data ||= Lookbook::Store.new({
152
+ request: request_data,
153
+ preview: preview_data,
154
+ examples: examples_data
96
155
  })
97
156
  end
98
157
 
99
- def preview_panels
100
- {
101
- preview: {
102
- label: "Preview",
103
- template: "lookbook/previews/panels/preview",
104
- srcdoc: Lookbook.config.preview_srcdoc ? render_examples(examples_data).gsub("\"", "&quot;") : nil,
105
- hotkey: "v",
106
- show: true,
107
- disabled: false,
108
- copy: false
109
- },
110
- output: {
111
- label: "HTML",
112
- template: "lookbook/previews/panels/output",
113
- hotkey: "o",
114
- show: true,
115
- disabled: false,
116
- copy: true
158
+ def panels
159
+ return @panels if @panels.present?
160
+ @panels = []
161
+ Lookbook.config.inspector_panels.each do |name, config|
162
+ config_with_defaults = Lookbook.config.inspector_panel_defaults.merge(config)
163
+
164
+ callable_data = {
165
+ name: name.to_s,
166
+ index_position: (@panels.filter { |p| p.pane == config.pane }.size + 1),
167
+ **inspector_data
117
168
  }
118
- }
169
+
170
+ resolved_config = config_with_defaults.transform_values do |value|
171
+ value.class == Proc ? value.call(Lookbook::Store.new(callable_data)) : value
172
+ end
173
+ resolved_config[:name] = name.to_s
174
+
175
+ @panels << Lookbook::Store.new(resolved_config, deep: false)
176
+ end
177
+
178
+ @panels.filter(&:show).sort_by { |p| [p.position, p.label] }
179
+ end
180
+
181
+ def main_panels
182
+ panels.filter { |panel| panel.pane == :main }
119
183
  end
120
184
 
121
185
  def drawer_panels
122
- {
123
- source: {
124
- label: "Source",
125
- template: "lookbook/previews/panels/source",
126
- hotkey: "s",
127
- show: true,
128
- disabled: false,
129
- copy: true
130
- },
131
- notes: {
132
- label: "Notes",
133
- template: "lookbook/previews/panels/notes",
134
- hotkey: "n",
135
- show: true,
136
- disabled: @examples.select { |e| e[:notes].present? }.none?
137
- },
138
- params: {
139
- label: "Params",
140
- template: "lookbook/previews/panels/params",
141
- hotkey: "p",
142
- show: true,
143
- disabled: @example.type == :group || @example.params.none?
144
- }
145
- }
186
+ panels.filter { |panel| panel.pane == :drawer }
146
187
  end
147
188
 
148
189
  def preview_controller
@@ -153,10 +194,6 @@ module Lookbook
153
194
  @preview_controller ||= controller
154
195
  end
155
196
 
156
- def render_in_layout(path, layout: nil, **locals)
157
- render path, layout: layout.presence || (params[:lookbook_embed] ? "lookbook/basic" : "lookbook/application"), locals: locals
158
- end
159
-
160
197
  def prettify_error(exception)
161
198
  error_params = if exception.is_a?(ViewComponent::PreviewTemplateError)
162
199
  {
@@ -178,5 +215,9 @@ module Lookbook
178
215
  end
179
216
  Lookbook::Error.new(exception, **(error_params || {}))
180
217
  end
218
+
219
+ def path_segments
220
+ params[:path].split("/")
221
+ end
181
222
  end
182
223
  end
@@ -9,12 +9,16 @@ module Lookbook
9
9
  end
10
10
 
11
11
  def landing_path
12
- landing = feature_enabled?(:pages) ? Lookbook.pages.find(&:landing) || Lookbook.pages.first : nil
12
+ landing = Lookbook.pages.find(&:landing) || Lookbook.pages.first
13
13
  if landing.present?
14
- page_path(landing.lookup_path)
14
+ lookbook_page_path landing.lookup_path
15
15
  else
16
16
  lookbook_home_path
17
17
  end
18
18
  end
19
+
20
+ def generate_id(*args)
21
+ args.map { |args| args.delete_prefix("/").tr("&?=/_\-", "-") }.join("-")
22
+ end
19
23
  end
20
24
  end
@@ -1,16 +1,15 @@
1
1
  module Lookbook
2
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
3
+ COMPONENT_CLASSES = {} # cache for constantized references
7
4
 
8
- def icon(name, size: 4, **attrs)
9
- render_component "icon", name: name, size: size, **attrs
5
+ def render_component(ref, **attrs, &block)
6
+ klass = component_class(ref)
7
+ comp = attrs.key?(:content) ? klass.new(**attrs.except(:content)).with_content(attrs[:content]) : klass.new(**attrs)
8
+ render comp, &block
10
9
  end
11
10
 
12
- def code(language = "ruby", **opts, &block)
13
- render_component "code", language: language, **opts, &block
11
+ def render_tag(tag = :div, **attrs, &block)
12
+ render Lookbook::TagComponent.new(tag: tag, **attrs), &block
14
13
  end
15
14
 
16
15
  if Rails.version.to_f < 6.1
@@ -20,10 +19,23 @@ module Lookbook
20
19
  end
21
20
  end
22
21
 
23
- alias_method :component, :render_component
24
-
25
22
  private
26
23
 
24
+ def component_class(ref)
25
+ klass = COMPONENT_CLASSES[ref]
26
+ if klass.nil?
27
+ ref = ref.to_s.tr("-", "_")
28
+ class_namespace = ref.camelize
29
+ begin
30
+ klass = "Lookbook::#{class_namespace}::Component".constantize
31
+ rescue
32
+ klass = "Lookbook::#{class_namespace}Component".constantize
33
+ end
34
+ COMPONENT_CLASSES[ref] = klass
35
+ end
36
+ klass
37
+ end
38
+
27
39
  def build_tag_values(*args)
28
40
  tag_values = []
29
41
  args.each do |tag_value|
@@ -4,12 +4,16 @@ module Lookbook
4
4
  Lookbook::Markdown.render(block ? capture(&block) : text)
5
5
  end
6
6
 
7
- def highlight(source, language, opts = {})
8
- Lookbook::CodeFormatter.highlight(source, language, opts)
7
+ def highlight(source, **opts)
8
+ Lookbook::CodeFormatter.highlight(source, **opts)
9
9
  end
10
10
 
11
- def beautify(source, language = "html")
12
- Lookbook::CodeFormatter.beautify(source, language)
11
+ def beautify(source, **opts)
12
+ Lookbook::CodeFormatter.beautify(source, **opts)
13
+ end
14
+
15
+ def pretty_json(obj)
16
+ JSON.pretty_generate(obj)
13
17
  end
14
18
  end
15
19
  end