lookbook 0.2.1 → 0.2.2

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: 7f1b2263fb0949354da2aab14aeea2e2c0052810b6eb316249d9796718413614
4
- data.tar.gz: 7423d9845627d153a1c46f1adc3fa4a14ed708d97f80a664b67c0e9cc0018528
3
+ metadata.gz: 6e8a93baa87f47304f68676fd5884b61eb32782f552e89316e4abb702373003f
4
+ data.tar.gz: ada4f4b935bc63066b980a8faf7fcf81f4f7a594790f4f4ddd8a01a630b880f5
5
5
  SHA512:
6
- metadata.gz: b821396d726636b7c633535543c1c4e718181b65809b85ae5b4ffee857914cb6910f554d28c0cced233d0e8419b60545b4115bda573d6b15b473fdf6c9d0fe24
7
- data.tar.gz: 902e85067b97aa9371e9708aca1c14c2f73d928603755f63357e1644e7b078e515a2e979cdedfef0e37b206f930e9ac21f7ab15aeb138d2f8e3adc86f0dd1441
6
+ metadata.gz: 8cc694ae333a50382654e9c36f52a9cfe964a20bd6715ce365dd55adcca44c564e1a742c053625286dad9f929bf9640919d961277d4c426ab7c9584cb57fd5da
7
+ data.tar.gz: ef0fc829bf1528cc521f2cd98887fc67476ed1d0a316e8ebefd9813e744714a0145b897091f7cc6768c2b459b1c3e5bf3982b2ee7ae451f2b2e512fa35e67b4a
data/README.md CHANGED
@@ -1,13 +1,18 @@
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
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
 
@@ -38,18 +43,13 @@ The [demo app repo](https://github.com/allmarkedup/lookbook-demo) contains instr
38
43
 
39
44
  ### 1. Add as a dependency
40
45
 
41
- In your `Gemfile` add:
46
+ Add Lookbook to your `Gemfile` somewhere **after** the ViewComponent gem. For example:
42
47
 
43
48
  ```ruby
49
+ gem "view_component", require: "view_component/engine"
44
50
  gem "lookbook"
45
51
  ```
46
52
 
47
- or
48
-
49
- ```bash
50
- gem install lookbook
51
- ```
52
-
53
53
  ### 2. Mount the Lookbook engine
54
54
 
55
55
  You then need to mount the Lookbook engine (at a path of your choosing) in your `routes.rb` file:
@@ -70,125 +70,110 @@ Then you can start your app as normal and navigate to `http://localhost:3000/loo
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, of course).
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 information 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 directory (or 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
152
-
153
- Sometimes you may want to temporarily hide a preview or an individual example from 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.
156
+ #### Adding notes
154
157
 
155
- To **hide an entire preview** 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
- ```
163
-
164
- To **hide an individual example** include the `@hidden` tag in the appropriate method comment:
165
-
166
- ```ruby
167
- class MyComponentPreview < ViewComponent::Preview
162
+ class ProfileCardComponentPreview < ViewComponent::Preview
168
163
 
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
 
172
+ <img src=".github/assets/preview_example_notes.png" width="400">
173
+
189
174
  ## Configuration
190
175
 
191
- 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.
192
177
 
193
178
  However the following Lookbook-specific config options are also available:
194
179
 
@@ -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>
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
 
@@ -35,10 +35,12 @@ module Lookbook
35
35
  end
36
36
 
37
37
  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 }
38
+ if app.config.lookbook.auto_refresh
39
+ config_path = Lookbook::Engine.root.join("config", "lookbook_cable.yml")
40
+ Lookbook::Engine.cable.cable = app.config_for(config_path).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
@@ -1,3 +1,3 @@
1
1
  module Lookbook
2
- VERSION = "0.2.1"
2
+ VERSION = "0.2.2"
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.1
4
+ version: 0.2.2
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-06 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