lookbook 0.2.0 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 30e9c49fca5a575af92323c59fd35018ddcd92798939a87286915060f2f93401
4
- data.tar.gz: d90162b2ea72fcc7fc5fe38478b3ced236649421fdc08f0554acead3f5a41213
3
+ metadata.gz: f643b8df09b68fb0e91d8dd522b186d8bcb8e58fa7883841a112a82a4ec81f8d
4
+ data.tar.gz: f97771056316f5e652a9850c2ac6495fb1c934135fada28af6557c7cd602e90a
5
5
  SHA512:
6
- metadata.gz: 7dcac40f9c516acbb996dae1ab5e26026fe7a98cd607b066572f22a958101d5cdad4b447a9d50348f694f198b5dba3f10682d8236dfc9eb0ea0ff4c415c177e0
7
- data.tar.gz: 372f92f28d094d502dafe5acdc7ce243034542432a33ac5fa1075f62cb1aebfeed3f02913cabc08690b467b6bd16b46d380cdfb2c1dfd3c16bd814d03773c18e
6
+ metadata.gz: a4dc35a94c6173b687c903f098b5f0c05d8025c9171efa05ed0eb24993685c8495b7497f30df0f7d4550b2609f4dcbd0263624f8b1c9a7ed740bfd66dc48d288
7
+ data.tar.gz: 1f63d7ae4b684c46f89e0bfe683dbc83e0bf5daca16ced87c6061f0e7ea8c8da0c9131de71b9fd6dad7990211108fcad25d35ff5669b31b95eac78c6db70d48b
data/README.md CHANGED
@@ -1,31 +1,35 @@
1
1
  <div align="center">
2
- <h2>Lookbook</h2>
2
+ <h1>👀 Lookbook 👀</h1>
3
3
 
