lookbook 0.4.4 → 0.4.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +223 -54
  3. data/app/assets/lookbook/css/app.css +64 -8
  4. data/app/assets/lookbook/js/app.js +39 -53
  5. data/app/assets/lookbook/js/components/copy.js +16 -0
  6. data/app/assets/lookbook/js/components/filter.js +24 -0
  7. data/app/assets/lookbook/js/components/inspector.js +21 -0
  8. data/app/assets/lookbook/js/{nav/node.js → components/nav-group.js} +16 -15
  9. data/app/assets/lookbook/js/components/nav-item.js +26 -0
  10. data/app/assets/lookbook/js/components/nav.js +35 -0
  11. data/app/assets/lookbook/js/components/page.js +33 -0
  12. data/app/assets/lookbook/js/components/param.js +18 -0
  13. data/app/assets/lookbook/js/{workbench/preview.js → components/preview-window.js} +9 -10
  14. data/app/assets/lookbook/js/components/sidebar.js +3 -0
  15. data/app/assets/lookbook/js/components/sizes.js +16 -0
  16. data/app/assets/lookbook/js/components/splitter.js +25 -0
  17. data/app/assets/lookbook/js/config.js +14 -0
  18. data/app/assets/lookbook/js/{utils/reloader.js → lib/socket.js} +7 -12
  19. data/app/assets/lookbook/js/lib/split.js +21 -0
  20. data/app/assets/lookbook/js/lib/utils.js +3 -0
  21. data/app/assets/lookbook/js/stores/filter.js +11 -0
  22. data/app/assets/lookbook/js/stores/inspector.js +17 -0
  23. data/app/assets/lookbook/js/stores/layout.js +12 -0
  24. data/app/assets/lookbook/js/stores/nav.js +21 -0
  25. data/app/assets/lookbook/js/stores/sidebar.js +14 -0
  26. data/app/controllers/lookbook/app_controller.rb +82 -87
  27. data/app/helpers/lookbook/application_helper.rb +49 -5
  28. data/app/helpers/lookbook/preview_helper.rb +7 -0
  29. data/app/views/layouts/lookbook/app.html.erb +54 -0
  30. data/app/views/layouts/lookbook/preview.html.erb +12 -0
  31. data/app/views/lookbook/components/_code.html.erb +8 -0
  32. data/app/views/lookbook/{shared/_clipboard.html.erb → components/_copy.html.erb} +4 -5
  33. data/app/views/lookbook/components/_filter.html.erb +15 -0
  34. data/app/views/lookbook/{shared → components}/_header.html.erb +3 -3
  35. data/app/views/lookbook/components/_icon.html.erb +5 -0
  36. data/app/views/lookbook/components/_nav.html.erb +17 -0
  37. data/app/views/lookbook/components/_nav_collection.html.erb +5 -0
  38. data/app/views/lookbook/components/_nav_group.html.erb +17 -0
  39. data/app/views/lookbook/components/_nav_item.html.erb +21 -0
  40. data/app/views/lookbook/components/_nav_preview.html.erb +11 -0
  41. data/app/views/lookbook/components/_param.html.erb +20 -0
  42. data/app/views/lookbook/{workbench → components}/_preview.html.erb +8 -8
  43. data/app/views/lookbook/{app/error.html.erb → error.html.erb} +0 -0
  44. data/app/views/lookbook/index.html.erb +9 -0
  45. data/app/views/lookbook/inputs/_select.html.erb +8 -0
  46. data/app/views/lookbook/inputs/_text.html.erb +8 -0
  47. data/app/views/lookbook/inputs/_textarea.html.erb +8 -0
  48. data/app/views/lookbook/inputs/_toggle.html.erb +13 -0
  49. data/app/views/lookbook/{app/not_found.html.erb → not_found.html.erb} +2 -4
  50. data/app/views/lookbook/panels/_notes.html.erb +25 -0
  51. data/app/views/lookbook/panels/_output.html.erb +18 -0
  52. data/app/views/lookbook/panels/_params.html.erb +17 -0
  53. data/app/views/lookbook/panels/_source.html.erb +20 -0
  54. data/app/views/lookbook/show.html.erb +90 -0
  55. data/lib/lookbook/code_formatter.rb +20 -0
  56. data/lib/lookbook/engine.rb +14 -1
  57. data/lib/lookbook/features.rb +24 -0
  58. data/lib/lookbook/lang.rb +10 -5
  59. data/lib/lookbook/params.rb +110 -0
  60. data/lib/lookbook/preview.rb +1 -1
  61. data/lib/lookbook/preview_controller.rb +1 -1
  62. data/lib/lookbook/preview_example.rb +13 -1
  63. data/lib/lookbook/preview_group.rb +9 -1
  64. data/lib/lookbook/taggable.rb +2 -2
  65. data/lib/lookbook/version.rb +1 -1
  66. data/lib/lookbook.rb +4 -0
  67. data/public/lookbook-assets/css/app.css +2 -0
  68. data/public/lookbook-assets/css/app.css.map +1 -0
  69. data/public/lookbook-assets/js/app.js +2 -0
  70. data/public/lookbook-assets/js/app.js.map +1 -0
  71. metadata +58 -38
  72. data/app/assets/lookbook/js/nav/leaf.js +0 -20
  73. data/app/assets/lookbook/js/nav.js +0 -39
  74. data/app/assets/lookbook/js/page.js +0 -33
  75. data/app/assets/lookbook/js/utils/clipboard.js +0 -13
  76. data/app/assets/lookbook/js/utils/morph.js +0 -16
  77. data/app/assets/lookbook/js/utils/screen.js +0 -44
  78. data/app/assets/lookbook/js/utils/size_observer.js +0 -16
  79. data/app/assets/lookbook/js/utils/split.js +0 -26
  80. data/app/assets/lookbook/js/workbench/inspector.js +0 -11
  81. data/app/assets/lookbook/js/workbench.js +0 -14
  82. data/app/views/lookbook/app/index.html.erb +0 -11
  83. data/app/views/lookbook/app/show.html.erb +0 -1
  84. data/app/views/lookbook/layouts/app.html.erb +0 -41
  85. data/app/views/lookbook/nav/_collection.html.erb +0 -5
  86. data/app/views/lookbook/nav/_leaf.html.erb +0 -22
  87. data/app/views/lookbook/nav/_node.html.erb +0 -19
  88. data/app/views/lookbook/nav/_preview.html.erb +0 -11
  89. data/app/views/lookbook/preview/group.html.erb +0 -8
  90. data/app/views/lookbook/shared/_sidebar.html.erb +0 -45
  91. data/app/views/lookbook/shared/_workbench.html.erb +0 -12
  92. data/app/views/lookbook/workbench/_header.html.erb +0 -39
  93. data/app/views/lookbook/workbench/_inspector.html.erb +0 -33
  94. data/app/views/lookbook/workbench/inspector/_code.html.erb +0 -3
  95. data/app/views/lookbook/workbench/inspector/_notes.html.erb +0 -24
  96. data/app/views/lookbook/workbench/inspector/_plain.html.erb +0 -3
  97. data/public/lookbook-assets/app.css +0 -2504
  98. data/public/lookbook-assets/app.js +0 -8680
