lookbook 0.8.0 → 1.0.0.beta.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 +43 -830
- data/app/assets/lookbook/css/lookbook.css +55 -0
- data/app/assets/lookbook/css/themes/blue.css +42 -0
- data/app/assets/lookbook/css/themes/indigo.css +42 -0
- data/app/assets/lookbook/css/themes/zinc.css +42 -0
- data/app/assets/lookbook/css/{tooltip_theme.css → tooltip.css} +14 -8
- data/app/assets/lookbook/js/app.js +64 -61
- data/app/assets/lookbook/js/components/clipboard.js +47 -0
- data/app/assets/lookbook/js/components/tooltip.js +30 -0
- data/app/assets/lookbook/js/config.js +7 -4
- data/app/assets/lookbook/js/helpers/build.js +22 -0
- data/app/assets/lookbook/js/helpers/dom.js +45 -0
- data/app/assets/lookbook/js/helpers/layout.js +21 -0
- data/app/assets/lookbook/js/helpers/request.js +16 -0
- data/app/assets/lookbook/js/helpers/string.js +11 -0
- data/app/assets/lookbook/js/lib/socket.js +4 -3
- data/app/assets/lookbook/js/lib/tippy.js +8 -0
- data/app/assets/lookbook/js/lookbook.js +61 -0
- data/app/assets/lookbook/js/plugins/logger.js +39 -0
- data/app/assets/lookbook/js/stores/filter.js +2 -2
- data/app/assets/lookbook/js/stores/inspector.js +22 -16
- data/app/assets/lookbook/js/stores/layout.js +101 -5
- data/app/assets/lookbook/js/stores/nav.js +17 -16
- data/app/assets/lookbook/js/stores/pages.js +4 -2
- data/app/assets/lookbook/js/stores/settings.js +7 -0
- data/app/assets/lookbook/js/stores/workbench.js +29 -0
- data/app/components/lookbook/button/component.html.erb +28 -0
- data/app/components/lookbook/button/component.js +55 -0
- data/app/components/lookbook/button/component.rb +39 -0
- data/app/components/lookbook/button_group/component.html.erb +3 -0
- data/app/components/lookbook/button_group/component.rb +18 -0
- data/app/components/lookbook/code/component.css +57 -0
- data/app/components/lookbook/code/component.html.erb +10 -0
- data/app/components/lookbook/code/component.js +3 -0
- data/app/components/lookbook/code/component.rb +56 -0
- data/app/components/lookbook/code/highlight_github_light.css +217 -0
- data/app/components/lookbook/component.rb +41 -0
- data/app/components/lookbook/copy_button/component.html.erb +11 -0
- data/app/components/lookbook/copy_button/component.js +16 -0
- data/app/components/lookbook/copy_button/component.rb +23 -0
- data/app/components/lookbook/dimensions_display/component.html.erb +10 -0
- data/app/components/lookbook/dimensions_display/component.js +30 -0
- data/app/components/lookbook/dimensions_display/component.rb +18 -0
- data/app/components/lookbook/embed/component.html.erb +50 -0
- data/app/components/lookbook/embed/component.js +39 -0
- data/app/components/lookbook/embed/component.rb +22 -0
- data/app/components/lookbook/filter/component.html.erb +17 -0
- data/app/components/lookbook/filter/component.js +21 -0
- data/app/components/lookbook/filter/component.rb +15 -0
- data/app/components/lookbook/header/component.html.erb +79 -0
- data/app/components/lookbook/header/component.rb +9 -0
- data/app/components/lookbook/icon/component.css +11 -0
- data/app/components/lookbook/icon/component.html.erb +5 -0
- data/app/components/lookbook/icon/component.js +5 -0
- data/app/components/lookbook/icon/component.rb +23 -0
- data/app/components/lookbook/nav/component.html.erb +33 -0
- data/app/components/lookbook/nav/component.js +52 -0
- data/app/components/lookbook/nav/component.rb +37 -0
- data/app/components/lookbook/nav/item/component.html.erb +23 -0
- data/app/components/lookbook/nav/item/component.js +66 -0
- data/app/components/lookbook/nav/item/component.rb +84 -0
- data/app/components/lookbook/params_editor/component.html.erb +3 -0
- data/app/components/lookbook/params_editor/component.js +12 -0
- data/app/components/lookbook/params_editor/component.rb +11 -0
- data/app/components/lookbook/params_editor/field/component.html.erb +50 -0
- data/app/components/lookbook/params_editor/field/component.js +36 -0
- data/app/components/lookbook/params_editor/field/component.rb +41 -0
- data/app/components/lookbook/prose/component.css +12 -0
- data/app/components/lookbook/prose/component.html.erb +3 -0
- data/app/components/lookbook/prose/component.rb +26 -0
- data/app/components/lookbook/split_layout/component.html.erb +13 -0
- data/app/components/lookbook/split_layout/component.js +151 -0
- data/app/components/lookbook/split_layout/component.rb +11 -0
- data/app/components/lookbook/tabbed_content/component.html.erb +5 -0
- data/app/components/lookbook/tabbed_content/component.js +21 -0
- data/app/components/lookbook/tabbed_content/component.rb +20 -0
- data/app/components/lookbook/tabbed_content/section/component.html.erb +8 -0
- data/app/components/lookbook/tabbed_content/section/component.rb +9 -0
- data/app/components/lookbook/tabs/component.css +8 -0
- data/app/components/lookbook/tabs/component.html.erb +14 -0
- data/app/components/lookbook/tabs/component.js +107 -0
- data/app/components/lookbook/tabs/component.rb +30 -0
- data/app/components/lookbook/tabs/dropdown_tab/component.html.erb +14 -0
- data/app/components/lookbook/tabs/dropdown_tab/component.rb +16 -0
- data/app/components/lookbook/tabs/tab/component.html.erb +18 -0
- data/app/components/lookbook/tabs/tab/component.rb +16 -0
- data/app/components/lookbook/tag_component.rb +29 -0
- data/app/components/lookbook/toolbar/component.css +16 -0
- data/app/components/lookbook/toolbar/component.html.erb +5 -0
- data/app/components/lookbook/toolbar/component.rb +26 -0
- data/app/components/lookbook/viewport/component.css +11 -0
- data/app/components/lookbook/viewport/component.html.erb +57 -0
- data/app/{assets/lookbook/js/components/preview-window.js → components/lookbook/viewport/component.js} +57 -14
- data/app/components/lookbook/viewport/component.rb +21 -0
- data/app/controllers/lookbook/application_controller.rb +16 -5
- data/app/controllers/lookbook/pages_controller.rb +18 -8
- data/app/controllers/lookbook/previews_controller.rb +60 -23
- data/app/helpers/lookbook/application_helper.rb +5 -1
- data/app/helpers/lookbook/component_helper.rb +22 -10
- data/app/helpers/lookbook/output_helper.rb +8 -4
- data/app/helpers/lookbook/page_helper.rb +13 -21
- data/app/views/layouts/lookbook/application.html.erb +76 -28
- data/app/views/layouts/lookbook/inspector.html.erb +7 -0
- data/app/views/layouts/lookbook/page.html.erb +53 -0
- data/app/views/layouts/lookbook/shell.html.erb +64 -0
- data/app/views/layouts/lookbook/skeleton.html.erb +27 -10
- data/app/views/layouts/lookbook/standalone.html.erb +5 -0
- data/app/views/lookbook/404.html.erb +15 -0
- data/app/views/lookbook/error.html.erb +34 -34
- data/app/views/lookbook/index.html.erb +11 -6
- data/app/views/lookbook/pages/show.html.erb +29 -67
- data/app/views/{layouts/lookbook → lookbook}/preview.html.erb +3 -5
- data/app/views/lookbook/previews/panels/_notes.html.erb +19 -25
- data/app/views/lookbook/previews/panels/_output.html.erb +7 -18
- data/app/views/lookbook/previews/panels/_params.html.erb +13 -15
- data/app/views/lookbook/previews/panels/_preview.html.erb +6 -52
- data/app/views/lookbook/previews/panels/_source.html.erb +7 -16
- data/app/views/lookbook/previews/show.html.erb +130 -24
- data/config/routes.rb +5 -5
- data/lib/lookbook/code_formatter.rb +37 -13
- data/lib/lookbook/collection.rb +19 -16
- data/lib/lookbook/config.rb +125 -0
- data/lib/lookbook/engine.rb +79 -74
- data/lib/lookbook/entity.rb +47 -0
- data/lib/lookbook/error.rb +1 -2
- data/lib/lookbook/features.rb +1 -1
- data/lib/lookbook/markdown.rb +3 -4
- data/lib/lookbook/page.rb +21 -12
- data/lib/lookbook/page_collection.rb +8 -0
- data/lib/lookbook/params.rb +15 -4
- data/lib/lookbook/preview.rb +15 -6
- data/lib/lookbook/preview_collection.rb +8 -0
- data/lib/lookbook/preview_controller.rb +6 -2
- data/lib/lookbook/preview_example.rb +5 -6
- data/lib/lookbook/preview_group.rb +4 -9
- data/lib/lookbook/{code_inspector.rb → source_inspector.rb} +2 -2
- data/lib/lookbook/theme.rb +22 -0
- data/lib/lookbook/utils.rb +10 -2
- data/lib/lookbook/version.rb +1 -1
- data/lib/lookbook.rb +4 -1
- data/lib/tasks/lookbook_tasks.rake +12 -0
- data/public/lookbook-assets/css/app.css +2340 -1
- data/public/lookbook-assets/css/app.css.map +11 -1
- data/public/lookbook-assets/css/lookbook.css +3040 -0
- data/public/lookbook-assets/css/lookbook.css.map +1 -0
- data/public/lookbook-assets/css/themes/blue.css +44 -0
- data/public/lookbook-assets/css/themes/blue.css.map +1 -0
- data/public/lookbook-assets/css/themes/indigo.css +44 -0
- data/public/lookbook-assets/css/themes/indigo.css.map +1 -0
- data/public/lookbook-assets/css/themes/zinc.css +44 -0
- data/public/lookbook-assets/css/themes/zinc.css.map +1 -0
- data/public/lookbook-assets/js/app.js +10861 -1
- data/public/lookbook-assets/js/app.js.map +2571 -1
- data/public/lookbook-assets/js/embed.js +895 -1
- data/public/lookbook-assets/js/embed.js.map +1 -1
- data/public/lookbook-assets/js/lookbook.js +13529 -0
- data/public/lookbook-assets/js/lookbook.js.map +1 -0
- metadata +127 -114
- data/app/assets/lookbook/css/app.css +0 -161
- data/app/assets/lookbook/css/code_theme.css +0 -214
- data/app/assets/lookbook/js/components/app.js +0 -55
- data/app/assets/lookbook/js/components/code.js +0 -5
- data/app/assets/lookbook/js/components/copy.js +0 -20
- data/app/assets/lookbook/js/components/embed.js +0 -89
- data/app/assets/lookbook/js/components/filter.js +0 -35
- data/app/assets/lookbook/js/components/inspector.js +0 -66
- data/app/assets/lookbook/js/components/nav-group.js +0 -47
- data/app/assets/lookbook/js/components/nav-item.js +0 -29
- data/app/assets/lookbook/js/components/nav.js +0 -28
- data/app/assets/lookbook/js/components/page.js +0 -25
- data/app/assets/lookbook/js/components/param.js +0 -34
- data/app/assets/lookbook/js/components/sidebar.js +0 -18
- data/app/assets/lookbook/js/components/sizes.js +0 -16
- data/app/assets/lookbook/js/components/splitter.js +0 -25
- data/app/assets/lookbook/js/components/tabs.js +0 -52
- data/app/assets/lookbook/js/lib/split.js +0 -15
- data/app/assets/lookbook/js/stores/sidebar.js +0 -26
- data/app/views/layouts/lookbook/basic.html.erb +0 -7
- data/app/views/lookbook/components/_branding.html.erb +0 -8
- data/app/views/lookbook/components/_code.html.erb +0 -17
- data/app/views/lookbook/components/_copy_button.html.erb +0 -11
- data/app/views/lookbook/components/_drawer.html.erb +0 -112
- data/app/views/lookbook/components/_embed.html.erb +0 -39
- data/app/views/lookbook/components/_errors.html.erb +0 -13
- data/app/views/lookbook/components/_filter.html.erb +0 -18
- data/app/views/lookbook/components/_header.html.erb +0 -6
- data/app/views/lookbook/components/_icon.html.erb +0 -5
- data/app/views/lookbook/components/_nav.html.erb +0 -16
- data/app/views/lookbook/components/_nav_collection.html.erb +0 -5
- data/app/views/lookbook/components/_nav_group.html.erb +0 -14
- data/app/views/lookbook/components/_nav_item.html.erb +0 -24
- data/app/views/lookbook/components/_nav_page.html.erb +0 -22
- data/app/views/lookbook/components/_nav_preview.html.erb +0 -13
- data/app/views/lookbook/components/_not_found.html.erb +0 -11
- data/app/views/lookbook/components/_param.html.erb +0 -21
- data/app/views/lookbook/components/_preview.html.erb +0 -77
- data/app/views/lookbook/components/_sidebar.html.erb +0 -69
- data/app/views/lookbook/pages/not_found.html.erb +0 -15
- data/app/views/lookbook/previews/error.html.erb +0 -1
- data/app/views/lookbook/previews/inputs/_select.html.erb +0 -7
- data/app/views/lookbook/previews/inputs/_text.html.erb +0 -8
- data/app/views/lookbook/previews/inputs/_textarea.html.erb +0 -8
- data/app/views/lookbook/previews/inputs/_toggle.html.erb +0 -13
- data/app/views/lookbook/previews/not_found.html.erb +0 -23
@@ -0,0 +1,21 @@
|
|
1
|
+
export default function tabbedContentComponent(store) {
|
2
|
+
return {
|
3
|
+
get id() {
|
4
|
+
return this.$root.id;
|
5
|
+
},
|
6
|
+
|
7
|
+
get sections() {
|
8
|
+
return Array.from(this.$refs.sections.children);
|
9
|
+
},
|
10
|
+
|
11
|
+
isActive(el) {
|
12
|
+
return store.activeTab === this._getRef(el);
|
13
|
+
},
|
14
|
+
|
15
|
+
// protected
|
16
|
+
|
17
|
+
_getRef(el) {
|
18
|
+
return el.getAttribute("x-ref");
|
19
|
+
},
|
20
|
+
};
|
21
|
+
}
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Lookbook
|
2
|
+
class TabbedContent::Component < Lookbook::Component
|
3
|
+
renders_many :sections, ->(ref: nil, **attrs) do
|
4
|
+
@section_counter += 1
|
5
|
+
ref ||= "tab-#{@section_counter}"
|
6
|
+
Lookbook::TabbedContent::Section::Component.new ref: ref, **attrs
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(**html_attrs)
|
10
|
+
@section_counter = 0
|
11
|
+
super(**html_attrs)
|
12
|
+
end
|
13
|
+
|
14
|
+
protected
|
15
|
+
|
16
|
+
def alpine_component
|
17
|
+
"tabbedContentComponent"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<%= render_component_tag class: "px-4 flex items-center relative" do %>
|
2
|
+
<div x-ref="tabs" class="flex items-stretch h-10 space-x-6 select-none min-w-0">
|
3
|
+
<%= safe_join(tabs) %>
|
4
|
+
</div>
|
5
|
+
<div x-ref="dropdownTrigger" class="absolute" x-show="hasHiddenTabs" :style="{left: `${triggerLeft}px`}">
|
6
|
+
<%= render_component :button, icon: :chevrons_right, x_show: "visibleTabsCount > 0" %>
|
7
|
+
<%= render_component :button, icon: :menu, class: "-ml-3", x_show: "visibleTabsCount === 0" %>
|
8
|
+
</div>
|
9
|
+
<div class="hidden">
|
10
|
+
<div x-ref="tabsDropdown" data-cloak>
|
11
|
+
<%= safe_join(dropdown_tabs) %>
|
12
|
+
</div>
|
13
|
+
</div>
|
14
|
+
<% end %>
|
@@ -0,0 +1,107 @@
|
|
1
|
+
import debounce from "debounce";
|
2
|
+
import tippy from "~/app/assets/lookbook/js/lib/tippy";
|
3
|
+
import { observeSize } from "@helpers/layout";
|
4
|
+
import { getElementSize } from "@helpers/dom";
|
5
|
+
|
6
|
+
export default function tabsComponent(store) {
|
7
|
+
const initial = store.activeTab || null;
|
8
|
+
let dropdown = null;
|
9
|
+
return {
|
10
|
+
visibleTabsCount: 0,
|
11
|
+
|
12
|
+
triggerLeft: 0,
|
13
|
+
|
14
|
+
get tabs() {
|
15
|
+
return Array.from(this.$refs.tabs.children);
|
16
|
+
},
|
17
|
+
|
18
|
+
get dropdownTabs() {
|
19
|
+
return Array.from(
|
20
|
+
this.$refs.tabsDropdown ? this.$refs.tabsDropdown.children : []
|
21
|
+
);
|
22
|
+
},
|
23
|
+
|
24
|
+
get tabWidths() {
|
25
|
+
return this.tabs.map(
|
26
|
+
(tab) => getElementSize(tab, { includeMargins: true }).width
|
27
|
+
);
|
28
|
+
},
|
29
|
+
|
30
|
+
init() {
|
31
|
+
this.$nextTick(() => {
|
32
|
+
dropdown = tippy(this.$refs.dropdownTrigger, {
|
33
|
+
content: this.$refs.tabsDropdown,
|
34
|
+
theme: "menu",
|
35
|
+
interactive: true,
|
36
|
+
trigger: "click",
|
37
|
+
appendTo: this.$root,
|
38
|
+
});
|
39
|
+
|
40
|
+
const initialTab = initial
|
41
|
+
? this.tabs.find((t) => this._getRef(t) === initial)
|
42
|
+
: this.tabs[0];
|
43
|
+
this.selectTab(initialTab);
|
44
|
+
|
45
|
+
this.parentObserver = observeSize(
|
46
|
+
this.$root.parentElement,
|
47
|
+
debounce(this.handleResize.bind(this), 10)
|
48
|
+
);
|
49
|
+
|
50
|
+
this.$watch("visibleTabsCount", (value) => {
|
51
|
+
this.debug(`'#${this.$root.id}' visible tabs count:`, value);
|
52
|
+
});
|
53
|
+
});
|
54
|
+
},
|
55
|
+
|
56
|
+
handleResize({ width }) {
|
57
|
+
if (width === this._lastMeasuredWidth) {
|
58
|
+
return;
|
59
|
+
}
|
60
|
+
|
61
|
+
if (width === this.$root.offsetWidth) {
|
62
|
+
this.visibleTabsCount = this.tabs.length;
|
63
|
+
return;
|
64
|
+
}
|
65
|
+
|
66
|
+
let sumTabWidths = 60;
|
67
|
+
let triggerLeft = 20;
|
68
|
+
let visibleTabsCount = 0;
|
69
|
+
this.tabWidths.forEach((tabWidth) => {
|
70
|
+
sumTabWidths += tabWidth;
|
71
|
+
if (sumTabWidths < width) {
|
72
|
+
triggerLeft += tabWidth;
|
73
|
+
visibleTabsCount++;
|
74
|
+
}
|
75
|
+
});
|
76
|
+
|
77
|
+
this.visibleTabsCount = visibleTabsCount;
|
78
|
+
this.triggerLeft = triggerLeft;
|
79
|
+
this._lastMeasuredWidth = width;
|
80
|
+
},
|
81
|
+
|
82
|
+
selectTab(el) {
|
83
|
+
store.activeTab = this._getRef(el);
|
84
|
+
dropdown.hide();
|
85
|
+
},
|
86
|
+
|
87
|
+
isSelected(el) {
|
88
|
+
return store.activeTab === this._getRef(el);
|
89
|
+
},
|
90
|
+
|
91
|
+
isDisabled(el) {
|
92
|
+
return el.getAttribute("data-disabled") == "true";
|
93
|
+
},
|
94
|
+
|
95
|
+
hasHiddenTabs() {
|
96
|
+
return this.visibleTabsCount < this.tabs.length;
|
97
|
+
},
|
98
|
+
|
99
|
+
// protected
|
100
|
+
|
101
|
+
_lastMeasuredWidth: 0,
|
102
|
+
|
103
|
+
_getRef(el) {
|
104
|
+
return el ? el.getAttribute("x-ref").replace("dropdown-", "") : null;
|
105
|
+
},
|
106
|
+
};
|
107
|
+
}
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Lookbook
|
2
|
+
class Tabs::Component < Lookbook::Component
|
3
|
+
renders_many :tabs, ->(ref: nil, **attrs) do
|
4
|
+
@tab_counter += 1
|
5
|
+
ref ||= "tab-#{@tab_counter}"
|
6
|
+
attrs = {
|
7
|
+
ref: ref,
|
8
|
+
position: @tab_counter,
|
9
|
+
**attrs
|
10
|
+
}
|
11
|
+
dropdown_tab(**attrs)
|
12
|
+
Lookbook::Tabs::Tab::Component.new(**attrs)
|
13
|
+
end
|
14
|
+
|
15
|
+
renders_many :dropdown_tabs, ->(ref:, **attrs) do
|
16
|
+
Lookbook::Tabs::DropdownTab::Component.new(ref: "dropdown-#{ref}", **attrs)
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(**html_attrs)
|
20
|
+
@tab_counter = 0
|
21
|
+
super(**html_attrs)
|
22
|
+
end
|
23
|
+
|
24
|
+
protected
|
25
|
+
|
26
|
+
def alpine_component
|
27
|
+
"tabsComponent"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<%= render_component_tag :button,
|
2
|
+
x_ref: @ref,
|
3
|
+
class: "whitespace-nowrap px-4 py-2 border-l-2 cursor-pointer block w-full text-left",
|
4
|
+
":class": "{
|
5
|
+
'border-lookbook-tabs-highlight': isSelected($el),
|
6
|
+
'border-transparent text-lookbook-tabs-text hover:text-lookbook-tabs-text-hover': !isSelected($el),
|
7
|
+
'hidden': visibleTabsCount >= #{@position}
|
8
|
+
}",
|
9
|
+
"@click.prevent": "selectTab($el)",
|
10
|
+
data: {
|
11
|
+
disabled: @disabled
|
12
|
+
} do %>
|
13
|
+
<%= label %>
|
14
|
+
<% end %>
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Lookbook
|
2
|
+
class Tabs::DropdownTab::Component < Lookbook::Component
|
3
|
+
def initialize(ref:, label: nil, hotkey: nil, disabled: nil, position: 0, **html_attrs)
|
4
|
+
@ref = ref
|
5
|
+
@label = label
|
6
|
+
@hotkey = hotkey
|
7
|
+
@disabled = disabled
|
8
|
+
@position = position
|
9
|
+
super(**html_attrs)
|
10
|
+
end
|
11
|
+
|
12
|
+
def label
|
13
|
+
@label.presence || content
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<%= render_component_tag :button,
|
2
|
+
id: @id,
|
3
|
+
x_ref: @ref,
|
4
|
+
class: "whitespace-nowrap pt-2.5 pb-1.5 px-1 border-b-2 cursor-pointer",
|
5
|
+
":class": "{
|
6
|
+
'border-lookbook-tabs-highlight': isSelected($el),
|
7
|
+
'border-transparent text-lookbook-tabs-text hover:text-lookbook-tabs-text-hover': !isSelected($el),
|
8
|
+
'!text-lookbook-tabs-text-disabled': isDisabled($el),
|
9
|
+
'invisible pointer-events-none relative z-[-1]': visibleTabsCount < #{@position}
|
10
|
+
}",
|
11
|
+
"@click.prevent": "selectTab($el)",
|
12
|
+
"@keydown.window.prevent.#{@hotkey}": @hotkey.present? ? "$el.click()" : nil,
|
13
|
+
data: {
|
14
|
+
disabled: @disabled
|
15
|
+
},
|
16
|
+
cloak: true do %>
|
17
|
+
<%= label %>
|
18
|
+
<% end %>
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Lookbook
|
2
|
+
class Tabs::Tab::Component < Lookbook::Component
|
3
|
+
def initialize(ref:, label: nil, hotkey: nil, disabled: nil, position: 0, **html_attrs)
|
4
|
+
@ref = ref
|
5
|
+
@label = label
|
6
|
+
@hotkey = hotkey
|
7
|
+
@disabled = disabled
|
8
|
+
@position = position
|
9
|
+
super(**html_attrs)
|
10
|
+
end
|
11
|
+
|
12
|
+
def label
|
13
|
+
@label.presence || content
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Lookbook
|
2
|
+
class TagComponent < ViewComponent::Base
|
3
|
+
include Lookbook::ComponentHelper
|
4
|
+
|
5
|
+
def initialize(tag: :div, name: nil, cloak: false, **html_attrs)
|
6
|
+
@tag = tag
|
7
|
+
html_attrs[:class] = class_names(html_attrs[:class]) if html_attrs[:class]
|
8
|
+
html_attrs[:data] ||= {}
|
9
|
+
html_attrs[:data][:component] = name if name.present?
|
10
|
+
html_attrs[:x_cloak] = true if cloak == true
|
11
|
+
@html_attrs = convert_attrs(html_attrs)
|
12
|
+
end
|
13
|
+
|
14
|
+
def call
|
15
|
+
@html_attrs[Rails.version.to_f < 7.0 ? :escape_attributes : :escape] = false
|
16
|
+
tag.public_send(@tag, **@html_attrs) do
|
17
|
+
content
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
|
23
|
+
def convert_attrs(html_attrs)
|
24
|
+
html_attrs.map do |name, value|
|
25
|
+
[name.to_s.tr("_", "-"), value]
|
26
|
+
end.to_h
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
@layer components {
|
2
|
+
[data-component="toolbar"] {
|
3
|
+
& .toolbar-sections > * {
|
4
|
+
@apply h-10 flex items-center whitespace-nowrap border-lookbook-toolbar-divider;
|
5
|
+
}
|
6
|
+
|
7
|
+
& [data-component="button-group"] {
|
8
|
+
padding-left: 0.45rem;
|
9
|
+
padding-right: 0.45rem;
|
10
|
+
}
|
11
|
+
|
12
|
+
& [data-component="tabs"] {
|
13
|
+
@apply relative -mb-px;
|
14
|
+
}
|
15
|
+
}
|
16
|
+
}
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Lookbook
|
2
|
+
class Toolbar::Component < Lookbook::Component
|
3
|
+
DIVIDE_CLASSES = {
|
4
|
+
left: "border-l",
|
5
|
+
right: "border-r"
|
6
|
+
}
|
7
|
+
|
8
|
+
ALIGN_CLASSES = {
|
9
|
+
right: "ml-auto",
|
10
|
+
left: "mr-auto",
|
11
|
+
middle: "mx-auto"
|
12
|
+
}
|
13
|
+
|
14
|
+
renders_many :sections, ->(align: :nil, divide: nil, padded: false, **attrs, &block) do
|
15
|
+
Lookbook::TagComponent.new class: [
|
16
|
+
"min-w-0",
|
17
|
+
{
|
18
|
+
"px-4": padded,
|
19
|
+
"#{DIVIDE_CLASSES[divide]}": divide.present?,
|
20
|
+
"#{ALIGN_CLASSES[align]}": align.present?
|
21
|
+
},
|
22
|
+
attrs[:class]
|
23
|
+
], **attrs.except(:class), &block
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
@layer components {
|
2
|
+
[data-component="viewport"] {
|
3
|
+
background-color: #ffffff;
|
4
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3E%3Cg fill='%23f3f3f3' fill-opacity='1'%3E%3Cpath fill-rule='evenodd' d='M0 0h4v4H0V0zm4 4h4v4H4V4z'/%3E%3C/g%3E%3C/svg%3E");
|
5
|
+
|
6
|
+
.resize-handle {
|
7
|
+
@apply flex items-center justify-center h-full w-full border-lookbook-divider bg-lookbook-viewport-handle hover:bg-indigo-100 hover:bg-opacity-20;
|
8
|
+
@apply text-lookbook-viewport-handle-icon hover:text-lookbook-viewport-handle-icon-hover transition select-none touch-none;
|
9
|
+
}
|
10
|
+
}
|
11
|
+
}
|
@@ -0,0 +1,57 @@
|
|
1
|
+
<%= render_component_tag class: "h-full min-h-0 w-full overflow-hidden relative", "@viewport:reload.window": "reloadIframe" do %>
|
2
|
+
<div class="absolute border border-lookbook-divider w-full h-full"></div>
|
3
|
+
<div
|
4
|
+
x-ref="wrapper"
|
5
|
+
class="mx-auto h-full w-full border border-b-0 border-lookbook-divider relative"
|
6
|
+
:style="`<% if @resize_width %>max-width: ${maxWidth};<% end %>
|
7
|
+
<% if @resize_height && @max_height.present? %>
|
8
|
+
max-height: min(${maxHeight}, <%= @max_height %>px);
|
9
|
+
<% elsif @resize_height %>
|
10
|
+
max-height: ${maxHeight};
|
11
|
+
<% elsif @max_height.present? %>
|
12
|
+
max-height: <%= @max_height %>px;
|
13
|
+
<% end %>`">
|
14
|
+
<div
|
15
|
+
class="grid bg-white relative -inset-px <%= "grid-cols-[1fr_17px]" if @resize_width %> <%= "grid-rows-[1fr_17px]" if @resize_height %>"
|
16
|
+
style="width: calc(100% + 2px); height: calc(100% + 1px); <%= "max-height: #{@max_height}px;" if @max_height.present? %>">
|
17
|
+
<iframe seamless
|
18
|
+
x-ref="iframe"
|
19
|
+
class="h-full w-full border border-lookbook-divider"
|
20
|
+
:class="{ 'pointer-events-none': reflowing }"
|
21
|
+
style="<%= "max-height: #{@max_height}px;" if @max_height.present? %>"
|
22
|
+
src="<%= @src %>"
|
23
|
+
frameborder="0"
|
24
|
+
@load="$dispatch('viewport:loaded', {viewport: this})"></iframe>
|
25
|
+
<% if @resize_width %>
|
26
|
+
<div
|
27
|
+
class="resize-handle border-r border-t border-lookbook-divider cursor-[col-resize] <%= "border-b" unless @resize_height %>"
|
28
|
+
@pointerdown="onResizeWidthStart"
|
29
|
+
@dblclick="toggleFullWidth">
|
30
|
+
<svg class="h-4 w-4 pointer-events-none" fill="currentColor" viewBox="0 0 24 24">
|
31
|
+
<path d="M8 5h2v14H8zM14 5h2v14h-2z"></path>
|
32
|
+
</svg>
|
33
|
+
</div>
|
34
|
+
<% end %>
|
35
|
+
<% if @resize_height %>
|
36
|
+
<div
|
37
|
+
class="resize-handle border-b border-l border-lookbook-divider cursor-[col-resize] <%= "border-r" unless @resize_width %>"
|
38
|
+
@pointerdown="onResizeHeightStart"
|
39
|
+
@dblclick="toggleFullHeight">
|
40
|
+
<svg class="h-4 w-4 pointer-events-none rotate-90" fill="currentColor" viewBox="0 0 24 24" >
|
41
|
+
<path d="M8 5h2v14H8zM14 5h2v14h-2z"></path>
|
42
|
+
</svg>
|
43
|
+
</div>
|
44
|
+
<% end %>
|
45
|
+
<% if @resize_height && @resize_width %>
|
46
|
+
<div
|
47
|
+
class="resize-handle border-r border-b border-lookbook-divider cursor-[nwse-resize]"
|
48
|
+
@pointerdown="onResizeStart"
|
49
|
+
@dblclick="toggleFullSize">
|
50
|
+
<svg class="h-3.5 w-3.5 pointer-events-none rotate-45 relative -top-px -left-px" fill="currentColor" viewBox="0 0 24 24" >
|
51
|
+
<path d="M8 5h2v14H8zM14 5h2v14h-2z"></path>
|
52
|
+
</svg>
|
53
|
+
</div>
|
54
|
+
<% end %>
|
55
|
+
</div>
|
56
|
+
</div>
|
57
|
+
<% end %>
|
@@ -1,34 +1,53 @@
|
|
1
|
-
export default function
|
1
|
+
export default function viewportComponent(store) {
|
2
|
+
store = store || { width: "100%", height: "100%" };
|
3
|
+
store.resizing = false;
|
4
|
+
|
2
5
|
return {
|
3
|
-
|
4
|
-
|
5
|
-
},
|
6
|
+
store,
|
7
|
+
|
6
8
|
get maxWidth() {
|
7
|
-
return this.store.width === "100%" ? "100%" : `${
|
9
|
+
return this.store.width === "100%" ? "100%" : `${store.width}px`;
|
8
10
|
},
|
11
|
+
|
9
12
|
get maxHeight() {
|
10
|
-
return this.store.height === "100%" ? "100%" : `${
|
13
|
+
return this.store.height === "100%" ? "100%" : `${store.height}px`;
|
11
14
|
},
|
15
|
+
|
12
16
|
get parentWidth() {
|
13
|
-
return Math.round(this.$root.
|
17
|
+
return Math.round(this.$root.clientWidth);
|
14
18
|
},
|
19
|
+
|
15
20
|
get parentHeight() {
|
16
|
-
return Math.round(this.$root.
|
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 } =
|
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.$
|
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
|
-
|
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
|
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.$
|
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
|
-
|
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
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
landing = Lookbook.pages.find(&:landing) || Lookbook.pages.first
|
17
|
+
if landing.present?
|
18
|
+
redirect_to 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
|