4
- 👀 A native development UI for [ViewComponent](http://viewcomponent.org/) 👀
4
+ <p>A native development UI for <a href="http://viewcomponent.org/">ViewComponent</a></p>
5
5
 
6
+ <div>
7
+ <a href="https://rubygems.org/gems/lookbook"><img src="https://badge.fury.io/rb/lookbook.svg" alt="Gem version">
8
+ <a href="https://github.com/testdouble/standard"><img src="https://img.shields.io/badge/code_style-standard-brightgreen.svg" alt="Ruby Style Guide">
9
+ <a href="https://github.com/prettier/prettier"><img src="https://img.shields.io/badge/code_style-prettier-ff69b4.svg" alt="Code style: Prettier">
10
+ </div>
6
11
  </div>
7
12
 
8
13
  ---
9
14
 
10
- Lookbook gives [ViewComponent](http://viewcomponent.org/)-based projects a _ready-to-go_ development UI for navigating, inspecting and interacting with component previews.
15
+ **Lookbook gives [ViewComponent](http://viewcomponent.org/)-based projects a _ready-to-go_ development UI for navigating, inspecting and interacting with component previews.**
11
16
 
12
- It uses (and extends) the native [ViewComponent preview functionality](https://viewcomponent.org/guide/previews.html) and is intended to integrate seamlessly with existing component libraries.
17
+ It uses (and extends) the native [ViewComponent preview functionality](https://viewcomponent.org/guide/previews.html), so you don't need to learn a new DSL or create any extra files to get up and running.
13
18
 
14
19
  Lookbook uses [RDoc/Yard-style comment tags](https://rubydoc.info/gems/yard/file/docs/Tags.md) to extend the capabilities of ViewComponent's previews whilst maintaining compatability with the standard preview class format, so you can add or remove Lookbook at any time without having to rework your code.
15
20
 
16
- > ⚠️ **PLEASE NOTE!** Lookbook is very much a **work in progress** at the moment. There may be breaking changes on point-releases before a 1.0 version is ready. ⚠️
17
-
18
21
  ![Lookbook UI](.github/assets/lookbook_screenshot.png)
19
22
 
20
23
  ### Features
21
24
 
22
- - 👀 Navigate your component previews with ease
23
- - 🔍 Search/filter previews
24
- - 🖥 Resizable, responsive preview window
25
- - 🔦 Highlighted preview source code and HTML output (with one-click copy to clipboard)
26
- - 📝 Add notes via comments in the preview file (markdown supported)
27
- - 🚀 Live UI, auto-updates when component or previews files are updated
28
- - 🙈 Supports 'hidden' previews and examples
25
+ - Tree-style navigation menu
26
+ - Live nav search/filter
27
+ - Resizable preview window for responsive testing
28
+ - Highlighted preview source code and HTML output
29
+ - Add notes via comments in the preview file (markdown supported)
30
+ - Auto-updating UI when component or preview files are updated
31
+ - Supports 'hidden' previews and examples
32
+ - Works with standard the ViewComponent preview system
29
33
 
30
34
  ## Lookbook demo
31
35
 
@@ -35,27 +39,23 @@ The [demo app repo](https://github.com/allmarkedup/lookbook-demo) contains instr
35
39
 
36
40
  ## Installing
37
41
 
38
- Lookbook is current a work in progress and **has not been published as a Gem** yet.
39
-
40
- If you wish to play with it in it's current state you can include it directly from Github using the instructions below.
42
+ > ⚠️ **Please note:** Lookbook is still in the early stages of development and has not yet been well tested across a wide range of Rails/ViewComponent versions and setups. If you run into any problems please [open an issue](issues) with as much detail as possible. Thanks! ⚠️
41
43
 
42
44
  ### 1. Add as a dependency
43
45
 
44
- In your `Gemfile` add:
46
+ Add Lookbook to your `Gemfile` somewhere **after** the ViewComponent gem. For example:
45
47
 
46
48
  ```ruby
47
- gem "lookbook", '>= 0.1', git: "https://github.com/allmarkedup/lookbook", branch: "main"
49
+ gem "view_component", require: "view_component/engine"
50
+ gem "lookbook"
48
51
  ```
49
52
 
50
- This line should be placed <strong>below</strong> wherever you have specified the `view_component` gem.
51
-
52
53
  ### 2. Mount the Lookbook engine
53
54
 
54
55
  You then need to mount the Lookbook engine (at a path of your choosing) in your `routes.rb` file:
55
56
 
56
57
  ```ruby
57
58
  Rails.application.routes.draw do
58
- # any other routes...
59
59
  if Rails.env.development?
60
60
  mount Lookbook::Engine, at: "/lookbook"
61
61
  end
@@ -64,145 +64,116 @@ end
64
64
 
65
65
  The `at` property determines the root URL that the Lookbook UI will be served at.
66
66
 
67
- > If you would like to expose the Lookbook UI in production as well as in development, just remove the `if Rails.env.development?` wrapper from the mount statement.
67
+ > 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.
68
68
 
69
69
  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.
70
70
 
71
71
  ## Usage
72
72
 
73
- You don't need to do anything special to create ViewComponent previews for Lookbook.
73
+ 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.
74
74
 
75
- Lookbook will use the [ViewComponent configuration options](https://viewcomponent.org/api.html#configuration) for your project to find and render your components so you don't need to configure anything separately (unless you want to tweak the behaviour or look of Lookbook itself).
75
+ > If you are new to ViewComponent development, checkout the ViewComponent [documentation](https://viewcomponent.org/guide/) on how to get started developing your components and [creating previews](https://viewcomponent.org/guide/previews.html).
76
76
 
77
- > If you are new to ViewComponent development, checkout the [ViewComponent docs](https://viewcomponent.org/guide/) on how to get started developing your components and creating previews.
77
+ ### Annotating preview files
78
78
 
79
- Lookbook uses the exact same [preview files](https://viewcomponent.org/guide/previews.html) as 'regular' ViewComponent previews, so using preview templates, custom layouts and even bespoke [preview controllers](https://viewcomponent.org/guide/previews.html#configuring-preview-controller) all works as you would expect.
79
+ Lookbook parses [Yard-style comment tags](https://rubydoc.info/gems/yard/file/docs/Tags.md) in your preview classes to customise and extend the standard ViewComponent preview experience:
80
80
 
81
- ### Comment tags
81
+ ```ruby
82
+ # @label Basic Button
83
+ class ButtonComponentPreview < ViewComponent::Preview
82
84
 
83
- Lookbook uses [Yard-style tags](https://rubydoc.info/gems/yard/file/docs/Tags.md) in class and method comments to extract additional information about previews and examples.
85
+ # Primary button
86
+ # ---------------
87
+ # This is the button style you should use for most things.
88
+ #
89
+ # @label Primary
90
+ def default
91
+ render ButtonComponent.new do
92
+ "Click me"
93
+ end
94
+ end
84
95
 
85
- Tags are just strings identified by their `@` prefix - for example `@hidden`. Tags are always placed in a comment above the relevant preview class or example method. The comments can still contain any other text, and multiple tags can be included in any one comment. For example:
96
+ # Secondary button
97
+ # ---------------
98
+ # This should be used for less important actions.
99
+ def secondary
100
+ render ButtonComponent.new(style: :secondary) do
101
+ "Click me"
102
+ end
103
+ end
86
104
 
87
- ```ruby
88
- # This is a class-level comment.
89
- # @hidden
90
- class MyClass
91
- # This is a method-level comment.
105
+ # Unicorn button
106
+ # ---------------
107
+ # This button style is still a **work in progress**.
108
+ #
92
109
  # @hidden
93
- def my_method
110
+ def secondary
111
+ render ButtonComponent.new do
112
+ "Click me"
113
+ end
94
114
  end
95
115
  end
96
116
  ```
97
117
 
98
- Some tags can also require additional arguments. Further informatin on the tags Lookbook uses are detailed in the docs below.
118
+ **Tags** are just strings identified by their `@` prefix - for example `@hidden`. Tags are always placed in a comment above the relevant preview class or example method.
99
119
 
100
- ### 📝 Adding notes to previews
120
+ The following Lookbook-specific tags are available for use:
101
121
 
102
- Lookbook lets you add notes to your preview examples which are then displayed in the inspector panel. They look something like this:
122
+ #### `@label <text>`
103
123
 
104
- <img src=".github/assets/preview_example_notes.png" width="400">
124
+ Used to replace the auto-generated navigation label for the item with `<text>`.
105
125
 
106
- Notes are generated from comments above example methods in your preview files. Below is an example of two preview examples that both have notes:
126
+ > Available for preview classes & example methods.
107
127
 
108
128
  ```ruby
109
- class ButtonComponentPreview < ViewComponent::Preview
129
+ # @label Preview Label
130
+ class FooComponentPreview < ViewComponent::Preview
110
131
 
111
- # Add notes as comments above the example methods.
112
- # Multi-line is just fine and **markdown** is supported too!
113
- #
114
- # It's a good place to put usage and implementation instructions
115
- # for people browsing the component previews in the UI.
132
+ # @label Example Label
116
133
  def default
117
- render ButtonComponent.new(text: "Click me")
118
- end
119
-
120
- # Each preview example has it's own notes, extracted from the method comments.
121
- def danger
122
- render ButtonComponent.new(text: "Don't do it!", theme: :danger)
123
134
  end
124
135
  end
125
136
  ```
126
137
 
127
- ### 👀 Navigation customisation
138
+ #### `@hidden`
128
139
 
129
- Lookbook generates a nested navigation menu based on the file structure of your component preview directories. This can be customised in a number of ways.
140
+ Used to temporarily exclude an item from the Lookbook navigation. The item will still be accessible via it's URL.
130
141
 
131
- #### Preview and example labels
142
+ Can be useful when a component (or a variant of a component) is still in development and is not ready to be shared with the wider team.
132
143
 
133
- By default, the labels shown for previews and examples are stripped and 'titlized' versions of the preview file names and the example method names, respectively.
134
-
135
- If you wish to override the automatic navigation label generation for a preview or example you can use the `@label` comment tag:
144
+ > Available for both preview classes & example methods.
136
145
 
137
146
  ```ruby
138
- # @label Standard Button
139
- class BtnPreview < ViewComponent::Preview
147
+ # @hidden
148
+ class FooComponentPreview < ViewComponent::Preview
140
149
 
141
- # @label Icon Button
142
- def with_icon
150
+ # @hidden
151
+ def default
143
152
  end
144
153
  end
145
154
  ```
146
155
 
147
- In the example above, the preview and example would be displayed like this:
148
-
149
- <img src=".github/assets/nav_labels.png" width="200">
150
-
151
- #### Excluding previews and/or examples from the navigation
156
+ #### Adding notes
152
157
 
153
- Sometimes you may want a preview or an individual example to be 'hidden' in the Lookbook UI. This means that the preview or example will not show up in the navigation, but will still be accessible via it's URL. You can use the `@hidden` comment tag to manage this.
154
-
155
- To **hide an entire preview** so that it no longer shows up in the , include the `@hidden` tag in a class comment:
158
+ All comment text other than tags will be treated as markdown and rendered in the **Notes** panel for that example in the Lookbook UI.
156
159
 
157
160
  ```ruby
158
161
  # @hidden
159
- class MyComponentPreview < ViewComponent::Preview
160
- # examples here....
161
- end
162
- ```
162
+ class ProfileCardComponentPreview < ViewComponent::Preview
163
163
 
164
- To **hide an individual example**, include the `@hidden` tag in the appropriate method comment:
165
-
166
- ```ruby
167
- class MyComponentPreview < ViewComponent::Preview
168
-
169
- # Hidden Example
170
- # ----------
171
- # You won't see this in the nav!
172
- #
173
- # @hidden
174
- def hidden_example
175
- # ...
176
- end
177
-
178
- def a_visible_example
179
- # ...
180
- end
181
-
182
- # @hidden
183
- def another_hidden_example
184
- # ...
164
+ # Profile Card
165
+ # ------------
166
+ # Use the default profile card component whenever you need to represent a user.
167
+ def default
185
168
  end
186
169
  end
187
170
  ```
188
171
 
189
- ### 🔦 Viewing source code and rendered HTML output
190
-
191
- Lookbook displays the source code of the current preview (or the contents of preview template, if one is being used), right underneath the rendered preview itself:
192
-
193
- <img src=".github/assets/preview_source.png" width="400">
194
-
195
- You can also inspect the HTML output of the rendered preview (without any of the layout cruft):
196
-
197
- <img src=".github/assets/preview_output.png" width="400">
198
-
199
- All code panels have a 'copy-to-clipboard' button at the top right of the panel, just click it to copy the un-highlighted code to your clipboard.
200
-
201
- <img src=".github/assets/copy_to_clipboard.png" width="400">
172
+ <img src=".github/assets/preview_example_notes.png" width="400">
202
173
 
203
174
  ## Configuration
204
175
 
205
- Lookbook uses ViewComponent's configuration options for anything to do with previews, paths and general setup, so you won't need to duplicate any settings.
176
+ Lookbook will use the ViewComponent [configuration](https://viewcomponent.org/api.html#configuration) for your project to find and render your previews so you generally you won't need to configure anything separately.
206
177
 
207
178
  However the following Lookbook-specific config options are also available:
208
179
 
@@ -222,12 +193,22 @@ If you wish to add additional paths to listen for changes in, you can use the `l
222
193
  config.lookbook.listen_paths << Rails.root.join('app/other/directory')
223
194
  ```
224
195
 
196
+ ## Troubleshooting
197
+
198
+ #### Blank preview window
199
+
200
+ Certain setups (for example when using `Rack::LiveReload`) can cause an issue with the way that the preview iframe displays the rendered component preview (i.e. using the `srcdoc` attribute to avoid extra requests).
201
+
202
+ If you are seeing a blank preview window, but the source and output tabs are both displaying code as expected, you can disable the use of the `srcdoc` attribute using the following configuration option:
203
+
204
+ ```ruby
205
+ config.lookbook.preview_srcdoc = false
206
+ ```
207
+
225
208
  ## Contributing
226
209
 
227
210
  Lookbook is very much a small hobby/side project at the moment. I'd love to hear from anyone who is interested in contributing but I'm terrible at replying to emails or messages, so don't be surprised if I take forever to get back to you. It's not personal 😜
228
211
 
229
- However, I'm a frontend developer - not a Rails dev - so any thoughts, advice or PRs on how to improve the codebase will be always much appreciated. 🍻
230
-
231
212
  ### Developing on a local version of Lookbook
232
213
 
233
214
  The quickest way to get a development version of Lookbook up and running is to use the [lookbook-demo](https://github.com/allmarkedup/lookbook-demo) app and link it to a local version of the Lookbook gem:
@@ -43,5 +43,7 @@ Alpine.persistedStore("inspector", {
43
43
  // Init
44
44
 
45
45
  window.Alpine = Alpine;
46
- reloader(window.SOCKET_PATH).start();
46
+ if (window.SOCKET_PATH) {
47
+ reloader(window.SOCKET_PATH).start();
48
+ }
47
49
  Alpine.start();
@@ -8,9 +8,11 @@
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>">
10
10
 
11
- <script>
12
- window.SOCKET_PATH = "/lookbook/cable"
13
- </script>
11
+ <% if config.auto_refresh %>
12
+ <script>
13
+ window.SOCKET_PATH = "/lookbook/cable";
14
+ </script>
15
+ <% end %>
14
16
  <script src="/lookbook-assets/app.js" defer></script>
15
17
 
16
18
  <title><%= [@example&.label, @preview&.label, "Lookbook"].compact.join(" :: ") %></title>
@@ -2,7 +2,7 @@
2
2
  <div class="h-full relative mx-auto bg-white" x-data="preview" :style="`width: ${$store.preview.width}`">
3
3
  <iframe
4
4
  src="<%= url_for lookbook.preview_path %>"
5
- srcdoc="<%== @preview_srcdoc %>"
5
+ <% if config.preview_srcdoc %>srcdoc="<%== @preview_srcdoc %>"<% end %>
6
6
  frameborder="0"
7
7
  class="h-full w-full border-l border-gray-300 pr-4 -mx-px"
8
8
  x-data="sizeObserver"
data/config/routes.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  Lookbook::Engine.routes.draw do
2
- mount Lookbook::Engine.websocket => Lookbook::Engine.cable.mount_path
2
+ if Lookbook.config.auto_refresh
3
+ mount Lookbook::Engine.websocket => Lookbook::Engine.cable.mount_path
4
+ end
3
5
 
4
6
  root to: "browser#index", as: :home
5
7
 
@@ -28,6 +28,7 @@ module Lookbook
28
28
  options.preview_paths += vc_options.preview_paths
29
29
 
30
30
  options.preview_controller = vc_options.preview_controller if options.preview_controller.nil?
31
+ options.preview_srcdoc = true if options.preview_srcdoc.nil?
31
32
 
32
33
  options.listen_paths = options.listen_paths.map(&:to_s)
33
34
  options.listen_paths += options.preview_paths
@@ -35,10 +36,11 @@ module Lookbook
35
36
  end
36
37
 
37
38
  initializer "lookbook.cable.config" do |app|
38
- config_path = Lookbook::Engine.root.join("config", "lookbook_cable.yml")
39
- Lookbook::Engine.cable.cable = app.config_for(config_path).with_indifferent_access
40
- Lookbook::Engine.cable.mount_path = "/cable"
41
- Lookbook::Engine.cable.connection_class = -> { Lookbook::Connection }
39
+ if app.config.lookbook.auto_refresh
40
+ Lookbook::Engine.cable.cable = {adapter: "async"}.with_indifferent_access
41
+ Lookbook::Engine.cable.mount_path = "/cable"
42
+ Lookbook::Engine.cable.connection_class = -> { Lookbook::Connection }
43
+ end
42
44
  end
43
45
 
44
46
  initializer "lookbook.cable.logger" do
@@ -17,7 +17,9 @@ module Lookbook
17
17
 
18
18
  def lookbook_examples
19
19
  return @lookbook_examples if @lookbook_examples.present?
20
- examples = code_object.meths.map { |m| PreviewExample.new(m.name.to_s, self) }
20
+ public_methods = public_instance_methods(false)
21
+ public_method_objects = code_object.meths.filter { |m| public_methods.include?(m.name) }
22
+ examples = public_method_objects.map { |m| PreviewExample.new(m.name.to_s, self) }
21
23
  examples.reject!(&:hidden?)
22
24
  @lookbook_examples ||= Lookbook.config.sort_examples ? examples.sort_by(&:label) : examples
23
25
  end
@@ -1,3 +1,3 @@
1
1
  module Lookbook
2
- VERSION = "0.2.0"
2
+ VERSION = "0.2.4"
3
3
  end
@@ -1,4 +1,4 @@
1
- /*! tailwindcss v2.2.4 | MIT License | https://tailwindcss.com */
1
+ /*! tailwindcss v2.2.7 | MIT License | https://tailwindcss.com */
2
2
 
3
3
  /*! modern-normalize v1.1.0 | MIT License | https://github.com/sindresorhus/modern-normalize */
4
4
 
@@ -575,6 +575,14 @@ video {
575
575
  height: auto;
576
576
  }
577
577
 
578
+ /**
579
+ * Ensure the default browser behavior of the `hidden` attribute.
580
+ */
581
+
582
+ [hidden] {
583
+ display: none;
584
+ }
585
+
578
586
  *, ::before, ::after {
579
587
  --tw-translate-x: 0;
580
588
  --tw-translate-y: 0;
@@ -586,13 +594,13 @@ video {
586
594
  --tw-transform: translateX(var(--tw-translate-x)) translateY(var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
587
595
  --tw-border-opacity: 1;
588
596
  border-color: rgba(229, 231, 235, var(--tw-border-opacity));
589
- --tw-shadow: 0 0 #0000;
590
597
  --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/);
591
598
  --tw-ring-offset-width: 0px;
592
599
  --tw-ring-offset-color: #fff;
593
600
  --tw-ring-color: rgba(59, 130, 246, 0.5);
594
601
  --tw-ring-offset-shadow: 0 0 #0000;
595
602
  --tw-ring-shadow: 0 0 #0000;
603
+ --tw-shadow: 0 0 #0000;
596
604
  --tw-blur: var(--tw-empty,/*!*/ /*!*/);
597
605
  --tw-brightness: var(--tw-empty,/*!*/ /*!*/);
598
606
  --tw-contrast: var(--tw-empty,/*!*/ /*!*/);
@@ -603,16 +611,6 @@ video {
603
611
  --tw-sepia: var(--tw-empty,/*!*/ /*!*/);
604
612
  --tw-drop-shadow: var(--tw-empty,/*!*/ /*!*/);
605
613
  --tw-filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
606
- --tw-backdrop-blur: var(--tw-empty,/*!*/ /*!*/);
607
- --tw-backdrop-brightness: var(--tw-empty,/*!*/ /*!*/);
608
- --tw-backdrop-contrast: var(--tw-empty,/*!*/ /*!*/);
609
- --tw-backdrop-grayscale: var(--tw-empty,/*!*/ /*!*/);
610
- --tw-backdrop-hue-rotate: var(--tw-empty,/*!*/ /*!*/);
611
- --tw-backdrop-invert: var(--tw-empty,/*!*/ /*!*/);
612
- --tw-backdrop-opacity: var(--tw-empty,/*!*/ /*!*/);
613
- --tw-backdrop-saturate: var(--tw-empty,/*!*/ /*!*/);
614
- --tw-backdrop-sepia: var(--tw-empty,/*!*/ /*!*/);
615
- --tw-backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);
616
614
  }
617
615
 
618
616
  [type='text'],[type='email'],[type='url'],[type='password'],[type='number'],[type='date'],[type='datetime-local'],[type='month'],[type='search'],[type='tel'],[type='time'],[type='week'],[multiple],textarea,select {
@@ -2294,12 +2294,17 @@ Expression: "${expression}"
2294
2294
  });
2295
2295
  }
2296
2296
  var isDeferringHandlers = false;
2297
- var directiveHandlerStack = [];
2297
+ var directiveHandlerStacks = new Map();
2298
+ var currentHandlerStackKey = Symbol();
2298
2299
  function deferHandlingDirectives(callback) {
2299
2300
  isDeferringHandlers = true;
2301
+ let key = Symbol();
2302
+ currentHandlerStackKey = key;
2303
+ directiveHandlerStacks.set(key, []);
2300
2304
  let flushHandlers = () => {
2301
- while (directiveHandlerStack.length)
2302
- directiveHandlerStack.shift()();
2305
+ while (directiveHandlerStacks.get(key).length)
2306
+ directiveHandlerStacks.get(key).shift()();
2307
+ directiveHandlerStacks.delete(key);
2303
2308
  };
2304
2309
  let stopDeferring = () => {
2305
2310
  isDeferringHandlers = false;
@@ -2330,7 +2335,7 @@ Expression: "${expression}"
2330
2335
  return;
2331
2336
  handler3.inline && handler3.inline(el, directive2, utilities);
2332
2337
  handler3 = handler3.bind(handler3, el, directive2, utilities);
2333
- isDeferringHandlers ? directiveHandlerStack.push(handler3) : handler3();
2338
+ isDeferringHandlers ? directiveHandlerStacks.get(currentHandlerStackKey).push(handler3) : handler3();
2334
2339
  };
2335
2340
  fullHandler.runCleanups = doCleanup;
2336
2341
  return fullHandler;
@@ -2449,7 +2454,7 @@ Expression: "${expression}"
2449
2454
  onAttributesAdded((el, attrs) => {
2450
2455
  directives(el, attrs).forEach((handle) => handle());
2451
2456
  });
2452
- let outNestedComponents = (el) => !closestRoot(el.parentNode || closestRoot(el));
2457
+ let outNestedComponents = (el) => !closestRoot(el.parentElement);
2453
2458
  Array.from(document.querySelectorAll(allSelectors())).filter(outNestedComponents).forEach((el) => {
2454
2459
  initTree(el);
2455
2460
  });
@@ -2470,6 +2475,8 @@ Expression: "${expression}"
2470
2475
  initSelectorCallbacks.push(selectorCallback);
2471
2476
  }
2472
2477
  function closestRoot(el) {
2478
+ if (!el)
2479
+ return;
2473
2480
  if (rootSelectors().some((selector) => el.matches(selector)))
2474
2481
  return el;
2475
2482
  if (!el.parentElement)
@@ -2576,7 +2583,7 @@ Expression: "${expression}"
2576
2583
  get raw() {
2577
2584
  return raw;
2578
2585
  },
2579
- version: "3.2.2",
2586
+ version: "3.2.4",
2580
2587
  disableEffectScheduling,
2581
2588
  setReactivityEngine,
2582
2589
  addRootSelector,
@@ -2678,7 +2685,7 @@ Expression: "${expression}"
2678
2685
  let previousStyles = {};
2679
2686
  Object.entries(value).forEach(([key, value2]) => {
2680
2687
  previousStyles[key] = el.style[key];
2681
- el.style[key] = value2;
2688
+ el.style.setProperty(kebabCase(key), value2);
2682
2689
  });
2683
2690
  setTimeout(() => {
2684
2691
  if (el.style.length === 0) {
@@ -2696,6 +2703,9 @@ Expression: "${expression}"
2696
2703
  el.setAttribute("style", cache);
2697
2704
  };
2698
2705
  }
2706
+ function kebabCase(subject) {
2707
+ return subject.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
2708
+ }
2699
2709
  function once(callback, fallback = () => {
2700
2710
  }) {
2701
2711
  let called = false;
@@ -3113,6 +3123,8 @@ Expression: "${expression}"
3113
3123
  let handler3 = (e) => callback(e);
3114
3124
  let options = {};
3115
3125
  let wrapHandler = (callback2, wrapper) => (e) => wrapper(callback2, e);
3126
+ if (modifiers.includes("dot"))
3127
+ event = dotSyntax(event);
3116
3128
  if (modifiers.includes("camel"))
3117
3129
  event = camelCase2(event);
3118
3130
  if (modifiers.includes("passive"))
@@ -3174,6 +3186,9 @@ Expression: "${expression}"
3174
3186
  listenerTarget.removeEventListener(event, handler3, options);
3175
3187
  };
3176
3188
  }
3189
+ function dotSyntax(subject) {
3190
+ return subject.replace(/-/g, ".");
3191
+ }
3177
3192
  function camelCase2(subject) {
3178
3193
  return subject.toLowerCase().replace(/-(\w)/g, (match, char) => char.toUpperCase());
3179
3194
  }
@@ -3203,7 +3218,7 @@ Expression: "${expression}"
3203
3218
  function isNumeric(subject) {
3204
3219
  return !Array.isArray(subject) && !isNaN(subject);
3205
3220
  }
3206
- function kebabCase(subject) {
3221
+ function kebabCase2(subject) {
3207
3222
  return subject.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[_\s]/, "-").toLowerCase();
3208
3223
  }
3209
3224
  function isKeyEvent(event) {
@@ -3219,7 +3234,7 @@ Expression: "${expression}"
3219
3234
  }
3220
3235
  if (keyModifiers.length === 0)
3221
3236
  return false;
3222
- if (keyModifiers.length === 1 && keyModifiers[0] === keyToModifier(e.key))
3237
+ if (keyModifiers.length === 1 && keyToModifiers(e.key).includes(keyModifiers[0]))
3223
3238
  return false;
3224
3239
  const systemKeyModifiers = ["ctrl", "shift", "alt", "meta", "cmd", "super"];
3225
3240
  const selectedSystemKeyModifiers = systemKeyModifiers.filter((modifier) => keyModifiers.includes(modifier));
@@ -3231,22 +3246,33 @@ Expression: "${expression}"
3231
3246
  return e[`${modifier}Key`];
3232
3247
  });
3233
3248
  if (activelyPressedKeyModifiers.length === selectedSystemKeyModifiers.length) {
3234
- if (keyModifiers[0] === keyToModifier(e.key))
3249
+ if (keyToModifiers(e.key).includes(keyModifiers[0]))
3235
3250
  return false;
3236
3251
  }
3237
3252
  }
3238
3253
  return true;
3239
3254
  }
3240
- function keyToModifier(key) {
3241
- switch (key) {
3242
- case "/":
3243
- return "slash";
3244
- case " ":
3245
- case "Spacebar":
3246
- return "space";
3247
- default:
3248
- return key && kebabCase(key);
3249
- }
3255
+ function keyToModifiers(key) {
3256
+ if (!key)
3257
+ return [];
3258
+ key = kebabCase2(key);
3259
+ let modifierToKeyMap = {
3260
+ ctrl: "control",
3261
+ slash: "/",
3262
+ space: "-",
3263
+ spacebar: "-",
3264
+ cmd: "meta",
3265
+ esc: "escape",
3266
+ up: "arrow-up",
3267
+ down: "arrow-down",
3268
+ left: "arrow-left",
3269
+ right: "arrow-right"
3270
+ };
3271
+ modifierToKeyMap[key] = key;
3272
+ return Object.keys(modifierToKeyMap).map((modifier) => {
3273
+ if (modifierToKeyMap[modifier] === key)
3274
+ return modifier;
3275
+ }).filter((modifier) => modifier);
3250
3276
  }
3251
3277
  directive("model", (el, { modifiers, expression }, { effect: effect3, cleanup: cleanup2 }) => {
3252
3278
  let evaluate2 = evaluateLater(el, expression);
@@ -3287,7 +3313,7 @@ Expression: "${expression}"
3287
3313
  return (event, currentValue) => {
3288
3314
  return mutateDom(() => {
3289
3315
  if (event instanceof CustomEvent && event.detail !== void 0) {
3290
- return event.detail;
3316
+ return event.detail || event.target.value;
3291
3317
  } else if (el.type === "checkbox") {
3292
3318
  if (Array.isArray(currentValue)) {
3293
3319
  let newValue = modifiers.includes("number") ? safeParseNumber(event.target.value) : event.target.value;
@@ -3383,11 +3409,10 @@ Expression: "${expression}"
3383
3409
  let reactiveData = reactive(data2);
3384
3410
  initInterceptors(reactiveData);
3385
3411
  let undo = addScopeToNode(el, reactiveData);
3386
- if (reactiveData["init"])
3387
- reactiveData["init"]();
3412
+ reactiveData["init"] && evaluate(el, reactiveData["init"]);
3388
3413
  cleanup2(() => {
3389
3414
  undo();
3390
- reactiveData["destroy"] && reactiveData["destroy"]();
3415
+ reactiveData["destroy"] && evaluate(el, reactiveData["destroy"]);
3391
3416
  });
3392
3417
  }));
3393
3418
  directive("show", (el, { modifiers, expression }, { effect: effect3 }) => {
@@ -3444,6 +3469,8 @@ Expression: "${expression}"
3444
3469
  if (isNumeric3(items) && items >= 0) {
3445
3470
  items = Array.from(Array(items).keys(), (i) => i + 1);
3446
3471
  }
3472
+ if (items === void 0)
3473
+ items = [];
3447
3474
  let lookup = el._x_lookup;
3448
3475
  let prevKeys = el._x_prevKeys;
3449
3476
  let scopes = [];
@@ -7687,6 +7714,8 @@ Expression: "${expression}"
7687
7714
  active: "source"
7688
7715
  });
7689
7716
  window.Alpine = module_default;
7690
- reloader_default(window.SOCKET_PATH).start();
7717
+ if (window.SOCKET_PATH) {
7718
+ reloader_default(window.SOCKET_PATH).start();
7719
+ }
7691
7720
  module_default.start();
7692
7721
  })();
metadata CHANGED
@@ -1,27 +1,27 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lookbook
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mark Perkins
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-08-19 00:00:00.000000000 Z
11
+ date: 2021-09-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '6.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '6.0'
27
27
  - !ruby/object:Gem::Dependency
@@ -200,7 +200,6 @@ files:
200
200
  - app/views/lookbook/partials/nav/_label.html.erb
201
201
  - app/views/lookbook/partials/nav/_nav.html.erb
202
202
  - app/views/lookbook/partials/nav/_preview.html.erb
203
- - config/lookbook_cable.yml
204
203
  - config/routes.rb
205
204
  - lib/lookbook.rb
206
205
  - lib/lookbook/collection.rb
@@ -1,8 +0,0 @@
1
- development:
2
- adapter: async
3
-
4
- test:
5
- adapter: async
6
-
7
- production:
8
- adapter: async