lookbook 0.2.2 → 0.3.0.beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +93 -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 +36 -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/layouts/group.html.erb +6 -0
- data/app/views/lookbook/nav/_collection.html.erb +5 -0
- data/app/views/lookbook/nav/_node.html.erb +19 -0
- data/app/views/lookbook/nav/_preview.html.erb +29 -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 +37 -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/engine.rb +2 -2
- data/lib/lookbook/preview.rb +25 -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 +256 -102
- data/public/lookbook-assets/app.js +964 -95
- data/{app/views/lookbook/partials/_icon_sprite.html.erb → public/lookbook-assets/feather-sprite.svg} +1 -1
- metadata +52 -25
- 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
- data/config/lookbook_cable.yml +0 -8
@@ -1,76 +0,0 @@
|
|
1
|
-
export default function preview() {
|
2
|
-
const app = Alpine.store("app");
|
3
|
-
const preview = Alpine.store("preview");
|
4
|
-
return {
|
5
|
-
init() {
|
6
|
-
this.root = this.$el;
|
7
|
-
},
|
8
|
-
onResize(e) {
|
9
|
-
const size =
|
10
|
-
this.resizeStartSize - (this.resizeStartPosition - e.pageX) * 2;
|
11
|
-
const parentSize = this.root.parentElement.clientWidth;
|
12
|
-
const percentSize = (Math.round(size) / parentSize) * 100;
|
13
|
-
const minWidth = (300 / parentSize) * 100;
|
14
|
-
preview.width = `${Math.min(Math.max(percentSize, minWidth), 100)}%`;
|
15
|
-
},
|
16
|
-
onResizeStart(e) {
|
17
|
-
app.reflowing = true;
|
18
|
-
this.onResize = this.onResize.bind(this);
|
19
|
-
this.onResizeEnd = this.onResizeEnd.bind(this);
|
20
|
-
this.resizeStartPosition = e.pageX;
|
21
|
-
this.resizeStartSize = this.root.clientWidth;
|
22
|
-
window.addEventListener("pointermove", this.onResize);
|
23
|
-
window.addEventListener("pointerup", this.onResizeEnd);
|
24
|
-
},
|
25
|
-
onResizeEnd(e) {
|
26
|
-
window.removeEventListener("pointermove", this.onResize);
|
27
|
-
window.removeEventListener("pointerup", this.onResizeEnd);
|
28
|
-
app.reflowing = false;
|
29
|
-
},
|
30
|
-
handle: {
|
31
|
-
["@pointerdown"]: "onResizeStart",
|
32
|
-
["@dblclick"]() {
|
33
|
-
if (preview.width === "100%" && preview.lastWidth) {
|
34
|
-
preview.width = preview.lastWidth;
|
35
|
-
} else {
|
36
|
-
preview.lastWidth = preview.width;
|
37
|
-
preview.width = "100%";
|
38
|
-
}
|
39
|
-
},
|
40
|
-
},
|
41
|
-
};
|
42
|
-
}
|
43
|
-
|
44
|
-
// export default function (dimension, store, { shrink = false, centered = false } = {}) {
|
45
|
-
// const position = (e) => (dimension == "height" ? e.pageY : e.pageX);
|
46
|
-
// const pane = {
|
47
|
-
// onResize(e) {
|
48
|
-
// let size =
|
49
|
-
// this.resizeStartSize -
|
50
|
-
// (shrink
|
51
|
-
// ? (this.resizeStartPosition - position(e)) * (centered ? 2 : 1)
|
52
|
-
// : (position(e) - this.resizeStartPosition) * (centered ? 2 : 1));
|
53
|
-
// const parentSize =
|
54
|
-
// dimension == "height"
|
55
|
-
// ? this.$el.parentElement.clientHeight
|
56
|
-
// : this.$el.parentElement.clientWidth;
|
57
|
-
// const percentSize = (Math.round(size) / parentSize) * 100;
|
58
|
-
// store[dimension] = `${Math.min(Math.max(percentSize, 0), 100)}%`;
|
59
|
-
// },
|
60
|
-
// onResizeStart(e) {
|
61
|
-
// Spruce.store("app").reflowing = true;
|
62
|
-
// this.resizeStartPosition = position(e);
|
63
|
-
// this.resizeStartSize = dimension == "height" ? this.$el.clientHeight : this.$el.clientWidth;
|
64
|
-
// this.onResize = this.onResize.bind(this);
|
65
|
-
// this.onResizeEnd = this.onResizeEnd.bind(this);
|
66
|
-
// window.addEventListener("pointermove", this.onResize);
|
67
|
-
// window.addEventListener("pointerup", this.onResizeEnd);
|
68
|
-
// },
|
69
|
-
// onResizeEnd() {
|
70
|
-
// Spruce.store("app").reflowing = false;
|
71
|
-
// window.removeEventListener("pointermove", this.onResize);
|
72
|
-
// window.removeEventListener("pointerup", this.onResizeEnd);
|
73
|
-
// },
|
74
|
-
// };
|
75
|
-
// return pane;
|
76
|
-
// };
|
@@ -1,8 +0,0 @@
|
|
1
|
-
<div class="flex flex-col items-center justify-center h-screen w-full">
|
2
|
-
<div class="p-4 text-center">
|
3
|
-
<svg class="feather w-10 h-10 text-gray-300 mx-auto">
|
4
|
-
<use xlink:href="#layers" />
|
5
|
-
</svg>
|
6
|
-
<h5 class="mt-4 text-gray-400 text-base">Select a preview to get started</h5>
|
7
|
-
</div>
|
8
|
-
</div>
|
@@ -1,33 +0,0 @@
|
|
1
|
-
<div class="bg-gray-50 h-full flex flex-col" x-data="{viewportHeight: 0, viewportWidth: 0}">
|
2
|
-
|
3
|
-
<header class="py-2 px-4 w-full flex-none bg-white border-b border-gray-300 flex items-center h-10">
|
4
|
-
<div class="flex items-center ">
|
5
|
-
<div class="flex items-center space-x-1">
|
6
|
-
<strong><%= @preview.label %></strong>
|
7
|
-
<span>/</span>
|
8
|
-
<span><%= @example.label %></span>
|
9
|
-
</div>
|
10
|
-
<a href="<%= preview_path %>" class="block ml-2 text-gray-400 hover:text-indigo-800 transition" x-tooltip.theme.lookbook="`Open preview in new window`" target="_blank">
|
11
|
-
<svg class="feather w-3 h-3 ">
|
12
|
-
<use xlink:href="#external-link" />
|
13
|
-
</svg>
|
14
|
-
</a>
|
15
|
-
</div>
|
16
|
-
<div class="ml-auto flex text-xs font-monospace text-gray-700 space-x-1">
|
17
|
-
<span x-text="`${viewportWidth}px`"></span>
|
18
|
-
<span class="text-gray-500">x</span>
|
19
|
-
<span x-text="`${viewportHeight}px`"></span>
|
20
|
-
</div>
|
21
|
-
</header>
|
22
|
-
|
23
|
-
<div class="grid h-full" x-data :style="`grid-template-rows: 1fr 1px ${$store.inspector.height}px`">
|
24
|
-
<%= render "partials/preview" %>
|
25
|
-
<div class="w-full gutter border-t border-gray-300 relative" x-data="split({direction: 'vertical', minSize: 200, onDrag: (splits) => { $store.inspector.height = splits[2] }})">
|
26
|
-
<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>
|
27
|
-
</div>
|
28
|
-
<% if @inspector %>
|
29
|
-
<%= render "partials/inspector/inspector" %>
|
30
|
-
<% end %>
|
31
|
-
</div>
|
32
|
-
|
33
|
-
</div>
|
@@ -1,18 +0,0 @@
|
|
1
|
-
<div class="w-full bg-gray-50">
|
2
|
-
<div class="h-full relative mx-auto bg-white" x-data="preview" :style="`width: ${$store.preview.width}`">
|
3
|
-
<iframe
|
4
|
-
src="<%= url_for lookbook.preview_path %>"
|
5
|
-
srcdoc="<%== @preview_srcdoc %>"
|
6
|
-
frameborder="0"
|
7
|
-
class="h-full w-full border-l border-gray-300 pr-4 -mx-px"
|
8
|
-
x-data="sizeObserver"
|
9
|
-
x-effect="viewportWidth = observedWidth; viewportHeight = observedHeight;"
|
10
|
-
seamless></iframe>
|
11
|
-
<div class="absolute opacity-0 inset-0 pointer-events-none" :class="{ 'pointer-events-none': !$store.app.reflowing }"></div>
|
12
|
-
<div x-bind="handle" 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" style="touch-action: none">
|
13
|
-
<svg class="h-4 w-4 text-gray-600 pointer-events-none" fill="currentColor" viewBox="0 0 24 24">
|
14
|
-
<path d="M8 5h2v14H8zM14 5h2v14h-2z"></path>
|
15
|
-
</svg>
|
16
|
-
</div>
|
17
|
-
</div>
|
18
|
-
</div>
|
@@ -1,21 +0,0 @@
|
|
1
|
-
<div class="h-screen overflow-hidden" x-data="{open: $store.nav.open, isOpen(id){ return this.open[id] }}" @popstate.window="$store.nav.active = document.location.pathname">
|
2
|
-
<div class="bg-white h-10 border-b border-gray-300 flex items-center relative">
|
3
|
-
<input type="text"
|
4
|
-
class="text-sm px-4 h-10 w-full border-0 bg-transparent focus:outline-none outline-none focus:ring-0"
|
5
|
-
x-model="$store.nav.filter"
|
6
|
-
@keyup.stop="if ($event.key === 'Escape') $store.nav.filter = ''"
|
7
|
-
placeholder="Filter previews…">
|
8
|
-
<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="$store.nav.filter = ''">
|
9
|
-
<svg class="feather w-3 h-3">
|
10
|
-
<use xlink:href="#x" />
|
11
|
-
</svg>
|
12
|
-
</button>
|
13
|
-
</div>
|
14
|
-
<div
|
15
|
-
class="bg-gray-100 relative overflow-auto"
|
16
|
-
style="height: calc(100% - 40px)"
|
17
|
-
@scroll.passive="$store.nav.scrollTop = $event.currentTarget.scrollTop;"
|
18
|
-
x-init="setTimeout(() => {$el.scrollTop = $store.nav.scrollTop}, 30)">
|
19
|
-
<%= render "partials/nav/nav" %>
|
20
|
-
</div>
|
21
|
-
</div>
|
@@ -1 +0,0 @@
|
|
1
|
-
<pre class="h-full"><code class="h-full p-4 block highlight font-monospace" style="background-image: none"><%== highlight(content.strip, lang[:name]) %></code></pre>
|
@@ -1,43 +0,0 @@
|
|
1
|
-
<div id="inspector" class="bg-white w-full overflow-hidden flex flex-col">
|
2
|
-
<div class="px-4 border-b border-gray-200 flex items-center flex-none select-none cursor-[ns-resize]">
|
3
|
-
<nav class="-mb-px flex space-x-8 cursor-auto">
|
4
|
-
<% @inspector[: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 ': $store.inspector.active === '<%= key %>',
|
10
|
-
'border-transparent text-gray-500 hover:text-gray-700': $store.inspector.active !== '<%= key %>'
|
11
|
-
}"
|
12
|
-
@click.stop.prevent="$store.inspector.active = '<%= key %>'"
|
13
|
-
>
|
14
|
-
<%== props[:label] %>
|
15
|
-
</a>
|
16
|
-
<% end %>
|
17
|
-
</nav>
|
18
|
-
</div>
|
19
|
-
<div class="flex-auto overflow-auto bg-gray-50">
|
20
|
-
<% @inspector[:panes].each do |key, props| %>
|
21
|
-
<div
|
22
|
-
class="flex flex-col h-full relative"
|
23
|
-
x-show="$store.inspector.active === '<%= key %>'" x-cloak>
|
24
|
-
<% if props[:clipboard].present? %>
|
25
|
-
<button
|
26
|
-
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"
|
27
|
-
x-data="{content: null, done: false}"
|
28
|
-
x-tooltip.theme.lookbook="done ? 'cpied!' : 'copy to clipboard'"
|
29
|
-
data-tippy-placement="left"
|
30
|
-
@click="$clipboard(content); done = true; setTimeout(() => {done = false}, 1000)">
|
31
|
-
<svg class="feather h-4 w-4 ">
|
32
|
-
<use x-bind:href="done ? '#check' : '#clipboard'" />
|
33
|
-
</svg>
|
34
|
-
<div class="hidden" x-init="content = $el.innerText"><%= h props[:clipboard].strip %></div>
|
35
|
-
</button>
|
36
|
-
<% end %>
|
37
|
-
<div class="flex flex-col h-full overflow-auto">
|
38
|
-
<%= render "partials/inspector/#{props[:template]}", key: key, **props %>
|
39
|
-
</div>
|
40
|
-
</div>
|
41
|
-
<% end %>
|
42
|
-
</div>
|
43
|
-
</div>
|
@@ -1,17 +0,0 @@
|
|
1
|
-
<li <% if collection.hierarchy_depth == 1 %> class="py-1 border-b border-gray-300 cursor-pointer"<% end %> data-type="collection"
|
2
|
-
x-data="{id: '<%= collection.id %>', hidden: false}"
|
3
|
-
@filtered="$nextTick(() => {hidden = !$el.querySelectorAll('[data-type=preview]:not(.hidden)').length})"
|
4
|
-
:class="{hidden}">
|
5
|
-
<div @click="open[id] = !open[id]" style="<%= nav_padding_style(collection.hierarchy_depth) %>">
|
6
|
-
<%= render "partials/nav/label", text: collection.label, icon: "folder" %>
|
7
|
-
</div>
|
8
|
-
<ul x-show="isOpen(id)" x-cloak>
|
9
|
-
<% collection.items.each do |item| %>
|
10
|
-
<% if item.type == :collection %>
|
11
|
-
<%= render "partials/nav/collection", collection: item %>
|
12
|
-
<% elsif item.type == :preview %>
|
13
|
-
<%= render "partials/nav/preview", preview: item %>
|
14
|
-
<% end %>
|
15
|
-
<% end %>
|
16
|
-
</ul>
|
17
|
-
</li>
|
@@ -1,13 +0,0 @@
|
|
1
|
-
<% bold ||= false %>
|
2
|
-
<div class="flex items-center cursor-pointer pr-3 py-[4px]">
|
3
|
-
<svg class="feather w-3 h-3 mr-1 text-gray-500 flex-none">
|
4
|
-
<use xlink:href="#chevron-down" x-show="isOpen(id)" x-cloak />
|
5
|
-
<use xlink:href="#chevron-right" x-show="!isOpen(id)" />
|
6
|
-
</svg>
|
7
|
-
<svg class="feather h-3.5 w-3.5 mr-1.5 flex-none text-indigo-500">
|
8
|
-
<use xlink:href="#<%= icon %>" />
|
9
|
-
</svg>
|
10
|
-
<div class="truncate whitespace-nowrap text-left <%= "font-bold" if bold %>">
|
11
|
-
<%= text %>
|
12
|
-
</div>
|
13
|
-
</div>
|
@@ -1,27 +0,0 @@
|
|
1
|
-
<nav id="nav"
|
2
|
-
class="select-none"
|
3
|
-
x-data="{empty: false}"
|
4
|
-
@filtered="$nextTick(() => {
|
5
|
-
setTimeout(() => {empty = !$el.querySelectorAll('#nav > ul > li:not(.hidden)').length}, 0)
|
6
|
-
})"
|
7
|
-
@document:updated.document="$el.innerHTML = $event.detail.doc.getElementById('nav').innerHTML">
|
8
|
-
<% if @nav.items.any? %>
|
9
|
-
<ul>
|
10
|
-
<% @nav.items.each do |item| %>
|
11
|
-
<% if item.type == :collection %>
|
12
|
-
<%= render "partials/nav/collection", collection: item %>
|
13
|
-
<% else %>
|
14
|
-
<%= render "partials/nav/preview", preview: item %>
|
15
|
-
<% end %>
|
16
|
-
<% end %>
|
17
|
-
</ul>
|
18
|
-
<div class="p-4 text-center" x-show="empty" x-cloak>
|
19
|
-
<em class="text-gray-400">No matching previews found.</em>
|
20
|
-
</div>
|
21
|
-
<% else %>
|
22
|
-
<div class="p-4">
|
23
|
-
<h4 class="text-gray-500 mb-1">No previews found.</h4>
|
24
|
-
<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>
|
25
|
-
</div>
|
26
|
-
<% end %>
|
27
|
-
</nav>
|
@@ -1,48 +0,0 @@
|
|
1
|
-
<% examples = preview.get_examples %>
|
2
|
-
<li
|
3
|
-
data-type="preview"
|
4
|
-
x-data="{
|
5
|
-
id: '<%= preview.lookbook_id %>',
|
6
|
-
hidden: false,
|
7
|
-
examples: <%= examples.map { |e| {id: e.id, matched: true, filter: e.filter_match_string} }.to_json %>,
|
8
|
-
update(){
|
9
|
-
this.examples.forEach(e => { e.matched = $store.nav.shouldDisplay(e.filter) });
|
10
|
-
this.hidden = !this.examples.filter(e => e.matched).length;
|
11
|
-
this.$dispatch('filtered');
|
12
|
-
}
|
13
|
-
}"
|
14
|
-
x-init="
|
15
|
-
$watch('$store.nav.filter', (value) => update()); update()"
|
16
|
-
:class="{hidden}"
|
17
|
-
<% if preview.hierarchy_depth == 1 %> class="py-1 border-b border-gray-300 cursor-pointer"<% end %>>
|
18
|
-
<div @click="open[id] = !open[id]" style="<%= nav_padding_style(preview.hierarchy_depth) %>">
|
19
|
-
<%= render "partials/nav/label", text: preview.label, icon: "layers", bold: true %>
|
20
|
-
</div>
|
21
|
-
<ul x-show="isOpen(id)" x-cloak>
|
22
|
-
<% examples.each do |example| %>
|
23
|
-
<li x-show="examples.find(e => e.id === '<%= example.id %>').matched">
|
24
|
-
<a
|
25
|
-
x-data="{path: '<%= show_path example.path %>'}"
|
26
|
-
:href="path"
|
27
|
-
class="pr-3 py-[3px] flex items-center w-full group transition hover:bg-gray-200 hover:bg-opacity-50"
|
28
|
-
:class="{'!bg-indigo-100': location === path}"
|
29
|
-
style="<%= nav_padding_style(example.hierarchy_depth + 1) %>"
|
30
|
-
@click.stop.prevent="history.pushState({}, null, $event.currentTarget.href); $dispatch('popstate');"
|
31
|
-
>
|
32
|
-
<div :class="{'text-gray-900': location === path, 'text-indigo-500': location !== path}">
|
33
|
-
<svg class="feather w-3.5 h-3.5 mr-1.5 flex-none group-hover:text-indigo-800 transition">
|
34
|
-
<use xlink:href="#eye" />
|
35
|
-
</svg>
|
36
|
-
</div>
|
37
|
-
<div class="truncate whitespace-nowrap">
|
38
|
-
<%= example.label %>
|
39
|
-
</div>
|
40
|
-
</a>
|
41
|
-
</li>
|
42
|
-
<% end %>
|
43
|
-
</ul>
|
44
|
-
</li>
|
45
|
-
<% if false %>
|
46
|
-
|
47
|
-
|
48
|
-
<% end %>
|