lookbook 0.2.4 → 0.3.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 +81 -0
- data/app/assets/lookbook/css/app.css +28 -0
- data/app/assets/lookbook/js/app.js +49 -24
- data/app/assets/lookbook/js/nav/leaf.js +20 -0
- data/app/assets/lookbook/js/nav/node.js +31 -0
- data/app/assets/lookbook/js/nav.js +39 -0
- data/app/assets/lookbook/js/page.js +33 -0
- data/app/assets/lookbook/js/utils/clipboard.js +13 -0
- data/app/assets/lookbook/js/utils/morph.js +16 -0
- data/app/assets/lookbook/js/{reloader.js → utils/reloader.js} +0 -0
- data/app/assets/lookbook/js/utils/screen.js +44 -0
- data/app/assets/lookbook/js/{size_observer.js → utils/size_observer.js} +1 -1
- data/app/assets/lookbook/js/{split.js → utils/split.js} +4 -4
- data/app/assets/lookbook/js/workbench/inspector.js +11 -0
- data/app/assets/lookbook/js/workbench/preview.js +39 -0
- data/app/assets/lookbook/js/workbench.js +14 -0
- data/app/controllers/lookbook/{browser_controller.rb → app_controller.rb} +58 -31
- data/app/helpers/lookbook/application_helper.rb +1 -1
- data/app/views/lookbook/_sidebar.html.erb +45 -0
- data/app/views/lookbook/_workbench.html.erb +12 -0
- data/app/views/lookbook/{browser → app}/error.html.erb +0 -0
- data/app/views/lookbook/app/index.html.erb +11 -0
- data/app/views/lookbook/{browser → app}/not_found.html.erb +1 -1
- data/app/views/lookbook/app/show.html.erb +1 -0
- data/app/views/lookbook/layouts/app.html.erb +16 -26
- data/app/views/lookbook/nav/_collection.html.erb +5 -0
- data/app/views/lookbook/nav/_leaf.html.erb +22 -0
- data/app/views/lookbook/nav/_node.html.erb +19 -0
- data/app/views/lookbook/nav/_preview.html.erb +11 -0
- data/app/views/lookbook/preview_group.html.erb +8 -0
- data/app/views/lookbook/shared/_clipboard.html.erb +11 -0
- data/app/views/lookbook/shared/_header.html.erb +8 -0
- data/app/views/lookbook/workbench/_header.html.erb +39 -0
- data/app/views/lookbook/workbench/_inspector.html.erb +32 -0
- data/app/views/lookbook/workbench/_preview.html.erb +24 -0
- data/app/views/lookbook/workbench/inspector/_code.html.erb +3 -0
- data/app/views/lookbook/workbench/inspector/_notes.html.erb +24 -0
- data/app/views/lookbook/{partials → workbench}/inspector/_plain.html.erb +0 -0
- data/config/routes.rb +3 -3
- data/lib/lookbook/preview.rb +26 -3
- data/lib/lookbook/preview_controller.rb +6 -1
- data/lib/lookbook/preview_example.rb +3 -2
- data/lib/lookbook/preview_group.rb +37 -0
- data/lib/lookbook/taggable.rb +5 -1
- data/lib/lookbook/version.rb +1 -1
- data/lib/lookbook.rb +1 -0
- data/lib/tasks/lookbook_tasks.rake +1 -1
- data/public/lookbook-assets/app.css +229 -99
- data/public/lookbook-assets/app.js +882 -56
- data/{app/views/lookbook/partials/_icon_sprite.html.erb → public/lookbook-assets/feather-sprite.svg} +1 -1
- metadata +51 -22
- data/app/assets/lookbook/js/preview.js +0 -76
- data/app/views/lookbook/browser/index.html.erb +0 -8
- data/app/views/lookbook/browser/show.html.erb +0 -33
- data/app/views/lookbook/partials/_preview.html.erb +0 -18
- data/app/views/lookbook/partials/_sidebar.html.erb +0 -21
- data/app/views/lookbook/partials/inspector/_code.html.erb +0 -1
- data/app/views/lookbook/partials/inspector/_inspector.html.erb +0 -43
- data/app/views/lookbook/partials/inspector/_prose.html.erb +0 -3
- data/app/views/lookbook/partials/nav/_collection.html.erb +0 -17
- data/app/views/lookbook/partials/nav/_label.html.erb +0 -13
- data/app/views/lookbook/partials/nav/_nav.html.erb +0 -27
- data/app/views/lookbook/partials/nav/_preview.html.erb +0 -48
@@ -1,5 +1,7 @@
|
|
1
|
+
require "htmlbeautifier"
|
2
|
+
|
1
3
|
module Lookbook
|
2
|
-
class
|
4
|
+
class AppController < ActionController::Base
|
3
5
|
EXCEPTIONS = [ViewComponent::PreviewTemplateError, ViewComponent::ComponentError, ViewComponent::TemplateError, ActionView::Template::Error]
|
4
6
|
|
5
7
|
protect_from_forgery with: :exception
|
@@ -11,43 +13,40 @@ module Lookbook
|
|
11
13
|
before_action :find_preview, only: [:preview, :show]
|
12
14
|
before_action :find_example, only: [:preview, :show]
|
13
15
|
before_action :assign_nav, only: [:index, :show]
|
14
|
-
|
15
|
-
def index
|
16
|
-
end
|
16
|
+
before_action :initialize_inspector, only: [:show]
|
17
17
|
|
18
18
|
def preview
|
19
19
|
if @example
|
20
|
-
render html:
|
20
|
+
render html: rendered_example
|
21
21
|
else
|
22
|
-
render "
|
22
|
+
render "app/not_found"
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
26
|
def show
|
27
27
|
if @example
|
28
28
|
begin
|
29
|
-
@
|
30
|
-
@
|
31
|
-
|
32
|
-
@render_output_lang = Lookbook::Lang.find(:html)
|
33
|
-
if using_preview_template?
|
34
|
-
@source = @example.method_source
|
35
|
-
@source_lang = @example.source_lang
|
36
|
-
else
|
37
|
-
@source = @example.template_source(@render_args[:template])
|
38
|
-
@source_lang = @example.template_lang(@render_args[:template])
|
29
|
+
@rendered_example = rendered_example.gsub("\"", """)
|
30
|
+
(@example.type == :group ? @example.examples : [@example]).each do |example|
|
31
|
+
include_example_data(example)
|
39
32
|
end
|
40
33
|
assign_inspector
|
41
34
|
rescue *EXCEPTIONS
|
42
|
-
render "
|
35
|
+
render "app/error"
|
43
36
|
end
|
44
37
|
else
|
45
|
-
render "
|
38
|
+
render "app/not_found"
|
46
39
|
end
|
47
40
|
end
|
48
41
|
|
49
42
|
private
|
50
43
|
|
44
|
+
def initialize_inspector
|
45
|
+
@source = []
|
46
|
+
@output = []
|
47
|
+
@notes = []
|
48
|
+
end
|
49
|
+
|
51
50
|
def find_preview
|
52
51
|
candidates = []
|
53
52
|
params[:path].to_s.scan(%r{/|$}) { candidates << $` }
|
@@ -66,12 +65,39 @@ module Lookbook
|
|
66
65
|
end
|
67
66
|
end
|
68
67
|
|
69
|
-
def
|
70
|
-
|
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
|
+
}
|
75
|
+
render_args = @preview.render_args(example.name, params: preview_controller.params.permit!)
|
76
|
+
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
|
81
|
+
}
|
82
|
+
if example.notes.present?
|
83
|
+
@notes << {
|
84
|
+
label: example.label,
|
85
|
+
content: example.notes
|
86
|
+
}
|
87
|
+
end
|
71
88
|
end
|
72
89
|
|
73
|
-
def
|
74
|
-
@
|
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
|
+
joined = render_to_string "./preview_group", locals: {examples: examples}, layout: nil
|
99
|
+
preview_controller.render_in_layout_to_string(joined, @preview.lookbook_layout)
|
100
|
+
else
|
75
101
|
preview_controller.request.params[:path] = "#{@preview.preview_name}/#{@example.name}".chomp("/")
|
76
102
|
preview_controller.process(:previews)
|
77
103
|
end
|
@@ -82,23 +108,24 @@ module Lookbook
|
|
82
108
|
panes: {
|
83
109
|
source: {
|
84
110
|
label: "Source",
|
85
|
-
content: @source || "",
|
86
111
|
template: "code",
|
87
|
-
|
88
|
-
|
112
|
+
hotkey: "s",
|
113
|
+
items: @source,
|
114
|
+
clipboard: @source.map { |s| @source.many? ? "#{s[:label]}\n#{s[:content]}" : s[:content] }.join("\n\n")
|
89
115
|
},
|
90
116
|
output: {
|
91
117
|
label: "Output",
|
92
|
-
content: @render_output || "",
|
93
118
|
template: "code",
|
94
|
-
|
95
|
-
|
119
|
+
hotkey: "o",
|
120
|
+
items: @output,
|
121
|
+
clipboard: @output.map { |o| @output.many? ? "#{o[:label]}\n#{o[:content]}" : o[:content] }.join("\n\n")
|
96
122
|
},
|
97
123
|
notes: {
|
98
124
|
label: "Notes",
|
99
|
-
|
100
|
-
|
101
|
-
|
125
|
+
template: "notes",
|
126
|
+
hotkey: "n",
|
127
|
+
items: @notes,
|
128
|
+
disabled: @notes.none?
|
102
129
|
}
|
103
130
|
}
|
104
131
|
}
|
@@ -0,0 +1,45 @@
|
|
1
|
+
<div id="sidebar" class="h-full" x-data="nav" @document:updated.window="updateNav" @navigate.window="navigate">
|
2
|
+
<div class="bg-white h-10 border-b border-gray-300 flex items-center relative">
|
3
|
+
<button class="flex-none ml-4 " x-show="!$screen('md')" @click="$dispatch('sidebar:toggle')" x-cloak>
|
4
|
+
<svg class="feather w-5 h-5 hover:text-indigo-500 transition">
|
5
|
+
<use xlink:href="/lookbook-assets/feather-sprite.svg#x" />
|
6
|
+
</svg>
|
7
|
+
</button>
|
8
|
+
<input
|
9
|
+
type="text"
|
10
|
+
class="text-sm px-4 h-10 w-full border-0 bg-transparent focus:outline-none outline-none focus:ring-0"
|
11
|
+
placeholder="Filter previews…"
|
12
|
+
x-ref="filter"
|
13
|
+
x-model="$store.nav.filter"
|
14
|
+
x-effect="if (sidebarOpenMobile) focusFilter()"
|
15
|
+
@keyup.stop="if ($event.key === 'Escape') $store.nav.filtering ? clearFilter() : unfocusFilter()"
|
16
|
+
@keyup.f.document="focusFilter"
|
17
|
+
>
|
18
|
+
<button class="text-gray-400 hover:text-indigo-500 focus:ring-0 focus:outline-none absolute top-1/2 right-2 transform -translate-y-1/2" @click="clearFilter">
|
19
|
+
<svg class="feather w-3 h-3">
|
20
|
+
<use xlink:href="/lookbook-assets/feather-sprite.svg#x" />
|
21
|
+
</svg>
|
22
|
+
</button>
|
23
|
+
</div>
|
24
|
+
<div class="relative overflow-y-auto w-full" style="height: calc(100% - 40px)">
|
25
|
+
<div x-ref="shim">
|
26
|
+
<nav id="nav" class="select-none" x-data="navNode" @nav:updated.window="filter" x-init="$watch('$store.nav.filterText', () => filter()); $nextTick(() => filter());">
|
27
|
+
<% if @nav.items.any? %>
|
28
|
+
<ul x-ref="items">
|
29
|
+
<% @nav.items.each do |item| %>
|
30
|
+
<%= render "./nav/#{item.type.to_s}", node: item %>
|
31
|
+
<% end %>
|
32
|
+
</ul>
|
33
|
+
<div class="p-4 text-center" x-show="hidden" x-cloak>
|
34
|
+
<em class="text-gray-400">No matching previews found.</em>
|
35
|
+
</div>
|
36
|
+
<% else %>
|
37
|
+
<div class="p-4">
|
38
|
+
<h4 class="text-gray-500 mb-1">No previews found.</h4>
|
39
|
+
<p class="text-gray-400 text-xs">Have you set your <a class="underline" href="https://viewcomponent.org/api.html#preview_paths">preview paths</a> config correctly?</p>
|
40
|
+
</div>
|
41
|
+
<% end %>
|
42
|
+
</nav>
|
43
|
+
</div>
|
44
|
+
</div>
|
45
|
+
</div>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<div id="workbench" class="bg-gray-50 h-screen flex flex-col" x-data="workbench">
|
2
|
+
<%= render "./workbench/header" %>
|
3
|
+
<div class="md:grid flex-grow" :style="`grid-template-rows: 1fr 1px ${$store.inspector.height}px`">
|
4
|
+
<%= render "./workbench/preview" %>
|
5
|
+
<div class="w-full gutter border-t border-gray-300 relative" x-data="split(splitProps)" x-show="$screen('md')">
|
6
|
+
<div class="h-[11px] w-full bg-transparent hover:bg-indigo-100 hover:bg-opacity-20 transition absolute left-0 right-0 transform -translate-y-1/2 cursor-[row-resize]"></div>
|
7
|
+
</div>
|
8
|
+
<% if @inspector %>
|
9
|
+
<%= render "./workbench/inspector", **@inspector %>
|
10
|
+
<% end %>
|
11
|
+
</div>
|
12
|
+
</div>
|
File without changes
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<div class="flex flex-col h-full min-h-fill w-full">
|
2
|
+
<%= render "./shared/header" %>
|
3
|
+
<div class="flex flex-col items-center justify-center h-full min-h-fill">
|
4
|
+
<div class="p-4 text-center">
|
5
|
+
<svg class="feather w-10 h-10 text-gray-300 mx-auto">
|
6
|
+
<use xlink:href="/lookbook-assets/feather-sprite.svg#layers" />
|
7
|
+
</svg>
|
8
|
+
<h5 class="mt-4 text-gray-400 text-base">Select a preview to get started</h5>
|
9
|
+
</div>
|
10
|
+
</div>
|
11
|
+
</div>
|
@@ -1,7 +1,7 @@
|
|
1
1
|
<div class="bg-white flex flex-col items-center justify-center h-screen w-full">
|
2
2
|
<div class="p-4 text-center">
|
3
3
|
<svg class="feather w-10 h-10 text-red-300 mx-auto">
|
4
|
-
<use xlink:href="#alert-triangle" />
|
4
|
+
<use xlink:href="/lookbook-assets/feather-sprite.svg#alert-triangle" />
|
5
5
|
</svg>
|
6
6
|
<div class="mt-3 text-gray-700 max-w-xs">
|
7
7
|
<% if @preview %>
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= render "./workbench" %>
|
@@ -1,9 +1,9 @@
|
|
1
1
|
<!DOCTYPE html>
|
2
|
-
<html lang="en">
|
2
|
+
<html lang="en" class="h-screen">
|
3
3
|
<head>
|
4
4
|
<meta charset="UTF-8">
|
5
5
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
6
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
|
7
7
|
|
8
8
|
<link href="/lookbook-assets/app.css" rel="stylesheet">
|
9
9
|
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>👀</text></svg>">
|
@@ -17,35 +17,25 @@
|
|
17
17
|
|
18
18
|
<title><%= [@example&.label, @preview&.label, "Lookbook"].compact.join(" :: ") %></title>
|
19
19
|
</head>
|
20
|
-
<body class="text-gray-800 font-sans text-sm antialiased">
|
20
|
+
<body class="text-gray-800 font-sans text-sm antialiased h-screen overflow-hidden">
|
21
21
|
<div
|
22
|
-
|
22
|
+
x-data="page"
|
23
|
+
x-effect="updateTitle"
|
24
|
+
@refresh.document="fetchHTML().then(doc => $dispatch('document:updated', {doc}))"
|
25
|
+
@popstate.window="fetchHTML().then(doc => { $dispatch('document:loaded', {doc}); sidebarOpenMobile = false})"
|
26
|
+
@sidebar:toggle.window="sidebarOpenMobile = !sidebarOpenMobile"
|
23
27
|
:style="`grid-template-columns: ${$store.nav.width}px 1px 1fr;`"
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
const html = await response.text();
|
31
|
-
this.doc = (new DOMParser()).parseFromString(html, 'text/html');
|
32
|
-
$dispatch(eventName, {doc: this.doc})
|
33
|
-
});
|
34
|
-
}
|
35
|
-
}"
|
36
|
-
x-effect="document.title = doc.title; location = window.location.pathname"
|
37
|
-
@refresh.document="update('document:updated')"
|
38
|
-
@popstate.window="update('document:loaded')">
|
39
|
-
<%= render "partials/sidebar" %>
|
40
|
-
<div class="h-screen gutter border-r border-gray-300 relative" x-data="split({minSize: 200, onDrag: (splits) => { $store.nav.width = Math.min(splits[0], 500) }})">
|
28
|
+
class="md:grid w-screen h-screen"
|
29
|
+
>
|
30
|
+
<div class="h-full bg-gray-100 overflow-hidden" x-show="$screen('md') || sidebarOpenMobile" x-cloak>
|
31
|
+
<%= render "./sidebar" %>
|
32
|
+
</div>
|
33
|
+
<div x-data="split(splitProps)" class="h-full gutter border-r border-gray-300 relative" x-show="$screen('md')" x-cloak>
|
41
34
|
<div class="w-[9px] h-full bg-transparent hover:bg-indigo-100 hover:bg-opacity-20 transition absolute top-0 bottom-0 transform -translate-x-1/2 cursor-[col-resize] z-10"></div>
|
42
35
|
</div>
|
43
|
-
<
|
36
|
+
<main id="main" x-effect="render" class="h-full overflow-hidden w-full" x-show="!$screen('md') || !sidebarOpenMobile" x-cloak>
|
44
37
|
<%= yield %>
|
45
|
-
</
|
46
|
-
</div>
|
47
|
-
<div class="hidden">
|
48
|
-
<%= render "partials/icon_sprite" %>
|
38
|
+
</main>
|
49
39
|
</div>
|
50
40
|
</body>
|
51
41
|
</html>
|
@@ -0,0 +1,22 @@
|
|
1
|
+
<%
|
2
|
+
path = show_path leaf.path
|
3
|
+
display ||= :leaf
|
4
|
+
label ||= leaf.label
|
5
|
+
%>
|
6
|
+
<li x-data="navLeaf" :class="{hidden}" x-init="matchers = <%= leaf.matchers.to_json %>; path = '<%= path %>'; setActive()" @popstate.window="setActive">
|
7
|
+
<a href="<%= path %>"
|
8
|
+
class="pr-3 py-[5px] flex items-center w-full group transition hover:bg-gray-200 hover:bg-opacity-50"
|
9
|
+
style="<%= nav_padding_style(depth) %>"
|
10
|
+
:class="{'!bg-indigo-100':active}"
|
11
|
+
@click.stop.prevent="navigate"
|
12
|
+
>
|
13
|
+
<div class="relative w-3.5 h-3.5 mr-1.5 <%= "ml-[3px]" if display == :node %> " :class="active ? 'text-gray-900' : 'text-indigo-500'">
|
14
|
+
<svg class="feather flex-none group-hover:text-indigo-800 transition w-3.5 h-3.5">
|
15
|
+
<use xlink:href="/lookbook-assets/feather-sprite.svg#<%= display == :node ? "layers" : "eye" %>" />
|
16
|
+
</svg>
|
17
|
+
</div>
|
18
|
+
<div class="truncate whitespace-nowrap <%= "font-bold" if display == :node %>">
|
19
|
+
<%= label %>
|
20
|
+
</div>
|
21
|
+
</a>
|
22
|
+
</li>
|
@@ -0,0 +1,19 @@
|
|
1
|
+
<li id="<%= node.id %>" x-data="navNode" <% if node.hierarchy_depth == 1 %> class="py-1 border-b border-gray-300 cursor-pointer"<% end %> :class="{hidden}" x-cloak>
|
2
|
+
<div @click="toggle(); if (open()) { <%= "navigate('#{path}')" if defined?(path) %>}" style="<%= nav_padding_style(node.hierarchy_depth) %>" class="hover:bg-gray-200 hover:bg-opacity-50">
|
3
|
+
<div class="flex items-center cursor-pointer pr-3 py-[5px]">
|
4
|
+
<svg class="feather w-3 h-3 mr-1 text-gray-500 flex-none">
|
5
|
+
<use xlink:href="/lookbook-assets/feather-sprite.svg#chevron-down" x-show="open" x-cloak />
|
6
|
+
<use xlink:href="/lookbook-assets/feather-sprite.svg#chevron-right" x-show="!open()" x-cloak />
|
7
|
+
</svg>
|
8
|
+
<svg class="feather h-3.5 w-3.5 mr-1.5 flex-none text-indigo-500">
|
9
|
+
<use xlink:href="/lookbook-assets/feather-sprite.svg#<%= node.type == :preview ? 'layers' : 'folder' %>" />
|
10
|
+
</svg>
|
11
|
+
<div class="truncate whitespace-nowrap text-left <%= "font-bold" if node.type == :preview %>">
|
12
|
+
<%= node.label %>
|
13
|
+
</div>
|
14
|
+
</div>
|
15
|
+
</div>
|
16
|
+
<ul x-ref="items" x-show="open" x-cloak>
|
17
|
+
<%= yield %>
|
18
|
+
</ul>
|
19
|
+
</li>
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<% examples = node.get_examples %>
|
2
|
+
<% if examples.many? %>
|
3
|
+
<%= render "./nav/node", node: node, path: show_path(examples.first.path) do %>
|
4
|
+
<% examples.each do |example| %>
|
5
|
+
<%= render "./nav/leaf", leaf: example, depth: example.hierarchy_depth + 1 %>
|
6
|
+
<% end %>
|
7
|
+
<% end %>
|
8
|
+
<% else %>
|
9
|
+
<% example = examples.first %>
|
10
|
+
<%= render "./nav/leaf", leaf: example, depth: example.hierarchy_depth, label: node.label, display: :node %>
|
11
|
+
<% end %>
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<button
|
2
|
+
class="p-1.5 border-b border-l border-gray-200 hover:border-gray-300 rounded-bl-md bg-white absolute top-0 right-0 text-gray-400 hover:text-indigo-500 transition"
|
3
|
+
x-data="clipboard"
|
4
|
+
x-tooltip.theme.lookbook="done ? 'copied!' : 'copy to clipboard'"
|
5
|
+
@click="save"
|
6
|
+
data-tippy-placement="left">
|
7
|
+
<svg class="feather h-4 w-4 ">
|
8
|
+
<use x-bind:href="`/lookbook-assets/feather-sprite.svg#${done ? 'check' : 'clipboard'}`" />
|
9
|
+
</svg>
|
10
|
+
<div class="hidden" x-init="content = $el.innerText"><%== yield %></div>
|
11
|
+
</button>
|
@@ -0,0 +1,8 @@
|
|
1
|
+
<header class="py-2 px-4 w-full flex-none bg-white border-b border-gray-300 flex items-center h-10">
|
2
|
+
<button class="flex-none mr-3" x-show="!$screen('md')" @click="$dispatch('sidebar:toggle')">
|
3
|
+
<svg class="feather w-5 h-5 hover:text-indigo-500 transition">
|
4
|
+
<use xlink:href="/lookbook-assets/feather-sprite.svg#menu" />
|
5
|
+
</svg>
|
6
|
+
</button>
|
7
|
+
<%= yield %>
|
8
|
+
</header>
|
@@ -0,0 +1,39 @@
|
|
1
|
+
<%= render "./shared/header" do %>
|
2
|
+
<div class="flex items-center">
|
3
|
+
<div class="flex items-center space-x-1">
|
4
|
+
<strong class="whitespace-nowrap truncate"><%= @preview.label %></strong>
|
5
|
+
<% if @preview.get_examples.many? %>
|
6
|
+
<span>/</span>
|
7
|
+
<span class="whitespace-nowrap truncate"><%= @example.label %></span>
|
8
|
+
<% end %>
|
9
|
+
</div>
|
10
|
+
</div>
|
11
|
+
<div class="flex text-xs ml-auto font-monospace text-gray-700 space-x-1" x-show="$screen('md')">
|
12
|
+
<span x-text="`${previewViewportWidth}px`"></span>
|
13
|
+
<span class="text-gray-500">x</span>
|
14
|
+
<span x-text="`${previewViewportHeight}px`"></span>
|
15
|
+
</div>
|
16
|
+
<div class="flex items-center ml-auto md:ml-3 md:pl-3 space-x-3 md:border-l border-gray-300 text-gray-400">
|
17
|
+
<a
|
18
|
+
href="<%= url_for %>"
|
19
|
+
class="block transition hover:text-indigo-800"
|
20
|
+
x-tooltip.theme.lookbook="`Refresh preview`"
|
21
|
+
@click.prevent.stop="$dispatch('navigate')"
|
22
|
+
data-hotkey="r"
|
23
|
+
>
|
24
|
+
<svg class="feather w-4 h-4">
|
25
|
+
<use xlink:href="/lookbook-assets/feather-sprite.svg#refresh-cw" />
|
26
|
+
</svg>
|
27
|
+
</a>
|
28
|
+
<a
|
29
|
+
href="<%= preview_path %>"
|
30
|
+
class="block transition hover:text-indigo-800" target="_blank"
|
31
|
+
x-tooltip.theme.lookbook="`Open in new window`"
|
32
|
+
data-hotkey="w"
|
33
|
+
>
|
34
|
+
<svg class="feather w-4 h-4">
|
35
|
+
<use xlink:href="/lookbook-assets/feather-sprite.svg#external-link" />
|
36
|
+
</svg>
|
37
|
+
</a>
|
38
|
+
</div>
|
39
|
+
<% end %>
|
@@ -0,0 +1,32 @@
|
|
1
|
+
<div id="inspector" class="bg-white w-full overflow-hidden flex flex-col" x-data="inspector" x-show="$screen('md')">
|
2
|
+
<div class="px-4 border-b border-gray-200 flex items-center flex-none select-none">
|
3
|
+
<nav class="-mb-px flex space-x-8 cursor-auto">
|
4
|
+
<% panes.each do |key, props| %>
|
5
|
+
<a
|
6
|
+
href="#<%= key %>"
|
7
|
+
class="whitespace-nowrap py-2 px-1 border-b-2 cursor-pointer <%= "!text-gray-300" if props[:disabled] %>"
|
8
|
+
:class="{
|
9
|
+
'border-indigo-400': active('<%= key %>'),
|
10
|
+
'border-transparent text-gray-500 hover:text-gray-700': !active('<%= key %>')
|
11
|
+
}"
|
12
|
+
@click.stop.prevent="switchTo('<%= key %>')"
|
13
|
+
<% if props[:hotkey] %>data-hotkey="<%= props[:hotkey] %>"<% end %>
|
14
|
+
>
|
15
|
+
<%== props[:label] %>
|
16
|
+
</a>
|
17
|
+
<% end %>
|
18
|
+
</nav>
|
19
|
+
</div>
|
20
|
+
<div class="flex-auto overflow-auto bg-gray-50">
|
21
|
+
<% panes.each do |key, props| %>
|
22
|
+
<div class="flex flex-col h-full relative" x-show="active('<%= key %>')" x-cloak>
|
23
|
+
<% if props[:clipboard].present? %>
|
24
|
+
<%= render "./shared/clipboard" do %><%= h props[:clipboard].strip %><% end %>
|
25
|
+
<% end %>
|
26
|
+
<div class="flex flex-col h-full overflow-auto">
|
27
|
+
<%= render "./workbench/inspector/#{props[:template]}", key: key, **props %>
|
28
|
+
</div>
|
29
|
+
</div>
|
30
|
+
<% end %>
|
31
|
+
</div>
|
32
|
+
</div>
|
@@ -0,0 +1,24 @@
|
|
1
|
+
<div id="preview" class="h-full md:h-auto md:min-h-0 flex w-full bg-gray-50">
|
2
|
+
<div class="relative mx-auto bg-white" x-data="preview" :style="`width: ${$screen('md') ? $store.preview.width : '100%'}`">
|
3
|
+
<iframe seamless
|
4
|
+
class="absolute h-full inset-0 w-full border-l border-gray-300 md:pr-4 md:-mx-px"
|
5
|
+
src="<%= url_for lookbook.preview_path %>"
|
6
|
+
<% if config.preview_srcdoc %>srcdoc="<%== @rendered_example %>"<% end %>
|
7
|
+
frameborder="0"
|
8
|
+
x-data="sizeObserver"
|
9
|
+
x-effect="previewViewportWidth = observedWidth; previewViewportHeight = observedHeight;"
|
10
|
+
></iframe>
|
11
|
+
<div class="absolute opacity-0 inset-0 pointer-events-none" :class="{ 'pointer-events-none': !$store.page.reflowing }"></div>
|
12
|
+
<div
|
13
|
+
class="border-l border-r border-gray-300 bg-white hover:bg-indigo-100 hover:bg-opacity-20 transition absolute right-0 inset-y-0 flex items-center w-4 cursor-[col-resize] select-none"
|
14
|
+
style="touch-action: none"
|
15
|
+
@pointerdown="onResizeStart"
|
16
|
+
@dblclick="toggleFullWidth"
|
17
|
+
x-show="$screen('md')"
|
18
|
+
>
|
19
|
+
<svg class="h-4 w-4 text-gray-600 pointer-events-none" fill="currentColor" viewBox="0 0 24 24">
|
20
|
+
<path d="M8 5h2v14H8zM14 5h2v14h-2z"></path>
|
21
|
+
</svg>
|
22
|
+
</div>
|
23
|
+
</div>
|
24
|
+
</div>
|
@@ -0,0 +1,24 @@
|
|
1
|
+
<div class="text-gray-600">
|
2
|
+
<% if items.many? %>
|
3
|
+
<div class="divide-y divide-gray-300">
|
4
|
+
<% items.each do |item| %>
|
5
|
+
<div class="px-4 pt-10 pb-8 relative">
|
6
|
+
<div class="prose prose-sm">
|
7
|
+
<%= markdown(item[:content]) %>
|
8
|
+
</div>
|
9
|
+
<h6 class="text-[11px] text-gray-600 inline-block px-2 py-0.25 bg-gray-100 border border-t-0 border-gray-300 absolute top-0 left-4 rounded-b">
|
10
|
+
<%= item[:label] %>
|
11
|
+
</h6>
|
12
|
+
</div>
|
13
|
+
<% end %>
|
14
|
+
</div>
|
15
|
+
<% else %>
|
16
|
+
<div class="p-4 prose prose-sm">
|
17
|
+
<% if items.any? %>
|
18
|
+
<%= markdown(items[0][:content]) %>
|
19
|
+
<% else %>
|
20
|
+
<em class='opacity-50'>No notes provided.</em>
|
21
|
+
<% end %>
|
22
|
+
</div>
|
23
|
+
<% end %>
|
24
|
+
</div>
|
File without changes
|
data/config/routes.rb
CHANGED
@@ -3,8 +3,8 @@ Lookbook::Engine.routes.draw do
|
|
3
3
|
mount Lookbook::Engine.websocket => Lookbook::Engine.cable.mount_path
|
4
4
|
end
|
5
5
|
|
6
|
-
root to: "
|
6
|
+
root to: "app#index", as: :home
|
7
7
|
|
8
|
-
get "/preview/*path", to: "
|
9
|
-
get "/*path", to: "
|
8
|
+
get "/preview/*path", to: "app#preview", as: :preview
|
9
|
+
get "/*path", to: "app#show", as: :show
|
10
10
|
end
|
data/lib/lookbook/preview.rb
CHANGED
@@ -2,6 +2,10 @@ module Lookbook
|
|
2
2
|
module Preview
|
3
3
|
include Taggable
|
4
4
|
|
5
|
+
def id
|
6
|
+
lookbook_path.tr("_", "-")
|
7
|
+
end
|
8
|
+
|
5
9
|
# Examples::FooBarComponent::Preview -> "Foo Bar"
|
6
10
|
def lookbook_label
|
7
11
|
super.presence || lookbook_path.split("/").last.titleize
|
@@ -19,9 +23,22 @@ module Lookbook
|
|
19
23
|
return @lookbook_examples if @lookbook_examples.present?
|
20
24
|
public_methods = public_instance_methods(false)
|
21
25
|
public_method_objects = code_object.meths.filter { |m| public_methods.include?(m.name) }
|
22
|
-
|
23
|
-
|
24
|
-
@lookbook_examples
|
26
|
+
visible = public_method_objects.map { |m| PreviewExample.new(m.name.to_s, self) }.reject(&:hidden?)
|
27
|
+
sorted = Lookbook.config.sort_examples ? visible.sort_by(&:label) : visible
|
28
|
+
@lookbook_examples = []
|
29
|
+
if code_object.groups.any?
|
30
|
+
sorted.group_by { |m| m.group }.each do |name, examples|
|
31
|
+
if name.nil?
|
32
|
+
@lookbook_examples += examples
|
33
|
+
else
|
34
|
+
name = lookbook_label if name.strip == ""
|
35
|
+
@lookbook_examples << PreviewGroup.new(name.underscore, self, examples)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
else
|
39
|
+
@lookbook_examples = sorted
|
40
|
+
end
|
41
|
+
@lookbook_examples
|
25
42
|
end
|
26
43
|
|
27
44
|
# Examples::FooBarComponentPreview -> "Examples::FooBar"
|
@@ -57,6 +74,10 @@ module Lookbook
|
|
57
74
|
lookbook_path.tr("_", "-")
|
58
75
|
end
|
59
76
|
|
77
|
+
def lookbook_layout
|
78
|
+
defined?(@layout) ? @layout : nil
|
79
|
+
end
|
80
|
+
|
60
81
|
class << self
|
61
82
|
def all
|
62
83
|
ViewComponent::Preview.all
|
@@ -69,6 +90,8 @@ module Lookbook
|
|
69
90
|
def exists?(path)
|
70
91
|
!!find(path)
|
71
92
|
end
|
93
|
+
|
94
|
+
|
72
95
|
end
|
73
96
|
|
74
97
|
private
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Lookbook
|
2
2
|
module PreviewController
|
3
|
-
def
|
3
|
+
def render_example_to_string(preview, example_name)
|
4
4
|
prepend_application_view_paths
|
5
5
|
prepend_preview_examples_view_path
|
6
6
|
@preview = preview
|
@@ -13,5 +13,10 @@ module Lookbook
|
|
13
13
|
opts[:locals] = locals if locals.present?
|
14
14
|
render_to_string template, opts
|
15
15
|
end
|
16
|
+
|
17
|
+
def render_in_layout_to_string(content, layout_override)
|
18
|
+
layout = determine_layout(layout_override, prepend_views: false)[:layout]
|
19
|
+
render_to_string html: content, layout: layout
|
20
|
+
end
|
16
21
|
end
|
17
22
|
end
|
@@ -41,8 +41,8 @@ module Lookbook
|
|
41
41
|
:example
|
42
42
|
end
|
43
43
|
|
44
|
-
def
|
45
|
-
[
|
44
|
+
def matchers
|
45
|
+
[@preview.label, label].map { |m| m.gsub(/\s/, "").downcase }
|
46
46
|
end
|
47
47
|
|
48
48
|
def hierarchy_depth
|
@@ -62,6 +62,7 @@ module Lookbook
|
|
62
62
|
Pathname.new(Dir["#{base_path}/#{template_path}.html.*"].first)
|
63
63
|
end
|
64
64
|
|
65
|
+
alias_method :group, :lookbook_group
|
65
66
|
alias_method :notes, :lookbook_notes
|
66
67
|
alias_method :hidden?, :lookbook_hidden?
|
67
68
|
end
|