lookbook 0.5.0.beta.2 → 0.5.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7838a877b435b1952e1eed9163cad4f21fa44b2578d9114430bfc1d262b15c23
4
- data.tar.gz: 4f8512d21d07f2b93164706569f0a28483f6b43e60446f19857411b4e66ac6ee
3
+ metadata.gz: 0bf7750f26cce0e0b6517443da61e75a2d954b50c4882637a21fdac8b7d9c520
4
+ data.tar.gz: bb81e6a5cbb6db75b62591d6c233c1f1831db92e04045eafd372ae60264cc9fc
5
5
  SHA512:
6
- metadata.gz: bf6f6045ce9ea8d5bca9932bfb3538e0e5c307b856cdbc0a209520be5112bed281f623cc879af59e51216f1d284aa2de08ed6ef1b483f794ea74c434a150bb13
7
- data.tar.gz: 4b2d545082ea784bc1122a51c947b0d5391cc829e895adfe224107591acdb68fabb23e8d21fe8be1f1d6acf405e774130dad1e1ba9676470a5bea9240f82cd34
6
+ metadata.gz: a569c06a48d6b30b8f328f25ad1eecfa474427d61dbf5a91395c780593329bb4424fdec52cb71d332dd229edc3d2031d9a9af84e864a4e906dcfcd7d387ea953
7
+ data.tar.gz: 1316810b7e32c5fda3ebd96b0d23566654f0a274fdaa730ae0ac50d5eaa03d0ed093ab412f504b1b0176cb37ecb26258d27f4aac081b6bce13bc754b6555c62d
data/README.md CHANGED
@@ -28,7 +28,7 @@ Lookbook uses [RDoc/Yard-style comment tags](#annotating-preview-files) to exten
28
28
  - Auto-updating UI when component or preview files are updated _(Rails v6.0+ only)_
29
29
  - Use comment tag annotations for granular customisation of the preview experience
30
30
  - Fully compatible with standard the ViewComponent preview system
31
- - [**Experimental**] In-browser live editable preview parameters (similar to Storybook Controls/Knobs)
31
+ - In-browser live-editable preview parameters (similar to basic Storybook Controls/Knobs)
32
32
 
33
33
  ## Lookbook demo
34
34
 
@@ -63,10 +63,16 @@ end
63
63
 
64
64
  The `at` property determines the root URL that the Lookbook UI will be served at.
65
65
 
66
- > If you would like to expose the Lookbook UI in production as well as in development, just remove the `if Rails.env.development?` condition from around the mount statement.
67
-
68
66
  Then you can start your app as normal and navigate to `http://localhost:3000/lookbook` (or whatever mount path you specified) to view your component previews in the Lookbook UI.
69
67
 
68
+ #### Mounting in Production
69
+
70
+ If you would like to expose the Lookbook UI in production as well as in development
71
+
72
+ 1. Remove the `if Rails.env.development?` condition from around the mount statement in `routes.rb`
73
+ 2. Add `config.view_component.show_previews = true` to `config/environments/production.rb`
74
+
75
+
70
76
  ## Usage
71
77
 
72
78
  You don't need to do anything special to see your ViewComponent previews and examples in Lookbook - just create them as normal and they'll automatically appear in the Lookbook UI. Preview templates, custom layouts and even bespoke [preview controllers](https://viewcomponent.org/guide/previews.html#configuring-preview-controller) should all work as you would expect.
@@ -96,7 +102,7 @@ class ButtonComponentPreview < ViewComponent::Preview
96
102
  # Button with icon
97
103
  # ----------------
98
104
  # This example uses dynamic preview parameters
99
- # which can be edited live in the Lookbook UI
105
+ # which can be edited live in the Lookbook UI
100
106
  #
101
107
  # @param text
102
108
  # @param icon select [heart, cog, alert]
@@ -161,7 +167,7 @@ The following Lookbook-specific tags are available for use:
161
167
  * [`@display`](#display-tag)
162
168
  * [`@!group ... @!endgroup`](#group-tag)
163
169
  * [`@hidden`](#hidden-tag)
164
- * [`@param`](#param-tag) [⚠️ **experimental!** - requires [feature opt-in](#experimental-features) ⚠️]
170
+ * [`@param`](#param-tag)
165
171
 
166
172
  <h3 id="label-tag">🏷 @label</h3>
167
173
 
@@ -311,9 +317,7 @@ class FooComponentPreview < ViewComponent::Preview
311
317
  end
312
318
  ```
313
319
 
314
- <h3 id="param-tag"> 🚧 @param (experimental)</h3>
315
-
316
- > ⚠️ This feature is currently flagged as an **experimental** feature which requires [feature opt-in](#experimental-features) to use. Its API and implementation may change in the future.
320
+ <h3 id="param-tag">@param</h3>
317
321
 
318
322
  The `@param` tag provides the ability to specify **editable preview parameters** which can be changed in the Lookbook UI in order to customise the rendered output on the fly, much like the [Controls (knobs) addon](https://storybook.js.org/addons/@storybook/addon-controls) for Storybook.
319
323
 
@@ -326,7 +330,7 @@ The `@param` tag takes the following format:
326
330
  ```
327
331
 
328
332
  - `<name>` - name of the dynamic preview param
329
- - `<input_type>` - input field type to generate in the UI
333
+ - `<input_type>` - input field type to generate in the UI
330
334
  - `<opts?>` - YAML-encoded field options, used for some field types
331
335
 
332
336
  #### Input types
@@ -357,7 +361,7 @@ The following **input field types** are available for use:
357
361
  @param <name> select <options>
358
362
  ```
359
363
 
360
- `<options>` should be a [YAML array](https://yaml.org/YAML_for_ruby.html#simple_inline_array) of options which must be formatted in the same style as the input for Rails' [`options_for_select`](https://apidock.com/rails/v6.0.0/ActionView/Helpers/FormOptionsHelper/options_for_select) helper:
364
+ `<options>` should be a [YAML array](https://yaml.org/YAML_for_ruby.html#simple_inline_array) of options which must be formatted in the same style as the input for Rails' [`options_for_select`](https://apidock.com/rails/v6.0.0/ActionView/Helpers/FormOptionsHelper/options_for_select) helper:
361
365
 
362
366
  ```ruby
363
367
  # Basic options:
@@ -437,7 +441,7 @@ The following structured types are also available but should be considered **exp
437
441
 
438
442
  ```ruby
439
443
  class ButtonComponentPreview < ViewComponent::Preview
440
-
444
+
441
445
  # The params defined below will be editable in the UI:
442
446
  #
443
447
  # @param content text
@@ -496,6 +500,17 @@ If you wish to add additional paths to listen for changes in, you can use the `l
496
500
  config.lookbook.listen_paths << Rails.root.join('app/other/directory')
497
501
  ```
498
502
 
503
+ ### Custom favicon
504
+
505
+ If you want to change the favicon used by the Lookbook UI, you can provide a path to your own (or a data-uri string) using the `ui_favicon` option:
506
+
507
+ ```ruby
508
+ config.lookbook.ui_favicon = "/path/to/my/favicon.png"
509
+ ```
510
+
511
+ > To disable the favicon entirely, set the value to `false`.
512
+
513
+
499
514
  <h3 id="experimental-features">Experimental features opt-in</h3>
500
515
 
501
516
  Some features may occasionally be released behind a 'experimental' feature flag while they are being tested and refined, to allow people to try them out and provide feedback.
@@ -510,10 +525,6 @@ To opt into individual experimental features, include the name of the feature in
510
525
  config.lookbook.experimental_features = ["feature_name"]
511
526
  ```
512
527
 
513
- The current experimental features that can be opted into are:
514
-
515
- - `params`: Live-editable, dynamic preview parameters ([read more](#param-tag)). Include `"params"` in the `experimental_features` config option to opt in.
516
-
517
528
  #### Opting into all experimental features (not recommended!)
518
529
 
519
530
  If you want to live life on the bleeding-edge you can opt-in to all current **and future** experimental features (usual caveats apply):
@@ -529,9 +540,10 @@ Lookbook provides a few keyboard shortcuts to help you quickly move around the U
529
540
 
530
541
  - `f` - move focus to the nav filter box
531
542
  - `Esc` [when focus is in nav filter box] - Clear contents if text is present, or return focus to the UI if the box is already empty
532
- - `s` - Switch to Source tab in the inspector
533
- - `o` - Switch to Output tab in the inspector
534
- - `n` - Switch to Notes tab in the inspector
543
+ - `s` - Switch to Source tab in the drawer
544
+ - `n` - Switch to Notes tab in the drawer
545
+ - `v` - Switch to the rendered preview
546
+ - `o` - Switch to the code preview
535
547
  - `r` - Refresh the preview (useful if using something like Faker to generate randomised data for the preview)
536
548
  - `w` - Open the standalone rendered preview in a new window
537
549
 
@@ -1,7 +1,9 @@
1
- export default function copy(id) {
1
+ export default function copy() {
2
2
  return {
3
3
  get content() {
4
- const target = document.getElementById(id);
4
+ const target = document.getElementById(
5
+ this.$root.getAttribute("data-target")
6
+ );
5
7
  return (target ? target.innerHTML : "").trim();
6
8
  },
7
9
  done: false,
@@ -32,15 +32,23 @@ export default function inspector() {
32
32
  get drawerHidden() {
33
33
  return this.$store.inspector.drawer.hidden;
34
34
  },
35
- isActivePanel(panel) {
36
- return this.$store.inspector.drawer.active == panel;
35
+ get maxDrawerHeight() {
36
+ return Math.round(this.height * 0.7);
37
37
  },
38
- switchPanel(panel) {
39
- this.$store.inspector.drawer.active = panel;
38
+ get maxDrawerWidth() {
39
+ return Math.round(this.width * 0.7);
40
40
  },
41
- toggleView() {
42
- this.$store.inspector.preview.view =
43
- this.view === "html" ? "preview" : "html";
41
+ isActiveDrawerPanel(panel) {
42
+ return this.$store.inspector.drawer.panel === panel;
43
+ },
44
+ switchDrawerPanel(panel) {
45
+ this.$store.inspector.drawer.panel = panel;
46
+ },
47
+ isActivePreviewPanel(panel) {
48
+ return this.$store.inspector.preview.panel === panel;
49
+ },
50
+ switchPreviewPanel(panel) {
51
+ this.$store.inspector.preview.panel = panel;
44
52
  },
45
53
  toggleOrientation() {
46
54
  this.$store.inspector.drawer.orientation =
@@ -9,12 +9,13 @@ export default {
9
9
  drawer: {
10
10
  orientation: "horizontal",
11
11
  defaultPanel: "source",
12
- defaultHeight: 200,
12
+ defaultHeight: 300,
13
13
  defaultWidth: 500,
14
14
  minWidth: 350,
15
+ minHeight: 200,
15
16
  },
16
17
  preview: {
17
- view: "preview",
18
+ defaultPanel: "preview",
18
19
  },
19
20
  },
20
21
  };
@@ -6,16 +6,17 @@ export default function createInspectorStore(Alpine) {
6
6
  drawer: {
7
7
  hidden: Alpine.$persist(false).as("drawer-hidden"),
8
8
  orientation: Alpine.$persist(drawer.orientation).as("drawer-orientation"),
9
- active: Alpine.$persist(drawer.defaultPanel).as("drawer-active"),
9
+ panel: Alpine.$persist(drawer.defaultPanel).as("drawer-panel"),
10
10
  height: Alpine.$persist(drawer.defaultHeight).as("drawer-height"),
11
11
  width: Alpine.$persist(drawer.defaultWidth).as("drawer-width"),
12
12
  minWidth: drawer.minWidth,
13
+ minHeight: drawer.minHeight,
13
14
  visibleTabCount: Infinity,
14
15
  },
15
16
  preview: {
16
17
  width: Alpine.$persist("100%").as("preview-width"),
17
18
  height: Alpine.$persist("100%").as("preview-height"),
18
- view: Alpine.$persist(preview.view).as("preview-view"),
19
+ panel: Alpine.$persist(preview.defaultPanel).as("preview-panel"),
19
20
  lastWidth: null,
20
21
  lastHeight: null,
21
22
  resizing: false,
@@ -27,10 +27,8 @@ module Lookbook
27
27
  begin
28
28
  set_params
29
29
  @examples = examples_data
30
- @preview_srcdoc = if Lookbook.config.preview_srcdoc
31
- render_examples(examples_data).gsub("\"", "&quot;")
32
- end
33
- @panels = panels.filter { |name, panel| panel[:show] }
30
+ @drawer_panels = drawer_panels.filter { |name, panel| panel[:show] }
31
+ @preview_panels = preview_panels.filter { |name, panel| panel[:show] }
34
32
  rescue *EXCEPTIONS
35
33
  render "error"
36
34
  end
@@ -119,23 +117,37 @@ module Lookbook
119
117
  @nav
120
118
  end
121
119
 
122
- def panels
120
+ def preview_panels
123
121
  {
124
- source: {
125
- label: "Source",
126
- template: "lookbook/panels/source",
127
- hotkey: "s",
122
+ preview: {
123
+ label: "Preview",
124
+ template: "lookbook/panels/preview",
125
+ srcdoc: Lookbook.config.preview_srcdoc ? render_examples(examples_data).gsub("\"", "&quot;") : nil,
126
+ hotkey: "v",
128
127
  show: true,
129
128
  disabled: false,
130
- copy: true
129
+ copy: false
131
130
  },
132
131
  output: {
133
- label: "Output",
132
+ label: "HTML",
134
133
  template: "lookbook/panels/output",
135
134
  hotkey: "o",
136
135
  show: true,
137
136
  disabled: false,
138
137
  copy: true
138
+ }
139
+ }
140
+ end
141
+
142
+ def drawer_panels
143
+ {
144
+ source: {
145
+ label: "Source",
146
+ template: "lookbook/panels/source",
147
+ hotkey: "s",
148
+ show: true,
149
+ disabled: false,
150
+ copy: true
139
151
  },
140
152
  notes: {
141
153
  label: "Notes",
@@ -6,7 +6,9 @@
6
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/css/app.css?v=<%= Lookbook::VERSION %>" rel="stylesheet">
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>">
9
+ <% if config.ui_favicon != false %>
10
+ <link rel="icon" href="<%= config.ui_favicon || "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>" %>">
11
+ <% end %>
10
12
 
11
13
  <% if config.auto_refresh %>
12
14
  <script>
@@ -1,10 +1,10 @@
1
1
  <div id="drawer" class="bg-white w-full h-full flex flex-col min-w-0">
2
- <div class="px-4 border-b border-gray-200 select-none flex-none" x-show="!drawerHidden">
3
- <div class="-mb-px flex cursor-auto relative">
2
+ <div class="pl-4 border-b border-gray-300 select-none flex-none" x-show="!drawerHidden">
3
+ <div class="flex cursor-auto relative">
4
4
  <div
5
5
  id="inspector-tabs-<%= example.id %>"
6
6
  x-data="tabs"
7
- class="min-w-0"
7
+ class="min-w-0 -mb-px"
8
8
  x-effect="$store.inspector.drawer.visibleTabCount = visibleTabCount"
9
9
  >
10
10
  <nav x-ref="tabs" class="flex h-10 space-x-8 flex-grow pr-8">
@@ -14,11 +14,11 @@
14
14
  href="#inspector-panel-<%= key %>"
15
15
  class="whitespace-nowrap pt-2.5 pb-1.5 px-1 border-b-2 cursor-pointer <%= "!text-gray-300" if props[:disabled] %>"
16
16
  :class="{
17
- 'border-indigo-400': isActivePanel('<%= key %>'),
18
- 'border-transparent text-gray-500 hover:text-gray-700': !isActivePanel('<%= key %>'),
17
+ 'border-indigo-400': isActiveDrawerPanel('<%= key %>'),
18
+ 'border-transparent text-gray-500 hover:text-gray-700': !isActiveDrawerPanel('<%= key %>'),
19
19
  'invisible': (<%= i %> > visibleTabCount)
20
20
  }"
21
- @click.stop.prevent="switchPanel('<%= key %>')"
21
+ @click.stop.prevent="switchDrawerPanel('<%= key %>')"
22
22
  <% if props[:hotkey] %>data-hotkey="<%= props[:hotkey] %>"<% end %>
23
23
  >
24
24
  <%== props[:label] %>
@@ -41,14 +41,13 @@
41
41
  <template id="inspector-dropdown-tab-<%= key %>-<%= example.id %>" x-if="<%= i %> > $store.inspector.drawer.visibleTabCount">
42
42
  <div :class="{'border-t border-gray-300': (<%= i %> > $store.inspector.drawer.visibleTabCount + 1)}">
43
43
  <a
44
-
45
44
  href="#inspector-panel-<%= key %>"
46
45
  class="block whitespace-nowrap py-2 px-4 border-l-2 cursor-pointer <%= "!text-gray-300" if props[:disabled] %>"
47
46
  :class="{
48
- 'border-indigo-400': $store.inspector.drawer.active === '<%= key %>',
49
- 'border-transparent text-gray-500 hover:text-gray-700': $store.inspector.drawer.active !== '<%= key %>',
47
+ 'border-indigo-400': $store.inspector.drawer.panel === '<%= key %>',
48
+ 'border-transparent text-gray-500 hover:text-gray-700': $store.inspector.drawer.panel !== '<%= key %>',
50
49
  }"
51
- @click.stop.prevent=" hideDropdown(); switchPanel('<%= key %>')"
50
+ @click.stop.prevent=" hideDropdown(); switchDrawerPanel('<%= key %>')"
52
51
  >
53
52
  <%== props[:label] %>
54
53
  </a>
@@ -58,31 +57,35 @@
58
57
  </div>
59
58
  </nav>
60
59
  </div>
61
- <div class="flex items-center ml-auto pl-8 space-x-3">
62
- <% panels.each do |key, props| %>
63
- <div
64
- ref="<%= "inspector-panel-#{example.id}-#{key}-copy" %>"
65
- class="flex items-center"
66
- :class="{'pointer-events-none opacity-30': <%= !props[:copy].present? %>}"
67
- x-show="isActivePanel('<%= key %>')"
68
- >
69
- <button
70
- class="text-gray-400 transition"
71
- x-data="copy('<%= "inspector-panel-#{example.id}-#{key}-clipboard" %>')"
72
- x-tooltip.theme.lookbook="done ? 'copied!' : 'copy to clipboard'"
73
- @click="save"
74
- :class="{
75
- '!text-green-600 hover:text-green-600': done,
76
- 'hover:text-indigo-500': !done}"
60
+ <div class="flex items-center bg-white border-l border-gray-200 ml-auto space-x-3 px-3">
61
+ <div>
62
+ <% panels.each do |key, props| %>
63
+ <div
64
+ ref="<%= "inspector-panel-#{example.id}-#{key}-copy" %>"
65
+ class="flex items-center"
66
+ :class="{'pointer-events-none opacity-30': <%= !props[:copy].present? %>}"
67
+ x-show="isActiveDrawerPanel('<%= key %>')"
77
68
  x-cloak
78
- <% unless props[:copy].present? %>disabled<% end %>
79
69
  >
80
- <%= icon "${done ? 'check' : 'clipboard'}", size: 4 %>
81
- </button>
82
- </div>
83
- <% end %>
70
+ <button
71
+ data-target="<%= "inspector-panel-#{example.id}-#{key}-clipboard" %>"
72
+ class="text-gray-400 transition"
73
+ x-data="copy"
74
+ x-tooltip.theme.lookbook="done ? 'copied!' : 'copy to clipboard'"
75
+ @click="save"
76
+ :class="{
77
+ '!text-green-600 hover:text-green-600': done,
78
+ 'hover:text-indigo-500': !done}"
79
+ x-cloak
80
+ <% unless props[:copy].present? %>disabled<% end %>
81
+ >
82
+ <%= icon "${done ? 'check' : 'clipboard'}", size: 4 %>
83
+ </button>
84
+ </div>
85
+ <% end %>
86
+ </div>
84
87
  <button
85
- x-tooltip.theme.lookbook="`switch orientation`"
88
+ x-tooltip.theme.lookbook="`${horizontal ? 'pin drawer on right' : 'pin drawer on bottom'}`"
86
89
  @click="toggleOrientation"
87
90
  :class="{'pointer-events-none opacity-30': !canBeVertical}"
88
91
  >
@@ -91,10 +94,10 @@
91
94
  class: "scale-[-1] text-gray-400 hover:text-indigo-800" %>
92
95
  </button>
93
96
  <button
94
- x-tooltip.theme.lookbook="`close drawer`"
97
+ x-tooltip.theme.lookbook="`hide drawer`"
95
98
  @click="toggleDrawer"
96
99
  >
97
- <%= icon "x",
100
+ <%= icon "x-circle",
98
101
  size: 4,
99
102
  class: "text-gray-400 hover:text-indigo-800" %>
100
103
  </button>
@@ -105,7 +108,7 @@
105
108
  <% panels.each do |key, props| %>
106
109
  <div
107
110
  class="h-full w-full absolute inset-0"
108
- x-show="$store.inspector.drawer.active === '<%= key %>'"
111
+ x-show="isActiveDrawerPanel('<%= key %>')"
109
112
  x-cloak
110
113
  >
111
114
  <div id="inspector-panel-<%= example.id %>-<%= key %>" class="h-full">
@@ -1,5 +1,5 @@
1
- <header class="py-2 px-4 w-full flex-none bg-white border-b border-gray-300 flex items-center h-10 select-none min-w-0">
2
- <button class="flex-none mr-3" x-show="!$store.layout.desktop" @click="$store.sidebar.toggle">
1
+ <header class="pl-4 w-full flex-none bg-white border-b border-gray-300 flex items-center h-10 select-none min-w-0">
2
+ <button class="flex-none mr-6" x-show="!$store.layout.desktop" @click="$store.sidebar.toggle">
3
3
  <svg class="feather w-5 h-5 hover:text-indigo-500 transition">
4
4
  <use xlink:href="/lookbook-assets/feather-sprite.svg#menu" />
5
5
  </svg>
@@ -6,6 +6,8 @@
6
6
  <% end %>
7
7
  <% end %>
8
8
  <% else %>
9
- <% example = examples.first %>
10
- <%= component "nav_item", item: example, depth: example.hierarchy_depth, label: node.label, display: :node %>
9
+ <% if examples.any? %>
10
+ <% example = examples.first %>
11
+ <%= component "nav_item", item: example, depth: example.hierarchy_depth, label: node.label, display: :node %>
12
+ <% end %>
11
13
  <% end %>
@@ -1,52 +1,71 @@
1
- <div id="preview" class="h-auto min-h-0 w-full checked-bg">
2
- <div
3
- class="mx-auto h-full w-full"
4
- :style="`max-width: ${maxWidth}; max-height: ${maxHeight};`"
5
- x-data="previewWindow"
6
- >
7
- <div class="grid bg-white relative grid-cols-[1fr_17px] grid-rows-[1fr_17px] -inset-px" style="width: calc(100% + 2px); height: calc(100% + 2px)">
8
-
9
- <iframe seamless
10
- class="h-full w-full border border-gray-300"
11
- :class="{ 'pointer-events-none': $store.layout.reflowing }"
12
- src="<%= lookbook.preview_path(request.query_parameters) %>"
13
- <% if config.preview_srcdoc %>srcdoc="<%== srcdoc %>"<% end %>
14
- frameborder="0"
15
- x-data="sizes"
16
- x-effect="preview.width = width; preview.height = height;"
17
- ></iframe>
18
-
1
+ <div id="preview" class="grid grid-rows-[40px_1fr]">
2
+ <%= component "header" do %>
3
+ <div
4
+ id="preview-tabs-<%= @example.id %>" class="min-w-0 -mb-px">
5
+ <nav class="flex h-10 space-x-8 flex-grow pr-8">
6
+ <% panels.each do |key, props| %>
7
+ <a
8
+ id="preview-tab-<%= key %>-<%= @example.id %>"
9
+ href="#preview-panel-<%= key %>"
10
+ class="whitespace-nowrap pt-2.5 pb-1.5 px-1 border-b-2 cursor-pointer"
11
+ :class="{
12
+ 'border-indigo-400': isActivePreviewPanel('<%= key %>'),
13
+ 'border-transparent text-gray-500 hover:text-gray-700': !isActivePreviewPanel('<%= key %>'),
14
+ }"
15
+ @click.stop.prevent="switchPreviewPanel('<%= key %>')"
16
+ <% if props[:hotkey] %>data-hotkey="<%= props[:hotkey] %>"<% end %>
17
+ >
18
+ <%== props[:label] %>
19
+ </a>
20
+ <% end %>
21
+ </nav>
22
+ </div>
23
+ <div class="flex items-stretch h-full ml-auto space-x-3">
19
24
  <div
20
- class="resize-handle border-r border-t cursor-[col-resize]"
21
- @pointerdown="onResizeWidthStart"
22
- @dblclick="toggleFullWidth"
23
- >
24
- <svg class="h-4 w-4 pointer-events-none" fill="currentColor" viewBox="0 0 24 24">
25
- <path d="M8 5h2v14H8zM14 5h2v14h-2z"></path>
26
- </svg>
25
+ class="flex items-center text-xs font-monospace text-gray-700 space-x-1 opacity-50 hover:opacity-100 transition"
26
+ :class="{'opacity-100': $store.inspector.preview.resizing}"
27
+ x-show="isActivePreviewPanel('preview')">
28
+ <span x-text="`${preview.width}px`"></span>
29
+ <span class="text-gray-500">x</span>
30
+ <span x-text="`${preview.height}px`"></span>
27
31
  </div>
28
-
29
- <div
30
- class="resize-handle border-b border-l cursor-[col-resize]"
31
- @pointerdown="onResizeHeightStart"
32
- @dblclick="toggleFullHeight"
33
- >
34
- <svg class="h-4 w-4 pointer-events-none rotate-90" fill="currentColor" viewBox="0 0 24 24" >
35
- <path d="M8 5h2v14H8zM14 5h2v14h-2z"></path>
36
- </svg>
32
+ <div class="flex items-center bg-white border-l border-gray-200 space-x-3 text-gray-400 divide-x divide-gray-300 px-3">
33
+ <div class="flex items-center space-x-3">
34
+ <button
35
+ x-tooltip.theme.lookbook="`Refresh preview`"
36
+ @click.prevent.stop="refresh"
37
+ data-hotkey="r"
38
+ >
39
+ <%= icon "refresh-cw", size: 4, class: "hover:text-indigo-800" %>
40
+ </button>
41
+ <a
42
+ href="<%= preview_path %>"
43
+ target="_blank"
44
+ x-tooltip.theme.lookbook="`Open in new window`"
45
+ data-hotkey="w"
46
+ >
47
+ <%= icon "external-link", size: 4, class: "hover:text-indigo-800" %>
48
+ </a>
49
+ <button
50
+ x-tooltip.theme.lookbook="`${drawerHidden ? 'show' : 'hide'} drawer`"
51
+ @click="toggleDrawer"
52
+ x-show="drawerHidden"
53
+ data-hotkey="i"
54
+ >
55
+ <%= icon "${horizontal ? 'credit-card' : 'sidebar'}", size: 4, class: "hover:text-indigo-800 scale-[-1]" %>
56
+ </button>
57
+ </div>
58
+
37
59
  </div>
38
-
39
- <div
40
- class="resize-handle border-r border-b cursor-[nwse-resize]"
41
- @pointerdown="onResizeStart"
42
- @dblclick="toggleFullSize"
43
- >
44
- <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" >
45
- <path d="M8 5h2v14H8zM14 5h2v14h-2z"></path>
46
- </svg>
47
- </div>
48
-
49
60
  </div>
50
-
61
+ <% end %>
62
+ <div class="bg-white relative flex-grow">
63
+ <% panels.each do |key, props| %>
64
+ <div class="h-full w-full absolute inset-0" x-show="isActivePreviewPanel('<%= key %>')" x-cloak>
65
+ <div id="preview-panel-<%= example.id %>-<%= key %>" class="h-full">
66
+ <%= render props[:template], key: key, examples: examples, **props %>
67
+ </div>
68
+ </div>
69
+ <% end %>
51
70
  </div>
52
71
  </div>
@@ -1,4 +1,4 @@
1
- <div class="p-4 h-full bg-gray-50 overflow-auto" data-morph-strategy="replace">
1
+ <div class="p-4 h-full bg-white overflow-auto" data-morph-strategy="replace">
2
2
  <%= component "code", wrap: "vertical" do -%>
3
3
  <% if examples.many? %>
4
4
  <% examples.each do |example| -%>
@@ -0,0 +1,52 @@
1
+ <div id="preview-window" class="h-full min-h-0 w-full checked-bg">
2
+ <div
3
+ class="mx-auto h-full w-full"
4
+ :style="`max-width: ${maxWidth}; max-height: ${maxHeight};`"
5
+ x-data="previewWindow"
6
+ >
7
+ <div class="grid bg-white relative grid-cols-[1fr_17px] grid-rows-[1fr_17px] -inset-px" style="width: calc(100% + 2px); height: calc(100% + 2px)">
8
+
9
+ <iframe seamless
10
+ class="h-full w-full border border-gray-300"
11
+ :class="{ 'pointer-events-none': $store.layout.reflowing }"
12
+ src="<%= lookbook.preview_path(request.query_parameters) %>"
13
+ <% if config.preview_srcdoc %>srcdoc="<%== srcdoc %>"<% end %>
14
+ frameborder="0"
15
+ x-data="sizes"
16
+ x-effect="preview.width = width; preview.height = height;"
17
+ ></iframe>
18
+
19
+ <div
20
+ class="resize-handle border-r border-t cursor-[col-resize]"
21
+ @pointerdown="onResizeWidthStart"
22
+ @dblclick="toggleFullWidth"
23
+ >
24
+ <svg class="h-4 w-4 pointer-events-none" fill="currentColor" viewBox="0 0 24 24">
25
+ <path d="M8 5h2v14H8zM14 5h2v14h-2z"></path>
26
+ </svg>
27
+ </div>
28
+
29
+ <div
30
+ class="resize-handle border-b border-l cursor-[col-resize]"
31
+ @pointerdown="onResizeHeightStart"
32
+ @dblclick="toggleFullHeight"
33
+ >
34
+ <svg class="h-4 w-4 pointer-events-none rotate-90" fill="currentColor" viewBox="0 0 24 24" >
35
+ <path d="M8 5h2v14H8zM14 5h2v14h-2z"></path>
36
+ </svg>
37
+ </div>
38
+
39
+ <div
40
+ class="resize-handle border-r border-b cursor-[nwse-resize]"
41
+ @pointerdown="onResizeStart"
42
+ @dblclick="toggleFullSize"
43
+ >
44
+ <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" >
45
+ <path d="M8 5h2v14H8zM14 5h2v14h-2z"></path>
46
+ </svg>
47
+ </div>
48
+
49
+ </div>
50
+
51
+ </div>
52
+ </div>