@@ -1,13 +1,17 @@
1
- export default function navNode() {
1
+ import { getAlpineData } from "../lib/utils";
2
+
3
+ export default function navGroup() {
2
4
  return {
3
- id: null,
4
- hidden: true,
5
+ hidden: false,
5
6
  children: [],
6
- init() {
7
- this.id = this.$el.id;
7
+ get id() {
8
+ return this.$root.id;
8
9
  },
9
- open() {
10
- return this.$store.nav.open[this.id];
10
+ get open() {
11
+ return this.$store.nav.isOpen(this.id);
12
+ },
13
+ toggle() {
14
+ this.$store.nav.toggle(this.id);
11
15
  },
12
16
  getChildren() {
13
17
  return this.$refs.items
@@ -15,29 +19,26 @@ export default function navNode() {
15
19
  : [];
16
20
  },
17
21
  navigateToFirstChild() {
18
- if (this.open()) {
22
+ if (this.open) {
19
23
  const child = this.firstVisibleChild();
20
24
  if (child) {
21
25
  const link = child.querySelector(":scope > a.nav-link");
22
26
  if (link) {
23
- this.navigate(link.getAttribute("href"));
27
+ this.setLocation(link.getAttribute("href"));
24
28
  }
25
29
  }
26
30
  }
27
31
  },
28
- filter() {
32
+ filter(text) {
29
33
  this.hidden = true;
30
34
  this.getChildren().forEach((child) => {
31
- const data = child._x_dataStack[0];
32
- data.filter();
35
+ const data = getAlpineData(child);
36
+ data.filter(text);
33
37
  if (!data.hidden) {
34
38
  this.hidden = false;
35
39
  }
36
40
  });
37
41
  },
38
- toggle() {
39
- this.$store.nav.open[this.id] = !this.$store.nav.open[this.id];
40
- },
41
42
  firstVisibleChild() {
42
43
  return this.getChildren().find((child) => {
43
44
  return child._x_dataStack
@@ -0,0 +1,26 @@
1
+ export default function navItem(matchers) {
2
+ return {
3
+ hidden: false,
4
+ get id() {
5
+ return this.$root.id;
6
+ },
7
+ get path() {
8
+ return this.$root.getAttribute("data-path");
9
+ },
10
+ get active() {
11
+ return this.$store.nav.active === this.id;
12
+ },
13
+ navigate() {
14
+ this.setLocation(this.path);
15
+ this.$store.sidebar.open = false;
16
+ },
17
+ filter(text) {
18
+ if (text.length) {
19
+ const matched = matchers.map((m) => m.includes(text));
20
+ this.hidden = !matched.filter((m) => m).length;
21
+ } else {
22
+ this.hidden = false;
23
+ }
24
+ },
25
+ };
26
+ }
@@ -0,0 +1,35 @@
1
+ import { getAlpineData } from "../lib/utils";
2
+
3
+ export default function nav() {
4
+ return {
5
+ empty: false,
6
+ init() {
7
+ this.$watch("$store.filter.text", () => this.filter());
8
+ this.$nextTick(() => {
9
+ this.setActive();
10
+ this.filter();
11
+ });
12
+ },
13
+ filter() {
14
+ this.empty = true;
15
+ this.getChildren().forEach((child) => {
16
+ const data = getAlpineData(child);
17
+ data.filter(this.$store.filter.text);
18
+ if (!data.hidden) {
19
+ this.empty = false;
20
+ }
21
+ });
22
+ },
23
+ getChildren() {
24
+ return this.$refs.items
25
+ ? Array.from(this.$refs.items.querySelectorAll(":scope > li"))
26
+ : [];
27
+ },
28
+ setActive() {
29
+ const target = this.$el.querySelector(
30
+ `[data-path="${window.location.pathname}"]`
31
+ );
32
+ this.$store.nav.active = target ? target.id : "";
33
+ },
34
+ };
35
+ }
@@ -0,0 +1,33 @@
1
+ import createSocket from "../lib/socket";
2
+
3
+ export default function page() {
4
+ return {
5
+ init() {
6
+ const socket = createSocket(window.SOCKET_PATH);
7
+ socket.addListener("Lookbook::ReloadChannel", () => this.refresh());
8
+ },
9
+ async update() {
10
+ const response = await fetch(window.document.location);
11
+ if (!response.ok) return window.location.reload();
12
+ const html = await response.text();
13
+ this.morph(new DOMParser().parseFromString(html, "text/html"));
14
+ },
15
+ setLocation(loc) {
16
+ const path = loc instanceof Event ? loc.currentTarget.href : loc;
17
+ history.pushState({}, null, path);
18
+ this.$dispatch("popstate");
19
+ },
20
+ refresh() {
21
+ this.$dispatch("popstate");
22
+ },
23
+ morph(dom) {
24
+ const pageHtml = dom.getElementById(this.$root.id).outerHTML;
25
+ Alpine.morph(this.$root, pageHtml, {
26
+ key(el) {
27
+ return el.getAttribute("key") ? el.getAttribute("key") : el.id;
28
+ },
29
+ });
30
+ this.$dispatch("page:morphed");
31
+ },
32
+ };
33
+ }
@@ -0,0 +1,18 @@
1
+ export default function param() {
2
+ return {
3
+ setFocus() {
4
+ if (this.$refs.input) {
5
+ setTimeout(() => this.$refs.input.focus(), 0);
6
+ }
7
+ },
8
+ update(name, value) {
9
+ const searchParams = new URLSearchParams(window.location.search);
10
+ searchParams.set(name, value);
11
+ const path = location.href.replace(location.search, "");
12
+ this.setLocation(`${path}?${searchParams.toString()}`);
13
+ },
14
+ validate() {
15
+ return this.$el.reportValidity ? this.$el.reportValidity() : true;
16
+ },
17
+ };
18
+ }
@@ -1,33 +1,32 @@
1
1
  export default function preview() {
2
- const app = Alpine.store("page");
3
- const preview = Alpine.store("preview");
4
2
  return {
5
- init() {
6
- this.root = this.$el;
7
- },
8
3
  onResize(e) {
9
4
  const size =
10
5
  this.resizeStartSize - (this.resizeStartPosition - e.pageX) * 2;
11
- const parentSize = this.root.parentElement.clientWidth;
6
+ const parentSize = this.$root.parentElement.clientWidth;
12
7
  const percentSize = (Math.round(size) / parentSize) * 100;
13
8
  const minWidth = (300 / parentSize) * 100;
14
- preview.width = `${Math.min(Math.max(percentSize, minWidth), 100)}%`;
9
+ this.$store.inspector.preview.width = `${Math.min(
10
+ Math.max(percentSize, minWidth),
11
+ 100
12
+ )}%`;
15
13
  },
16
14
  onResizeStart(e) {
17
- app.reflowing = true;
15
+ this.$store.layout.reflowing = true;
18
16
  this.onResize = this.onResize.bind(this);
19
17
  this.onResizeEnd = this.onResizeEnd.bind(this);
20
18
  this.resizeStartPosition = e.pageX;
21
- this.resizeStartSize = this.root.clientWidth;
19
+ this.resizeStartSize = this.$root.clientWidth;
22
20
  window.addEventListener("pointermove", this.onResize);
23
21
  window.addEventListener("pointerup", this.onResizeEnd);
24
22
  },
25
23
  onResizeEnd() {
26
24
  window.removeEventListener("pointermove", this.onResize);
27
25
  window.removeEventListener("pointerup", this.onResizeEnd);
28
- app.reflowing = false;
26
+ this.$store.layout.reflowing = false;
29
27
  },
30
28
  toggleFullWidth() {
29
+ const preview = this.$store.inspector.preview;
31
30
  if (preview.width === "100%" && preview.lastWidth) {
32
31
  preview.width = preview.lastWidth;
33
32
  } else {
@@ -0,0 +1,3 @@
1
+ export default function sidebar() {
2
+ return {};
3
+ }
@@ -0,0 +1,16 @@
1
+ export default function sizeObserver() {
2
+ return {
3
+ width: 0,
4
+ height: 0,
5
+ init() {
6
+ const ro = new ResizeObserver((entries) => {
7
+ const rect = entries[0].contentRect;
8
+ this.width = Math.round(rect.width);
9
+ this.height = Math.round(rect.height);
10
+ });
11
+ ro.observe(this.$el);
12
+ this.width = Math.round(this.$el.clientWidth);
13
+ this.height = Math.round(this.$el.clientHeight);
14
+ },
15
+ };
16
+ }
@@ -0,0 +1,25 @@
1
+ import Split from "split-grid";
2
+
3
+ export default function splitter(direction, props = {}) {
4
+ return {
5
+ splits: [],
6
+ init() {
7
+ const type = `${direction === "vertical" ? "column" : "row"}Gutters`;
8
+ const element = this.$el;
9
+ Split({
10
+ [type]: [{ track: 1, element }],
11
+ minSize: props.minSize || 0,
12
+ writeStyle() {},
13
+ onDrag: (dir, track, style) => {
14
+ this.splits = style.split(" ").map((num) => parseInt(num));
15
+ },
16
+ onDragStart: () => {
17
+ this.$store.layout.reflowing = true;
18
+ },
19
+ onDragEnd: () => {
20
+ this.$store.layout.reflowing = false;
21
+ },
22
+ });
23
+ },
24
+ };
25
+ }
@@ -0,0 +1,14 @@
1
+ export default {
2
+ desktopWidth: 768,
3
+ sidebar: {
4
+ defaultWidth: 280,
5
+ minWidth: 200,
6
+ maxWidth: 500,
7
+ },
8
+ inspector: {
9
+ tabs: {
10
+ default: "source",
11
+ defaultHeight: 200,
12
+ },
13
+ },
14
+ };
@@ -1,21 +1,16 @@
1
1
  import { createConsumer } from "@rails/actioncable";
2
2
  import debounce from "debounce";
3
3
 
4
- export default function (endpoint) {
4
+ export default function socket(endpoint) {
5
5
  const uid = (Date.now() + ((Math.random() * 100) | 0)).toString();
6
6
  const consumer = createConsumer(`${endpoint}?uid=${uid}`);
7
-
8
7
  return {
9
- uid,
10
- consumer,
11
- start() {
12
- const received = debounce(() => {
13
- console.log("Lookbook files changed");
14
- document.dispatchEvent(new CustomEvent("refresh"));
15
- }, 300);
16
-
17
- consumer.subscriptions.create("Lookbook::ReloadChannel", {
18
- received,
8
+ addListener(channel, callback) {
9
+ consumer.subscriptions.create(channel, {
10
+ received: debounce((data) => {
11
+ console.log("Lookbook files changed");
12
+ callback(data);
13
+ }, 300),
19
14
  connected() {
20
15
  console.log("Lookbook websocket connected");
21
16
  },
@@ -0,0 +1,21 @@
1
+ import Split from "split-grid";
2
+
3
+ export default function (element, props) {
4
+ Split({
5
+ [`${props.direction === "vertical" ? "row" : "column"}Gutters`]: [
6
+ { track: 1, element },
7
+ ],
8
+ minSize: props.minSize,
9
+ writeStyle() {},
10
+ onDrag(dir, track, style) {
11
+ const splits = style.split(" ").map((num) => parseInt(num));
12
+ props.onDrag(splits);
13
+ },
14
+ // onDragStart() {
15
+ // this.reflowing = true;
16
+ // },
17
+ // onDragEnd() {
18
+ // this.reflowing = false;
19
+ // },
20
+ });
21
+ }
@@ -0,0 +1,3 @@
1
+ export function getAlpineData(el) {
2
+ return el._x_dataStack[0];
3
+ }
@@ -0,0 +1,11 @@
1
+ export default function createFilterStore(Alpine) {
2
+ return {
3
+ raw: Alpine.$persist("").as("filter-text"),
4
+ get text() {
5
+ return this.raw.replace(/\s/g, "").toLowerCase();
6
+ },
7
+ get active() {
8
+ return this.text.length > 0;
9
+ },
10
+ };
11
+ }
@@ -0,0 +1,17 @@
1
+ import config from "../config";
2
+
3
+ export default function createInspectorStore(Alpine) {
4
+ const { tabs } = config.inspector;
5
+ return {
6
+ panels: {
7
+ active: Alpine.$persist(tabs.default).as("inspector-panel-active"),
8
+ height: Alpine.$persist(tabs.defaultHeight).as("inspector-height"),
9
+ },
10
+ preview: {
11
+ width: Alpine.$persist("100%").as("preview-width"),
12
+ height: Alpine.$persist("100%").as("preview-height"),
13
+ source: Alpine.$persist(false).as("preview-source"),
14
+ lastWidth: null,
15
+ },
16
+ };
17
+ }
@@ -0,0 +1,12 @@
1
+ import config from "../config";
2
+
3
+ export default function createLayoutStore() {
4
+ return {
5
+ init() {
6
+ this.desktop = window.innerWidth >= config.desktopWidth;
7
+ },
8
+ reflowing: false,
9
+ desktop: true,
10
+ desktopWidth: config.desktopWidth,
11
+ };
12
+ }
@@ -0,0 +1,21 @@
1
+ export default function createNavStore(Alpine) {
2
+ return {
3
+ open: Alpine.$persist([]).as("nav-open"),
4
+ active: Alpine.$persist(null).as("nav-active"),
5
+ isOpen(id) {
6
+ return this.open.includes(id);
7
+ },
8
+ setOpen(id) {
9
+ this.open.push(id);
10
+ },
11
+ setClosed(id) {
12
+ const index = this.open.indexOf(id);
13
+ if (index > -1) {
14
+ this.open.splice(index, 1);
15
+ }
16
+ },
17
+ toggle(id) {
18
+ this.isOpen(id) ? this.setClosed(id) : this.setOpen(id);
19
+ },
20
+ };
21
+ }
@@ -0,0 +1,14 @@
1
+ import config from "../config";
2
+
3
+ export default function createSidebarStore(Alpine) {
4
+ const { defaultWidth, minWidth, maxWidth } = config.sidebar;
5
+ return {
6
+ open: Alpine.$persist(false).as("sidebar-open"),
7
+ width: Alpine.$persist(defaultWidth).as("sidebar-width"),
8
+ minWidth,
9
+ maxWidth,
10
+ toggle() {
11
+ Alpine.store("sidebar").open = !Alpine.store("sidebar").open;
12
+ },
13
+ };
14
+ }
@@ -1,52 +1,44 @@
1
- require "htmlbeautifier"
2
-
3
1
  module Lookbook
4
2
  class AppController < ActionController::Base
5
3
  EXCEPTIONS = [ViewComponent::PreviewTemplateError, ViewComponent::ComponentError, ViewComponent::TemplateError, ActionView::Template::Error]
6
4
 
7
5
  protect_from_forgery with: :exception
8
- prepend_view_path File.expand_path("../../views/lookbook", __dir__)
9
-
10
- layout "layouts/app"
11
- helper Lookbook::Engine.helpers
6
+ helper Lookbook::ApplicationHelper
12
7
 
13
8
  before_action :find_preview, only: [:preview, :show]
14
9
  before_action :find_example, only: [:preview, :show]
15
- before_action :assign_nav, only: [:index, :show]
16
- before_action :initialize_inspector, only: [:show]
10
+ before_action :build_nav
11
+
12
+ def self.controller_path
13
+ "lookbook"
14
+ end
17
15
 
18
16
  def preview
19
17
  if @example
20
- render html: rendered_example
18
+ set_params
19
+ render html: render_examples(examples_data)
21
20
  else
22
- render "app/not_found"
21
+ render "not_found"
23
22
  end
24
23
  end
25
24
 
26
25
  def show
27
26
  if @example
28
27
  begin
29
- @rendered_example = rendered_example.gsub("\"", "&quot;")
30
- (@example.type == :group ? @example.examples : [@example]).each do |example|
31
- include_example_data(example)
32
- end
33
- assign_inspector
28
+ set_params
29
+ @examples = examples_data
30
+ @preview_srcdoc = render_examples(examples_data).gsub("\"", "&quot;")
31
+ @panels = panels.filter { |name, panel| panel[:show] }
34
32
  rescue *EXCEPTIONS
35
- render "app/error"
33
+ render "error"
36
34
  end
37
35
  else
38
- render "app/not_found"
36
+ render "not_found"
39
37
  end
40
38
  end
41
39
 
42
40
  private
43
41
 
44
- def initialize_inspector
45
- @source = []
46
- @output = []
47
- @notes = []
48
- end
49
-
50
42
  def find_preview
51
43
  candidates = []
52
44
  params[:path].to_s.scan(%r{/|$}) { candidates << $` }
@@ -65,83 +57,47 @@ module Lookbook
65
57
  end
66
58
  end
67
59
 
68
- def include_example_data(example)
69
- content = HtmlBeautifier.beautify(preview_controller.render_example_to_string(@preview, example.name))
70
- @output << {
71
- label: "<!-- #{example.label} -->",
72
- content: content,
73
- lang: Lookbook::Lang.find(:html)
74
- }
60
+ def examples_data
61
+ @examples_data ||= (@example.type == :group ? @example.examples : [@example]).map do |example|
62
+ example_data(example)
63
+ end
64
+ end
65
+
66
+ def example_data(example)
75
67
  render_args = @preview.render_args(example.name, params: preview_controller.params.permit!)
76
68
  has_template = render_args[:template] != "view_components/preview"
77
- @source << {
78
- label: has_template ? "<!-- #{example.label} -->" : "\# #{example.label}",
79
- content: has_template ? example.template_source(render_args[:template]) : example.method_source,
80
- lang: has_template ? example.template_lang(render_args[:template]) : example.source_lang
69
+ {
70
+ label: example.label,
71
+ notes: example.notes,
72
+ html: preview_controller.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: enabled?(:params) ? example.params : []
81
76
  }
82
- if example.notes.present?
83
- @notes << {
84
- label: example.label,
85
- content: example.notes
86
- }
87
- end
88
77
  end
89
78
 
90
- def rendered_example
91
- if @example.type == :group
92
- examples = @example.examples.map do |example|
93
- {
94
- label: example.label,
95
- html: preview_controller.render_example_to_string(@preview, example.name)
96
- }
97
- end
98
- set_params
99
- preview_controller.render_in_layout_to_string("lookbook/preview/group", {examples: examples}, @preview.lookbook_layout)
100
- else
101
- set_params(@example)
102
- preview_controller.params[:path] = "#{@preview.preview_name}/#{@example.name}".chomp("/")
103
- preview_controller.process(:previews)
104
- end
79
+ def render_examples(examples)
80
+ preview_controller.render_in_layout_to_string("layouts/lookbook/preview", {examples: examples}, @preview.lookbook_layout)
105
81
  end
106
82
 
107
- def set_params(example = nil)
108
- example_params = example.nil? ? @preview.display_params : example.display_params
83
+ 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
90
+ end
91
+ end
92
+ # set display params
109
93
  preview_controller.params.merge!({
110
94
  lookbook: {
111
- display: example_params
95
+ display: @example.display_params
112
96
  }
113
97
  })
114
98
  end
115
99
 
116
- def assign_inspector
117
- @inspector = {
118
- panes: {
119
- source: {
120
- label: "Source",
121
- template: "code",
122
- hotkey: "s",
123
- items: @source,
124
- clipboard: @source.map { |s| @source.many? ? "#{s[:label]}\n#{s[:content]}" : s[:content] }.join("\n\n")
125
- },
126
- output: {
127
- label: "Output",
128
- template: "code",
129
- hotkey: "o",
130
- items: @output,
131
- clipboard: @output.map { |o| @output.many? ? "#{o[:label]}\n#{o[:content]}" : o[:content] }.join("\n\n")
132
- },
133
- notes: {
134
- label: "Notes",
135
- template: "notes",
136
- hotkey: "n",
137
- items: @notes,
138
- disabled: @notes.none?
139
- }
140
- }
141
- }
142
- end
143
-
144
- def assign_nav
100
+ def build_nav
145
101
  @nav = Collection.new
146
102
  previews.reject { |p| p.hidden? }.each do |preview|
147
103
  current = @nav
@@ -161,6 +117,41 @@ module Lookbook
161
117
  @nav
162
118
  end
163
119
 
120
+ def panels
121
+ {
122
+ source: {
123
+ label: "Source",
124
+ template: "lookbook/panels/source",
125
+ hotkey: "s",
126
+ show: true,
127
+ disabled: false,
128
+ copy: true
129
+ },
130
+ output: {
131
+ label: "Output",
132
+ template: "lookbook/panels/output",
133
+ hotkey: "o",
134
+ show: true,
135
+ disabled: false,
136
+ copy: true
137
+ },
138
+ notes: {
139
+ label: "Notes",
140
+ template: "lookbook/panels/notes",
141
+ hotkey: "n",
142
+ show: true,
143
+ disabled: @examples.filter { |e| e[:notes].present? }.none?
144
+ },
145
+ params: {
146
+ label: "Params",
147
+ template: "lookbook/panels/params",
148
+ hotkey: "p",
149
+ show: enabled?(:params),
150
+ disabled: @example.type == :group || @example.params.none?
151
+ }
152
+ }
153
+ end
154
+
164
155
  def previews
165
156
  Lookbook::Preview.all.sort_by(&:label)
166
157
  end
@@ -174,5 +165,9 @@ module Lookbook
174
165
  controller.response = response
175
166
  @preview_controller ||= controller
176
167
  end
168
+
169
+ def enabled?(feature)
170
+ Lookbook::Features.enabled?(feature)
171
+ end
177
172
  end
178
173
  end