lookbook 0.4.4 → 0.4.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +220 -53
- data/app/assets/lookbook/css/app.css +14 -8
- data/app/assets/lookbook/js/app.js +2 -0
- data/app/assets/lookbook/js/nav.js +5 -6
- data/app/assets/lookbook/js/page.js +5 -0
- data/app/assets/lookbook/js/utils/morph.js +3 -0
- data/app/assets/lookbook/js/workbench/param.js +19 -0
- data/app/controllers/lookbook/app_controller.rb +23 -1
- data/app/helpers/lookbook/application_helper.rb +1 -1
- data/app/helpers/lookbook/preview_helper.rb +7 -0
- data/app/views/lookbook/workbench/_inspector.html.erb +6 -1
- data/app/views/lookbook/workbench/inspector/_params.html.erb +28 -0
- data/app/views/lookbook/workbench/inspector/params/_select.html.erb +8 -0
- data/app/views/lookbook/workbench/inspector/params/_text.html.erb +8 -0
- data/app/views/lookbook/workbench/inspector/params/_textarea.html.erb +8 -0
- data/app/views/lookbook/workbench/inspector/params/_toggle.html.erb +13 -0
- data/lib/lookbook/engine.rb +6 -0
- data/lib/lookbook/features.rb +24 -0
- data/lib/lookbook/params.rb +110 -0
- data/lib/lookbook/preview.rb +1 -1
- data/lib/lookbook/preview_example.rb +13 -1
- data/lib/lookbook/preview_group.rb +5 -1
- data/lib/lookbook/taggable.rb +2 -2
- data/lib/lookbook/version.rb +1 -1
- data/lib/lookbook.rb +2 -0
- data/public/lookbook-assets/app.css +129 -7
- data/public/lookbook-assets/app.js +46 -8
- metadata +12 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 676b8a78237532971ec7d22d7265b3da7d3b896aa8020d7ab1c514edc3859b45
|
4
|
+
data.tar.gz: dfb016bf927d7492afdeacd3b10a2425563e227b5cbd4ebd4d3f7a5bfa97609f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2e5aeb26c2db3322e7aa127e176b691744d5af3cfbd207159f65d0e9dee1362382f418b07bda2fa7b75d7f85b35f522efe106707e665c6e12f9ab5e4ca1f4cd1
|
7
|
+
data.tar.gz: be2908ed45f9645dd9387adf6547c8c45ba262310325438197de22d6db9909705fa3e45de0912914004d516c4546618ccc73fd96fea3fbdc86f2affa5e7ebb3d
|
data/README.md
CHANGED
@@ -28,6 +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
32
|
|
32
33
|
## Lookbook demo
|
33
34
|
|
@@ -76,7 +77,7 @@ Lookbook parses [Yard-style comment tags](https://rubydoc.info/gems/yard/file/do
|
|
76
77
|
|
77
78
|
```ruby
|
78
79
|
# @label Basic Button
|
79
|
-
# @display bg_color
|
80
|
+
# @display bg_color #fff
|
80
81
|
class ButtonComponentPreview < ViewComponent::Preview
|
81
82
|
|
82
83
|
# Primary button
|
@@ -90,11 +91,24 @@ class ButtonComponentPreview < ViewComponent::Preview
|
|
90
91
|
end
|
91
92
|
end
|
92
93
|
|
94
|
+
# Button with icon
|
95
|
+
# ----------------
|
96
|
+
# This example uses dynamic preview parameters
|
97
|
+
# which can be edited live in the Lookbook UI
|
98
|
+
#
|
99
|
+
# @param text
|
100
|
+
# @param icon select [heart, cog, alert]
|
101
|
+
def icon(text: "Spread the love", icon: "heart")
|
102
|
+
render ButtonComponent.new(icon: icon) do
|
103
|
+
text
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
93
107
|
# Inverted button
|
94
108
|
# ---------------
|
95
109
|
# For light-on-dark screens
|
96
110
|
#
|
97
|
-
# @display bg_color
|
111
|
+
# @display bg_color #000
|
98
112
|
def secondary
|
99
113
|
render ButtonComponent.new(style: :inverted) do
|
100
114
|
"Click me"
|
@@ -141,54 +155,41 @@ end
|
|
141
155
|
|
142
156
|
The following Lookbook-specific tags are available for use:
|
143
157
|
|
144
|
-
* `@label
|
145
|
-
* `@
|
146
|
-
*
|
147
|
-
*
|
158
|
+
* [`@label`](#label-tag)
|
159
|
+
* [`@display`](#display-tag)
|
160
|
+
* [`@!group ... @!endgroup`](#group-tag)
|
161
|
+
* [`@hidden`](#hidden-tag)
|
162
|
+
* [`@param`](#param-tag) [⚠️ **experimental!** - requires [feature opt-in](#experimental-features) ⚠️]
|
148
163
|
|
149
|
-
|
164
|
+
<h3 id="label-tag">🏷 @label</h3>
|
150
165
|
|
151
166
|
Used to replace the auto-generated navigation label for the item with `<text>`.
|
152
167
|
|
153
|
-
> Available for preview classes & example methods.
|
154
|
-
|
155
168
|
```ruby
|
156
|
-
|
157
|
-
class FooComponentPreview < ViewComponent::Preview
|
158
|
-
|
159
|
-
# @label Example Label
|
160
|
-
def default
|
161
|
-
end
|
162
|
-
end
|
169
|
+
@label <text>
|
163
170
|
```
|
164
171
|
|
165
|
-
|
166
|
-
|
167
|
-
Used to temporarily exclude an item from the Lookbook navigation. The item will still be accessible via it's URL.
|
168
|
-
|
169
|
-
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.
|
170
|
-
|
171
|
-
> Available for both preview classes & example methods.
|
172
|
+
> Available for preview classes & example methods.
|
172
173
|
|
173
174
|
```ruby
|
174
|
-
# @
|
175
|
+
# @label Preview Label
|
175
176
|
class FooComponentPreview < ViewComponent::Preview
|
176
177
|
|
177
|
-
# @
|
178
|
+
# @label Example Label
|
178
179
|
def default
|
179
180
|
end
|
180
181
|
end
|
181
182
|
```
|
182
183
|
|
183
|
-
|
184
|
+
<h3 id="display-tag">🏷 @display</h3>
|
184
185
|
|
185
186
|
The `@display` tag lets you pass custom parameters to your preview layout so that the component preview can be customised on a per-example basis.
|
186
187
|
|
187
188
|
```ruby
|
188
|
-
# @display bg_color
|
189
|
+
# @display bg_color #eee
|
189
190
|
class FooComponentPreview < ViewComponent::Preview
|
190
191
|
|
191
|
-
# @display max_width
|
192
|
+
# @display max_width 500px
|
192
193
|
# @display wrapper true
|
193
194
|
def default
|
194
195
|
end
|
@@ -198,13 +199,11 @@ end
|
|
198
199
|
The `@display` tag can be applied at the preview (class) or at the example (method) level, and takes the following format:
|
199
200
|
|
200
201
|
```ruby
|
201
|
-
|
202
|
+
@display <key> <value>
|
202
203
|
```
|
203
204
|
|
204
205
|
- `<key>` must be a valid Ruby hash key name, without quotes or spaces
|
205
|
-
- `<value>`
|
206
|
-
|
207
|
-
> [See below for some examples](#some-display-value-examples) of valid and invalid `@display` values.
|
206
|
+
- `<value>` will be parsed using the [Ruby YAML parser](https://yaml.org/YAML_for_ruby.html) to resolve the value
|
208
207
|
|
209
208
|
These display parameters can then be accessed via the `params` hash in your preview layout using `params[:lookbook][:display][<key>]`:
|
210
209
|
|
@@ -244,27 +243,7 @@ config.lookbook.preview_display_params = {
|
|
244
243
|
|
245
244
|
Globally defined display params will be available to all previews. Any preview or example-level `@display` values with the same name will take precedence and override a globally-set one.
|
246
245
|
|
247
|
-
|
248
|
-
|
249
|
-
Valid:
|
250
|
-
|
251
|
-
```ruby
|
252
|
-
# @display body_classes "bg-red border border-4 border-green"
|
253
|
-
# @display wrap_in_container true
|
254
|
-
# @display emojis_to_show 4
|
255
|
-
# @display page_title "Special example title"
|
256
|
-
```
|
257
|
-
|
258
|
-
Invalid:
|
259
|
-
|
260
|
-
```ruby
|
261
|
-
# @display body_classes 'bg-red border border-4 border-green' [❌ single quotes]
|
262
|
-
# @display wrap_in_container should_wrap [❌ unquoted string, perhaps trying to call a method]
|
263
|
-
# @display page title "Special example title" [❌ space in key]
|
264
|
-
# @display bg_color #fff [❌ colors need quotes around them, it's not CSS!]
|
265
|
-
```
|
266
|
-
|
267
|
-
### 🔖 `@!group <name> ... @!endgroup`
|
246
|
+
<h3 id="group-tag">🔖 `@!group ... @!endgroup`</h3>
|
268
247
|
|
269
248
|
For smaller components, it can often make sense to render a set of preview examples in a single window, rather than representing them as individual items in the navigation which can start to look a bit cluttered.
|
270
249
|
|
@@ -310,6 +289,167 @@ The example above would display the `Sizes` examples grouped together on a singl
|
|
310
289
|
|
311
290
|
You can have as many groups as you like within a single preview class, but each example can only belong to one group.
|
312
291
|
|
292
|
+
<h3 id="hidden-tag">🏷 `@hidden`</h3>
|
293
|
+
|
294
|
+
Used to temporarily exclude an item from the Lookbook navigation. The item will still be accessible via it's URL.
|
295
|
+
|
296
|
+
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.
|
297
|
+
|
298
|
+
> Available for both preview classes & example methods.
|
299
|
+
|
300
|
+
```ruby
|
301
|
+
# @hidden
|
302
|
+
class FooComponentPreview < ViewComponent::Preview
|
303
|
+
|
304
|
+
# @hidden
|
305
|
+
def default
|
306
|
+
end
|
307
|
+
end
|
308
|
+
```
|
309
|
+
|
310
|
+
<h3 id="param-tag"> 🚧 @param (experimental)</h3>
|
311
|
+
|
312
|
+
> ⚠️ 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.
|
313
|
+
|
314
|
+
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.
|
315
|
+
|
316
|
+
Each `@param` will have an associated form field generated for it. The values for each field will be handled as [dynamic preview params](https://viewcomponent.org/guide/previews.html#:~:text=It%E2%80%99s%20also%20possible%20to%20set%20dynamic%20values%20from%20the%20params%20by%20setting%20them%20as%20arguments%3A) when rendering the example.
|
317
|
+
|
318
|
+
The `@param` tag takes the following format:
|
319
|
+
|
320
|
+
```ruby
|
321
|
+
@param <name> <input_type> <opts?>
|
322
|
+
```
|
323
|
+
|
324
|
+
- `<name>` - name of the dynamic preview param
|
325
|
+
- `<input_type>` - input field type to generate in the UI
|
326
|
+
- `<opts?>` - YAML-encoded field options, used for some field types
|
327
|
+
|
328
|
+
#### Input types
|
329
|
+
|
330
|
+
The following **input field types** are available for use:
|
331
|
+
|
332
|
+
📝 **Text-style inputs** - Single line fields, useful for short strings of text or numbers.
|
333
|
+
|
334
|
+
```ruby
|
335
|
+
@param <name> text
|
336
|
+
@param <name> email
|
337
|
+
@param <name> number
|
338
|
+
@param <name> url
|
339
|
+
@param <name> tel
|
340
|
+
```
|
341
|
+
|
342
|
+
> The above types only differ in the validation constraints they impose on the input field.
|
343
|
+
|
344
|
+
📝 **Textarea** - Multi-line textarea field for longer-form content.
|
345
|
+
|
346
|
+
```ruby
|
347
|
+
@param <name> textarea
|
348
|
+
```
|
349
|
+
|
350
|
+
📝 **Select box** - Dropdown select field for selecting from a list of known options.
|
351
|
+
|
352
|
+
```ruby
|
353
|
+
@param <name> select <options>
|
354
|
+
```
|
355
|
+
|
356
|
+
`<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:
|
357
|
+
|
358
|
+
```ruby
|
359
|
+
# Basic options:
|
360
|
+
# @param theme select [primary, secondary, danger]
|
361
|
+
|
362
|
+
# With custom labels (each item itself an array of [label, value]):
|
363
|
+
# @param theme select [[Primary theme, primary], [Secondary theme, secondary], [Danger theme, danger]]
|
364
|
+
|
365
|
+
# With empty option (`~` in YAML)
|
366
|
+
# @param theme select [~, primary, secondary, danger]
|
367
|
+
```
|
368
|
+
|
369
|
+
> **Note**: In most cases YAML does not require quoting of strings, however if you are running into issues check out the [Ruby YAML docs](https://yaml.org/YAML_for_ruby.html) for a complete syntax reference.
|
370
|
+
|
371
|
+
📝 **Toggle** - On/off switch for toggling boolean values.
|
372
|
+
|
373
|
+
```ruby
|
374
|
+
@param <name> toggle
|
375
|
+
```
|
376
|
+
|
377
|
+
#### Default values
|
378
|
+
|
379
|
+
Default values are specified as part of the preview example method parameters in the usual Ruby way:
|
380
|
+
|
381
|
+
```ruby
|
382
|
+
def button(content: "Click me", theme: "primary", arrow: false)
|
383
|
+
# ...
|
384
|
+
end
|
385
|
+
```
|
386
|
+
|
387
|
+
These will be used as the default values for the param fields.
|
388
|
+
|
389
|
+
> Note that the default values are **not** evaluated at runtime, so you cannot use method calls to generate the defaults. Only static default values are supported.
|
390
|
+
|
391
|
+
#### Type casting values
|
392
|
+
|
393
|
+
Most dynamic param values are passed to the example method as strings, with the following exceptions:
|
394
|
+
|
395
|
+
- `toggle` input - values are cast to booleans
|
396
|
+
- `number` input - values are cast to integers
|
397
|
+
|
398
|
+
In some cases, you may want to type cast the parameter value to something else (for example a `Symbol`) before using it when initializing the component.
|
399
|
+
|
400
|
+
To help with this, a `type` option can be specified in the `@param` definition to automatically cast the dynamic value to a different type:
|
401
|
+
|
402
|
+
```ruby
|
403
|
+
# @param <name> [<type>] <input_type> <opts?>
|
404
|
+
```
|
405
|
+
|
406
|
+
In the example below, the value of the `theme` param (by default a string) will be automatically cast to a Symbol, ready for use in instantiating the component.
|
407
|
+
|
408
|
+
```ruby
|
409
|
+
# @param theme [Symbol] select [primary, secondary, danger]
|
410
|
+
def default(theme: :primary)
|
411
|
+
render Elements::ButtonComponent.new(theme: theme) do
|
412
|
+
"Click me"
|
413
|
+
end
|
414
|
+
end
|
415
|
+
```
|
416
|
+
|
417
|
+
The supported types to cast to are:
|
418
|
+
|
419
|
+
- `String` - *default for all except `toggle` inputs*
|
420
|
+
- `Boolean` - *default for `toggle` inputs*
|
421
|
+
- `Symbol`
|
422
|
+
- `Date`
|
423
|
+
- `DateTime`
|
424
|
+
- `Integer`
|
425
|
+
- `Float`
|
426
|
+
|
427
|
+
The following structured types are also available but should be considered **experimental** - you may run into bugs!
|
428
|
+
|
429
|
+
- `Hash` - *value string converted to Hash using the Ruby YAML parser*
|
430
|
+
- `Array` - *value string converted to Array using the Ruby YAML parser*
|
431
|
+
|
432
|
+
#### Full example:
|
433
|
+
|
434
|
+
```ruby
|
435
|
+
class ButtonComponentPreview < ViewComponent::Preview
|
436
|
+
|
437
|
+
# The params defined below will be editable in the UI:
|
438
|
+
#
|
439
|
+
# @param content text
|
440
|
+
# @param theme select [primary, secondary, danger]
|
441
|
+
# @param arrow toggle
|
442
|
+
def default(content: "Click me", theme: "primary", arrow: true)
|
443
|
+
render Elements::ButtonComponent.new(theme: theme, arrow: arrow) do
|
444
|
+
content
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
end
|
449
|
+
```
|
450
|
+
|
451
|
+
<img src=".github/assets/dynamic_params.png">
|
452
|
+
|
313
453
|
### Adding notes
|
314
454
|
|
315
455
|
All comment text other than tags will be treated as markdown and rendered in the **Notes** panel for that example in the Lookbook UI.
|
@@ -352,6 +492,33 @@ If you wish to add additional paths to listen for changes in, you can use the `l
|
|
352
492
|
config.lookbook.listen_paths << Rails.root.join('app/other/directory')
|
353
493
|
```
|
354
494
|
|
495
|
+
<h3 id="experimental-features">Experimental features opt-in</h3>
|
496
|
+
|
497
|
+
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.
|
498
|
+
|
499
|
+
> ⚠️ **Please note:** Experimental features should be considered to be **subject to extensive change** and breaking changes to them may be made within point releases - these features are **not** considered to be covered by [semver](https://semver.org/) whilst flagged as 'experimental'. ⚠️
|
500
|
+
|
501
|
+
#### Opting into specific features (recommended)
|
502
|
+
|
503
|
+
To opt into individual experimental features, include the name of the feature in the `experimental_features` config option:
|
504
|
+
|
505
|
+
```ruby
|
506
|
+
config.lookbook.experimental_features = ["feature_name"]
|
507
|
+
```
|
508
|
+
|
509
|
+
The current experimental features that can be opted into are:
|
510
|
+
|
511
|
+
- `params`: Live-editable, dynamic preview parameters ([read more](#param-tag)). Include `"params"` in the `experimental_features` config option to opt in.
|
512
|
+
|
513
|
+
#### Opting into all experimental features (not recommended!)
|
514
|
+
|
515
|
+
If you want to live life on the bleeding-edge you can opt-in to all current **and future** experimental features (usual caveats apply):
|
516
|
+
|
517
|
+
```ruby
|
518
|
+
config.lookbook.experimental_features = true
|
519
|
+
```
|
520
|
+
|
521
|
+
|
355
522
|
## Keyboard shortcuts
|
356
523
|
|
357
524
|
Lookbook provides a few keyboard shortcuts to help you quickly move around the UI.
|
@@ -35,14 +35,6 @@
|
|
35
35
|
fill: none;
|
36
36
|
}
|
37
37
|
|
38
|
-
.h-fill {
|
39
|
-
height: fill-available;
|
40
|
-
}
|
41
|
-
|
42
|
-
.min-h-fill {
|
43
|
-
min-height: fill-available;
|
44
|
-
}
|
45
|
-
|
46
38
|
::-webkit-scrollbar {
|
47
39
|
width: 8px;
|
48
40
|
height: 8px;
|
@@ -63,3 +55,17 @@
|
|
63
55
|
@apply bg-gray-400;
|
64
56
|
}
|
65
57
|
}
|
58
|
+
|
59
|
+
@layer utilities {
|
60
|
+
.h-fill {
|
61
|
+
height: fill-available;
|
62
|
+
}
|
63
|
+
|
64
|
+
.min-h-fill {
|
65
|
+
min-height: fill-available;
|
66
|
+
}
|
67
|
+
|
68
|
+
.form-input {
|
69
|
+
@apply border-gray-300 text-gray-700 focus:ring-indigo-300 focus:border-indigo-300 rounded-sm text-sm w-full;
|
70
|
+
}
|
71
|
+
}
|
@@ -9,6 +9,7 @@ import page from "./page";
|
|
9
9
|
import workbench from "./workbench";
|
10
10
|
import preview from "./workbench/preview";
|
11
11
|
import inspector from "./workbench/inspector";
|
12
|
+
import param from "./workbench/param";
|
12
13
|
import nav from "./nav";
|
13
14
|
import navNode from "./nav/node";
|
14
15
|
import navLeaf from "./nav/leaf";
|
@@ -56,6 +57,7 @@ Alpine.data("navLeaf", navLeaf);
|
|
56
57
|
Alpine.data("workbench", workbench);
|
57
58
|
Alpine.data("preview", preview);
|
58
59
|
Alpine.data("inspector", inspector);
|
60
|
+
Alpine.data("param", param);
|
59
61
|
Alpine.data("clipboard", clipboard);
|
60
62
|
Alpine.data("sizeObserver", sizeObserver);
|
61
63
|
Alpine.data("split", split);
|
@@ -22,13 +22,12 @@ export default function () {
|
|
22
22
|
});
|
23
23
|
},
|
24
24
|
navigate(path) {
|
25
|
-
|
26
|
-
path = path.currentTarget.href;
|
27
|
-
}
|
28
|
-
history.pushState({}, null, path);
|
29
|
-
this.$dispatch("popstate");
|
25
|
+
this.navigateTo(path instanceof Event ? path.currentTarget.href : path);
|
30
26
|
},
|
31
|
-
focusFilter() {
|
27
|
+
focusFilter($event) {
|
28
|
+
if ($event.target.tagName === "INPUT") {
|
29
|
+
return;
|
30
|
+
}
|
32
31
|
this.currentFocus = this.$refs.filter;
|
33
32
|
setTimeout(() => this.$refs.filter.focus(), 0);
|
34
33
|
},
|
@@ -27,7 +27,12 @@ export default function page() {
|
|
27
27
|
render() {
|
28
28
|
if (this.ready) {
|
29
29
|
morph(this.$el, store.doc.getElementById(this.$el.id));
|
30
|
+
this.$dispatch("document:patched");
|
30
31
|
}
|
31
32
|
},
|
33
|
+
navigateTo(path) {
|
34
|
+
history.pushState({}, null, path);
|
35
|
+
this.$dispatch("popstate");
|
36
|
+
},
|
32
37
|
};
|
33
38
|
}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
export default function param() {
|
2
|
+
return {
|
3
|
+
focused: false,
|
4
|
+
setFocus() {
|
5
|
+
if (this.focused && this.$el.focus) {
|
6
|
+
this.$el.focus();
|
7
|
+
}
|
8
|
+
},
|
9
|
+
update(name, value) {
|
10
|
+
const searchParams = new URLSearchParams(window.location.search);
|
11
|
+
searchParams.set(name, value);
|
12
|
+
const path = location.href.replace(location.search, "");
|
13
|
+
this.navigateTo(`${path}?${searchParams.toString()}`);
|
14
|
+
},
|
15
|
+
validate() {
|
16
|
+
return this.$el.reportValidity ? this.$el.reportValidity() : true;
|
17
|
+
},
|
18
|
+
};
|
19
|
+
}
|
@@ -8,7 +8,7 @@ module Lookbook
|
|
8
8
|
prepend_view_path File.expand_path("../../views/lookbook", __dir__)
|
9
9
|
|
10
10
|
layout "layouts/app"
|
11
|
-
helper Lookbook::
|
11
|
+
helper Lookbook::ApplicationHelper
|
12
12
|
|
13
13
|
before_action :find_preview, only: [:preview, :show]
|
14
14
|
before_action :find_example, only: [:preview, :show]
|
@@ -105,6 +105,15 @@ module Lookbook
|
|
105
105
|
end
|
106
106
|
|
107
107
|
def set_params(example = nil)
|
108
|
+
if example.present? && enabled?(:params)
|
109
|
+
# cast known params to type
|
110
|
+
example.params.each do |param|
|
111
|
+
if preview_controller.params.key?(param[:name])
|
112
|
+
preview_controller.params[param[:name]] = Lookbook::Params.cast(preview_controller.params[param[:name]], param[:type])
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
# set display params
|
108
117
|
example_params = example.nil? ? @preview.display_params : example.display_params
|
109
118
|
preview_controller.params.merge!({
|
110
119
|
lookbook: {
|
@@ -139,6 +148,15 @@ module Lookbook
|
|
139
148
|
}
|
140
149
|
}
|
141
150
|
}
|
151
|
+
if enabled?(:params)
|
152
|
+
@inspector[:panes][:params] = {
|
153
|
+
label: "Params",
|
154
|
+
template: "params",
|
155
|
+
hotkey: "p",
|
156
|
+
items: @source.many? ? [] : @example.params,
|
157
|
+
disabled: @source.many? || @example.params.none?
|
158
|
+
}
|
159
|
+
end
|
142
160
|
end
|
143
161
|
|
144
162
|
def assign_nav
|
@@ -174,5 +192,9 @@ module Lookbook
|
|
174
192
|
controller.response = response
|
175
193
|
@preview_controller ||= controller
|
176
194
|
end
|
195
|
+
|
196
|
+
def enabled?(feature)
|
197
|
+
Lookbook::Features.enabled?(feature)
|
198
|
+
end
|
177
199
|
end
|
178
200
|
end
|
@@ -20,7 +20,12 @@
|
|
20
20
|
</div>
|
21
21
|
<div class="flex-auto overflow-auto bg-gray-50">
|
22
22
|
<% panes.each do |key, props| %>
|
23
|
-
<div class="flex flex-col h-full relative" x-show="active('<%= key %>')" x-
|
23
|
+
<div class="flex flex-col h-full relative" x-show="active('<%= key %>')" x-effect="
|
24
|
+
if ($store.inspector.active === '<%= key %>') {
|
25
|
+
const input = $el.querySelector('[data-param-input]');
|
26
|
+
if (input) setTimeout(() => input.focus(), 0)
|
27
|
+
}
|
28
|
+
" x-cloak>
|
24
29
|
<% if props[:clipboard].present? %>
|
25
30
|
<%= render "shared/clipboard" do %><%= h props[:clipboard].strip %><% end %>
|
26
31
|
<% end %>
|
@@ -0,0 +1,28 @@
|
|
1
|
+
<% if @example.type == :group %>
|
2
|
+
<div class="p-4 prose prose-sm">
|
3
|
+
<em class='opacity-50'>Params are not supported for grouped previews.</em>
|
4
|
+
</div>
|
5
|
+
<% elsif items.none? %>
|
6
|
+
<div class="p-4 prose prose-sm">
|
7
|
+
<em class='opacity-50'>No params configured.</em>
|
8
|
+
</div>
|
9
|
+
<% else %>
|
10
|
+
<div class="py-3">
|
11
|
+
<% items.each do |param| %>
|
12
|
+
<div class="px-4 py-3" x-data="param" @document:patched="setFocus">
|
13
|
+
<div class="flex items-start max-w-[800px]">
|
14
|
+
<div class="w-[200px] flex-none py-2">
|
15
|
+
<label for="param-<%= param[:name] %>" class="font-bold"><%= param[:name].titleize %></label>
|
16
|
+
</div>
|
17
|
+
<div class="flex-grow" @focus="focussed = true" @blur="focussed = false">
|
18
|
+
<%= render "workbench/inspector/params/#{param[:input]}",
|
19
|
+
**param,
|
20
|
+
value: params.key?(param[:name]) ? params[param[:name]] : param[:default],
|
21
|
+
id: "#{@example.id}-param-#{param[:name]}"
|
22
|
+
%>
|
23
|
+
</div>
|
24
|
+
</div>
|
25
|
+
</div>
|
26
|
+
<% end %>
|
27
|
+
</div>
|
28
|
+
<% end %>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<div id="<%= id %>" x-init="checked = <%= value == "true" ? "true" : "false" %>" data-param-input>
|
2
|
+
<button type="button"
|
3
|
+
class="relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-400"
|
4
|
+
:class="{'bg-indigo-500': checked, 'bg-gray-300': !checked}"
|
5
|
+
role="switch"
|
6
|
+
@click.stop="checked = !checked; update('<%= name %>', checked)">
|
7
|
+
<span
|
8
|
+
aria-hidden="true"
|
9
|
+
class="pointer-events-none inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200"
|
10
|
+
:class="{'translate-x-5': checked, 'translate-x-0': !checked}"
|
11
|
+
></span>
|
12
|
+
</button>
|
13
|
+
</div>
|
data/lib/lookbook/engine.rb
CHANGED
@@ -8,6 +8,10 @@ module Lookbook
|
|
8
8
|
def config
|
9
9
|
@config ||= Engine.config.lookbook
|
10
10
|
end
|
11
|
+
|
12
|
+
def logger
|
13
|
+
@logger ||= config.debug == true ? Rails.logger : Lookbook::NullLogger.new
|
14
|
+
end
|
11
15
|
end
|
12
16
|
|
13
17
|
class Engine < Rails::Engine
|
@@ -35,6 +39,8 @@ module Lookbook
|
|
35
39
|
options.listen_paths = options.listen_paths.map(&:to_s)
|
36
40
|
options.listen_paths += options.preview_paths
|
37
41
|
options.listen_paths << (vc_options.view_component_path || Rails.root.join("app/components"))
|
42
|
+
|
43
|
+
options.experimental_features = false unless options.experimental_features.present?
|
38
44
|
end
|
39
45
|
|
40
46
|
initializer "lookbook.cable.config" do |app|
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Lookbook
|
2
|
+
module Features
|
3
|
+
EXPERIMENTAL_FEATURES = [:params]
|
4
|
+
|
5
|
+
def self.experimental_feature?(name)
|
6
|
+
EXPERIMENTAL_FEATURES.include?(name.to_sym)
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.enabled?(name)
|
10
|
+
return true unless experimental_feature?(name)
|
11
|
+
enabled.include?(name.to_sym)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.enabled
|
15
|
+
if Lookbook.config.experimental_features == true
|
16
|
+
EXPERIMENTAL_FEATURES
|
17
|
+
elsif Lookbook.config.experimental_features.blank?
|
18
|
+
[]
|
19
|
+
else
|
20
|
+
Lookbook.config.experimental_features.map(&:to_sym)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
module Lookbook
|
2
|
+
module Params
|
3
|
+
class << self
|
4
|
+
def build_param(param, default)
|
5
|
+
input, options_str = param.text.present? ? param.text.split(" ", 2) : [nil, ""]
|
6
|
+
type = param.types&.first
|
7
|
+
options = YAML.safe_load(options_str || "~")
|
8
|
+
input ||= guess_input(type, default)
|
9
|
+
type ||= guess_type(input, default)
|
10
|
+
{
|
11
|
+
name: param.name,
|
12
|
+
input: input_text?(input) ? "text" : input,
|
13
|
+
input_type: (input if input_text?(input)),
|
14
|
+
options: options,
|
15
|
+
type: type,
|
16
|
+
default: default
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
def parse_method_param_str(param_str)
|
21
|
+
return nil if param_str[0].nil? || param_str[1].nil?
|
22
|
+
name = param_str[0].chomp(":")
|
23
|
+
value = param_str[1]&.strip
|
24
|
+
value = case value
|
25
|
+
when "nil"
|
26
|
+
nil
|
27
|
+
else
|
28
|
+
if value&.first == ":"
|
29
|
+
value.delete_prefix(":").to_sym
|
30
|
+
else
|
31
|
+
YAML.safe_load(value)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
[name, value]
|
35
|
+
end
|
36
|
+
|
37
|
+
def cast(value, type = "String")
|
38
|
+
case type.downcase
|
39
|
+
when "symbol"
|
40
|
+
value.delete_prefix(":").to_sym
|
41
|
+
when "hash"
|
42
|
+
result = safe_parse_yaml(value, {})
|
43
|
+
unless result.is_a? Hash
|
44
|
+
Lookbook.logger.debug "Failed to parse '#{value}' into a Hash"
|
45
|
+
result = {}
|
46
|
+
end
|
47
|
+
result
|
48
|
+
when "array"
|
49
|
+
result = safe_parse_yaml(value, [])
|
50
|
+
unless result.is_a? Array
|
51
|
+
Lookbook.logger.debug "Failed to parse '#{value}' into an Array"
|
52
|
+
result = []
|
53
|
+
end
|
54
|
+
result
|
55
|
+
else
|
56
|
+
begin
|
57
|
+
type_class = "ActiveModel::Type::#{type}".constantize
|
58
|
+
type_class.new.cast(value)
|
59
|
+
rescue NameError
|
60
|
+
raise ArgumentError, "'#{type}' is not a valid param type to cast to."
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def guess_input(type, default)
|
68
|
+
if type&.downcase == "boolean" || (type.blank? && boolean?(default))
|
69
|
+
"toggle"
|
70
|
+
else
|
71
|
+
"text"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def guess_type(input, default)
|
76
|
+
if input&.downcase == "toggle"
|
77
|
+
"Boolean"
|
78
|
+
elsif input&.downcase == "number"
|
79
|
+
"Integer"
|
80
|
+
elsif boolean?(default)
|
81
|
+
"Boolean"
|
82
|
+
elsif default.is_a? Symbol
|
83
|
+
"Symbol"
|
84
|
+
else
|
85
|
+
"String"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def input_text?(input)
|
90
|
+
[
|
91
|
+
"email",
|
92
|
+
"number",
|
93
|
+
"tel",
|
94
|
+
"text",
|
95
|
+
"url"
|
96
|
+
].include? input
|
97
|
+
end
|
98
|
+
|
99
|
+
def safe_parse_yaml(value, fallback)
|
100
|
+
value.present? ? YAML.safe_load(value) : fallback
|
101
|
+
rescue Psych::SyntaxError
|
102
|
+
fallback
|
103
|
+
end
|
104
|
+
|
105
|
+
def boolean?(value)
|
106
|
+
value == true || value == false
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
data/lib/lookbook/preview.rb
CHANGED
@@ -10,7 +10,7 @@ module Lookbook
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def id
|
13
|
-
path.underscore.tr("_", "-")
|
13
|
+
path.underscore.tr("/", "-").tr("_", "-")
|
14
14
|
end
|
15
15
|
|
16
16
|
def path
|
@@ -25,6 +25,12 @@ module Lookbook
|
|
25
25
|
@preview.display_params.merge(lookbook_display_params)
|
26
26
|
end
|
27
27
|
|
28
|
+
def params
|
29
|
+
@params || code_object&.tags("param")&.map do |param|
|
30
|
+
Lookbook::Params.build_param(param, parameter_defaults[param.name])
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
28
34
|
def method_source
|
29
35
|
code_object.source.split("\n")[1..-2].join("\n").strip_heredoc
|
30
36
|
end
|
@@ -55,6 +61,12 @@ module Lookbook
|
|
55
61
|
|
56
62
|
private
|
57
63
|
|
64
|
+
def parameter_defaults
|
65
|
+
@parameter_defaults || code_object&.parameters&.map do |param_str|
|
66
|
+
Lookbook::Params.parse_method_param_str(param_str)
|
67
|
+
end&.compact&.to_h
|
68
|
+
end
|
69
|
+
|
58
70
|
def taggable_object_path
|
59
71
|
"#{@preview.name}##{name}"
|
60
72
|
end
|
@@ -11,7 +11,7 @@ module Lookbook
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def id
|
14
|
-
path.underscore.tr("_", "-")
|
14
|
+
path.underscore.tr("/", "-").tr("_", "-")
|
15
15
|
end
|
16
16
|
|
17
17
|
def path
|
@@ -26,6 +26,10 @@ module Lookbook
|
|
26
26
|
:group
|
27
27
|
end
|
28
28
|
|
29
|
+
def params
|
30
|
+
[]
|
31
|
+
end
|
32
|
+
|
29
33
|
def hidden?
|
30
34
|
false
|
31
35
|
end
|
data/lib/lookbook/taggable.rb
CHANGED
@@ -27,8 +27,8 @@ module Lookbook
|
|
27
27
|
parts = tag.text.strip.match(/^([^\s]*)\s?(.*)$/)
|
28
28
|
if parts.present?
|
29
29
|
begin
|
30
|
-
display_params[parts[1]] =
|
31
|
-
rescue
|
30
|
+
display_params[parts[1]] = YAML.safe_load(parts[2] || "~")
|
31
|
+
rescue SyntaxError => err
|
32
32
|
Rails.logger.error("\n👀 [Lookbook] Invalid JSON in @display tag.\n👀 [Lookbook] (#{err})\n")
|
33
33
|
end
|
34
34
|
end
|
data/lib/lookbook/version.rb
CHANGED
data/lib/lookbook.rb
CHANGED
@@ -5,6 +5,8 @@ module Lookbook
|
|
5
5
|
extend ActiveSupport::Autoload
|
6
6
|
|
7
7
|
autoload :Lang, "lookbook/lang"
|
8
|
+
autoload :Params, "lookbook/params"
|
9
|
+
autoload :Features, "lookbook/features"
|
8
10
|
autoload :Collection, "lookbook/collection"
|
9
11
|
autoload :Parser, "lookbook/parser"
|
10
12
|
autoload :Preview, "lookbook/preview"
|
@@ -1,4 +1,4 @@
|
|
1
|
-
/*! tailwindcss v2.2.
|
1
|
+
/*! tailwindcss v2.2.19 | MIT License | https://tailwindcss.com */
|
2
2
|
|
3
3
|
/*! modern-normalize v1.1.0 | MIT License | https://github.com/sindresorhus/modern-normalize */
|
4
4
|
|
@@ -606,6 +606,9 @@ video {
|
|
606
606
|
--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));
|
607
607
|
--tw-border-opacity: 1;
|
608
608
|
border-color: rgba(229, 231, 235, var(--tw-border-opacity));
|
609
|
+
--tw-ring-offset-shadow: 0 0 #0000;
|
610
|
+
--tw-ring-shadow: 0 0 #0000;
|
611
|
+
--tw-shadow: 0 0 #0000;
|
609
612
|
--tw-ring-inset: var(--tw-empty,/*!*/ /*!*/);
|
610
613
|
--tw-ring-offset-width: 0px;
|
611
614
|
--tw-ring-offset-color: #fff;
|
@@ -820,12 +823,6 @@ pre[class*="language-"] {
|
|
820
823
|
fill: none;
|
821
824
|
}
|
822
825
|
|
823
|
-
.min-h-fill {
|
824
|
-
min-height: -webkit-fill-available;
|
825
|
-
min-height: -moz-available;
|
826
|
-
min-height: fill-available;
|
827
|
-
}
|
828
|
-
|
829
826
|
::-webkit-scrollbar {
|
830
827
|
width: 8px;
|
831
828
|
height: 8px;
|
@@ -1563,6 +1560,10 @@ pre[class*="language-"] {
|
|
1563
1560
|
display: flex;
|
1564
1561
|
}
|
1565
1562
|
|
1563
|
+
.inline-flex {
|
1564
|
+
display: inline-flex;
|
1565
|
+
}
|
1566
|
+
|
1566
1567
|
.hidden {
|
1567
1568
|
display: none;
|
1568
1569
|
}
|
@@ -1599,6 +1600,10 @@ pre[class*="language-"] {
|
|
1599
1600
|
height: 11px;
|
1600
1601
|
}
|
1601
1602
|
|
1603
|
+
.h-6 {
|
1604
|
+
height: 1.5rem;
|
1605
|
+
}
|
1606
|
+
|
1602
1607
|
.w-full {
|
1603
1608
|
width: 100%;
|
1604
1609
|
}
|
@@ -1631,10 +1636,22 @@ pre[class*="language-"] {
|
|
1631
1636
|
width: 1.25rem;
|
1632
1637
|
}
|
1633
1638
|
|
1639
|
+
.w-\[200px\] {
|
1640
|
+
width: 200px;
|
1641
|
+
}
|
1642
|
+
|
1643
|
+
.w-11 {
|
1644
|
+
width: 2.75rem;
|
1645
|
+
}
|
1646
|
+
|
1634
1647
|
.max-w-xs {
|
1635
1648
|
max-width: 20rem;
|
1636
1649
|
}
|
1637
1650
|
|
1651
|
+
.max-w-\[800px\] {
|
1652
|
+
max-width: 800px;
|
1653
|
+
}
|
1654
|
+
|
1638
1655
|
.flex-none {
|
1639
1656
|
flex: none;
|
1640
1657
|
}
|
@@ -1643,6 +1660,10 @@ pre[class*="language-"] {
|
|
1643
1660
|
flex: 1 1 auto;
|
1644
1661
|
}
|
1645
1662
|
|
1663
|
+
.flex-shrink-0 {
|
1664
|
+
flex-shrink: 0;
|
1665
|
+
}
|
1666
|
+
|
1646
1667
|
.flex-grow {
|
1647
1668
|
flex-grow: 1;
|
1648
1669
|
}
|
@@ -1657,6 +1678,16 @@ pre[class*="language-"] {
|
|
1657
1678
|
transform: var(--tw-transform);
|
1658
1679
|
}
|
1659
1680
|
|
1681
|
+
.translate-x-5 {
|
1682
|
+
--tw-translate-x: 1.25rem;
|
1683
|
+
transform: var(--tw-transform);
|
1684
|
+
}
|
1685
|
+
|
1686
|
+
.translate-x-0 {
|
1687
|
+
--tw-translate-x: 0px;
|
1688
|
+
transform: var(--tw-transform);
|
1689
|
+
}
|
1690
|
+
|
1660
1691
|
.transform {
|
1661
1692
|
transform: var(--tw-transform);
|
1662
1693
|
}
|
@@ -1692,6 +1723,10 @@ pre[class*="language-"] {
|
|
1692
1723
|
flex-direction: column;
|
1693
1724
|
}
|
1694
1725
|
|
1726
|
+
.items-start {
|
1727
|
+
align-items: flex-start;
|
1728
|
+
}
|
1729
|
+
|
1695
1730
|
.items-center {
|
1696
1731
|
align-items: center;
|
1697
1732
|
}
|
@@ -1751,6 +1786,10 @@ pre[class*="language-"] {
|
|
1751
1786
|
white-space: nowrap;
|
1752
1787
|
}
|
1753
1788
|
|
1789
|
+
.rounded-full {
|
1790
|
+
border-radius: 9999px;
|
1791
|
+
}
|
1792
|
+
|
1754
1793
|
.rounded-b {
|
1755
1794
|
border-bottom-right-radius: 0.25rem;
|
1756
1795
|
border-bottom-left-radius: 0.25rem;
|
@@ -1768,6 +1807,10 @@ pre[class*="language-"] {
|
|
1768
1807
|
border-width: 1px;
|
1769
1808
|
}
|
1770
1809
|
|
1810
|
+
.border-2 {
|
1811
|
+
border-width: 2px;
|
1812
|
+
}
|
1813
|
+
|
1771
1814
|
.border-r {
|
1772
1815
|
border-right-width: 1px;
|
1773
1816
|
}
|
@@ -1835,6 +1878,16 @@ pre[class*="language-"] {
|
|
1835
1878
|
background-color: rgba(249, 250, 251, var(--tw-bg-opacity));
|
1836
1879
|
}
|
1837
1880
|
|
1881
|
+
.bg-indigo-500 {
|
1882
|
+
--tw-bg-opacity: 1;
|
1883
|
+
background-color: rgba(99, 102, 241, var(--tw-bg-opacity));
|
1884
|
+
}
|
1885
|
+
|
1886
|
+
.bg-gray-300 {
|
1887
|
+
--tw-bg-opacity: 1;
|
1888
|
+
background-color: rgba(209, 213, 219, var(--tw-bg-opacity));
|
1889
|
+
}
|
1890
|
+
|
1838
1891
|
.p-4 {
|
1839
1892
|
padding: 1rem;
|
1840
1893
|
}
|
@@ -1882,6 +1935,11 @@ pre[class*="language-"] {
|
|
1882
1935
|
padding-bottom: 0px;
|
1883
1936
|
}
|
1884
1937
|
|
1938
|
+
.py-3 {
|
1939
|
+
padding-top: 0.75rem;
|
1940
|
+
padding-bottom: 0.75rem;
|
1941
|
+
}
|
1942
|
+
|
1885
1943
|
.pr-3 {
|
1886
1944
|
padding-right: 0.75rem;
|
1887
1945
|
}
|
@@ -2000,11 +2058,22 @@ pre[class*="language-"] {
|
|
2000
2058
|
opacity: 0.5;
|
2001
2059
|
}
|
2002
2060
|
|
2061
|
+
.shadow {
|
2062
|
+
--tw-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
|
2063
|
+
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
|
2064
|
+
}
|
2065
|
+
|
2003
2066
|
.outline-none {
|
2004
2067
|
outline: 2px solid transparent;
|
2005
2068
|
outline-offset: 2px;
|
2006
2069
|
}
|
2007
2070
|
|
2071
|
+
.ring-0 {
|
2072
|
+
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
|
2073
|
+
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color);
|
2074
|
+
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
|
2075
|
+
}
|
2076
|
+
|
2008
2077
|
.blur {
|
2009
2078
|
--tw-blur: blur(8px);
|
2010
2079
|
filter: var(--tw-filter);
|
@@ -2022,6 +2091,44 @@ pre[class*="language-"] {
|
|
2022
2091
|
transition-duration: 150ms;
|
2023
2092
|
}
|
2024
2093
|
|
2094
|
+
.transition-colors {
|
2095
|
+
transition-property: background-color, border-color, color, fill, stroke;
|
2096
|
+
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
2097
|
+
transition-duration: 150ms;
|
2098
|
+
}
|
2099
|
+
|
2100
|
+
.duration-200 {
|
2101
|
+
transition-duration: 200ms;
|
2102
|
+
}
|
2103
|
+
|
2104
|
+
.ease-in-out {
|
2105
|
+
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
2106
|
+
}
|
2107
|
+
|
2108
|
+
.min-h-fill {
|
2109
|
+
min-height: -webkit-fill-available;
|
2110
|
+
min-height: -moz-available;
|
2111
|
+
min-height: fill-available;
|
2112
|
+
}
|
2113
|
+
|
2114
|
+
.form-input {
|
2115
|
+
width: 100%;
|
2116
|
+
border-radius: 0.125rem;
|
2117
|
+
--tw-border-opacity: 1;
|
2118
|
+
border-color: rgba(209, 213, 219, var(--tw-border-opacity));
|
2119
|
+
font-size: 0.875rem;
|
2120
|
+
line-height: 1.25rem;
|
2121
|
+
--tw-text-opacity: 1;
|
2122
|
+
color: rgba(55, 65, 81, var(--tw-text-opacity));
|
2123
|
+
}
|
2124
|
+
|
2125
|
+
.form-input:focus {
|
2126
|
+
--tw-border-opacity: 1;
|
2127
|
+
border-color: rgba(165, 180, 252, var(--tw-border-opacity));
|
2128
|
+
--tw-ring-opacity: 1;
|
2129
|
+
--tw-ring-color: rgba(165, 180, 252, var(--tw-ring-opacity));
|
2130
|
+
}
|
2131
|
+
|
2025
2132
|
.tippy-box[data-animation=fade][data-state=hidden]{
|
2026
2133
|
opacity:0
|
2027
2134
|
}
|
@@ -2463,6 +2570,21 @@ pre[class*="language-"] {
|
|
2463
2570
|
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
|
2464
2571
|
}
|
2465
2572
|
|
2573
|
+
.focus\:ring-2:focus {
|
2574
|
+
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
|
2575
|
+
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
|
2576
|
+
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
|
2577
|
+
}
|
2578
|
+
|
2579
|
+
.focus\:ring-indigo-400:focus {
|
2580
|
+
--tw-ring-opacity: 1;
|
2581
|
+
--tw-ring-color: rgba(129, 140, 248, var(--tw-ring-opacity));
|
2582
|
+
}
|
2583
|
+
|
2584
|
+
.focus\:ring-offset-2:focus {
|
2585
|
+
--tw-ring-offset-width: 2px;
|
2586
|
+
}
|
2587
|
+
|
2466
2588
|
.group:hover .group-hover\:text-indigo-800 {
|
2467
2589
|
--tw-text-opacity: 1;
|
2468
2590
|
color: rgba(55, 48, 163, var(--tw-text-opacity));
|
@@ -7333,7 +7333,9 @@ Expression: "${expression}"
|
|
7333
7333
|
var module_default3 = src_default3;
|
7334
7334
|
|
7335
7335
|
// node_modules/@ryangjchandler/alpine-clipboard/src/index.js
|
7336
|
-
|
7336
|
+
var onCopy = () => {
|
7337
|
+
};
|
7338
|
+
function Clipboard(Alpine3) {
|
7337
7339
|
Alpine3.magic("clipboard", () => {
|
7338
7340
|
return function(target) {
|
7339
7341
|
if (typeof target === "function") {
|
@@ -7342,10 +7344,17 @@ Expression: "${expression}"
|
|
7342
7344
|
if (typeof target === "object") {
|
7343
7345
|
target = JSON.stringify(target);
|
7344
7346
|
}
|
7345
|
-
return window.navigator.clipboard.writeText(target);
|
7347
|
+
return window.navigator.clipboard.writeText(target).then(onCopy);
|
7346
7348
|
};
|
7347
7349
|
});
|
7348
7350
|
}
|
7351
|
+
Clipboard.configure = (config) => {
|
7352
|
+
if (config.hasOwnProperty("onCopy") && typeof config.onCopy === "function") {
|
7353
|
+
onCopy = config.onCopy;
|
7354
|
+
}
|
7355
|
+
return Clipboard;
|
7356
|
+
};
|
7357
|
+
var src_default4 = Clipboard;
|
7349
7358
|
|
7350
7359
|
// app/assets/lookbook/js/utils/screen.js
|
7351
7360
|
function screen_default(Alpine3) {
|
@@ -8359,6 +8368,9 @@ Expression: "${expression}"
|
|
8359
8368
|
if (fromEl.isEqualNode(toEl)) {
|
8360
8369
|
return false;
|
8361
8370
|
}
|
8371
|
+
if (fromEl.hasAttribute("skip-morph")) {
|
8372
|
+
return false;
|
8373
|
+
}
|
8362
8374
|
return true;
|
8363
8375
|
}
|
8364
8376
|
}, opts));
|
@@ -8395,7 +8407,12 @@ Expression: "${expression}"
|
|
8395
8407
|
render() {
|
8396
8408
|
if (this.ready) {
|
8397
8409
|
morph_default(this.$el, store2.doc.getElementById(this.$el.id));
|
8410
|
+
this.$dispatch("document:patched");
|
8398
8411
|
}
|
8412
|
+
},
|
8413
|
+
navigateTo(path2) {
|
8414
|
+
history.pushState({}, null, path2);
|
8415
|
+
this.$dispatch("popstate");
|
8399
8416
|
}
|
8400
8417
|
};
|
8401
8418
|
}
|
@@ -8469,6 +8486,27 @@ Expression: "${expression}"
|
|
8469
8486
|
};
|
8470
8487
|
}
|
8471
8488
|
|
8489
|
+
// app/assets/lookbook/js/workbench/param.js
|
8490
|
+
function param() {
|
8491
|
+
return {
|
8492
|
+
focused: false,
|
8493
|
+
setFocus() {
|
8494
|
+
if (this.focused && this.$el.focus) {
|
8495
|
+
this.$el.focus();
|
8496
|
+
}
|
8497
|
+
},
|
8498
|
+
update(name, value) {
|
8499
|
+
const searchParams = new URLSearchParams(window.location.search);
|
8500
|
+
searchParams.set(name, value);
|
8501
|
+
const path2 = location.href.replace(location.search, "");
|
8502
|
+
this.navigateTo(`${path2}?${searchParams.toString()}`);
|
8503
|
+
},
|
8504
|
+
validate() {
|
8505
|
+
return this.$el.reportValidity ? this.$el.reportValidity() : true;
|
8506
|
+
}
|
8507
|
+
};
|
8508
|
+
}
|
8509
|
+
|
8472
8510
|
// app/assets/lookbook/js/nav.js
|
8473
8511
|
function nav_default() {
|
8474
8512
|
return {
|
@@ -8492,13 +8530,12 @@ Expression: "${expression}"
|
|
8492
8530
|
});
|
8493
8531
|
},
|
8494
8532
|
navigate(path2) {
|
8495
|
-
|
8496
|
-
path2 = path2.currentTarget.href;
|
8497
|
-
}
|
8498
|
-
history.pushState({}, null, path2);
|
8499
|
-
this.$dispatch("popstate");
|
8533
|
+
this.navigateTo(path2 instanceof Event ? path2.currentTarget.href : path2);
|
8500
8534
|
},
|
8501
|
-
focusFilter() {
|
8535
|
+
focusFilter($event) {
|
8536
|
+
if ($event.target.tagName === "INPUT") {
|
8537
|
+
return;
|
8538
|
+
}
|
8502
8539
|
this.currentFocus = this.$refs.filter;
|
8503
8540
|
setTimeout(() => this.$refs.filter.focus(), 0);
|
8504
8541
|
},
|
@@ -8666,6 +8703,7 @@ Expression: "${expression}"
|
|
8666
8703
|
module_default.data("workbench", workbench);
|
8667
8704
|
module_default.data("preview", preview);
|
8668
8705
|
module_default.data("inspector", inspector);
|
8706
|
+
module_default.data("param", param);
|
8669
8707
|
module_default.data("clipboard", clipboard);
|
8670
8708
|
module_default.data("sizeObserver", sizeObserver);
|
8671
8709
|
module_default.data("split", split_default);
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lookbook
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.5
|
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-11-
|
11
|
+
date: 2021-11-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -202,11 +202,13 @@ files:
|
|
202
202
|
- app/assets/lookbook/js/utils/split.js
|
203
203
|
- app/assets/lookbook/js/workbench.js
|
204
204
|
- app/assets/lookbook/js/workbench/inspector.js
|
205
|
+
- app/assets/lookbook/js/workbench/param.js
|
205
206
|
- app/assets/lookbook/js/workbench/preview.js
|
206
207
|
- app/channels/lookbook/connection.rb
|
207
208
|
- app/channels/lookbook/reload_channel.rb
|
208
209
|
- app/controllers/lookbook/app_controller.rb
|
209
210
|
- app/helpers/lookbook/application_helper.rb
|
211
|
+
- app/helpers/lookbook/preview_helper.rb
|
210
212
|
- app/views/lookbook/app/error.html.erb
|
211
213
|
- app/views/lookbook/app/index.html.erb
|
212
214
|
- app/views/lookbook/app/not_found.html.erb
|
@@ -226,13 +228,20 @@ files:
|
|
226
228
|
- app/views/lookbook/workbench/_preview.html.erb
|
227
229
|
- app/views/lookbook/workbench/inspector/_code.html.erb
|
228
230
|
- app/views/lookbook/workbench/inspector/_notes.html.erb
|
231
|
+
- app/views/lookbook/workbench/inspector/_params.html.erb
|
229
232
|
- app/views/lookbook/workbench/inspector/_plain.html.erb
|
233
|
+
- app/views/lookbook/workbench/inspector/params/_select.html.erb
|
234
|
+
- app/views/lookbook/workbench/inspector/params/_text.html.erb
|
235
|
+
- app/views/lookbook/workbench/inspector/params/_textarea.html.erb
|
236
|
+
- app/views/lookbook/workbench/inspector/params/_toggle.html.erb
|
230
237
|
- config/routes.rb
|
231
238
|
- lib/lookbook.rb
|
232
239
|
- lib/lookbook/collection.rb
|
233
240
|
- lib/lookbook/engine.rb
|
241
|
+
- lib/lookbook/features.rb
|
234
242
|
- lib/lookbook/lang.rb
|
235
243
|
- lib/lookbook/null_logger.rb
|
244
|
+
- lib/lookbook/params.rb
|
236
245
|
- lib/lookbook/parser.rb
|
237
246
|
- lib/lookbook/preview.rb
|
238
247
|
- lib/lookbook/preview_controller.rb
|
@@ -263,7 +272,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
263
272
|
- !ruby/object:Gem::Version
|
264
273
|
version: '0'
|
265
274
|
requirements: []
|
266
|
-
rubygems_version: 3.
|
275
|
+
rubygems_version: 3.2.22
|
267
276
|
signing_key:
|
268
277
|
specification_version: 4
|
269
278
|
summary: A native development UI for ViewComponent
|