lookbook 0.4.3 → 0.4.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +223 -54
  3. data/app/assets/lookbook/css/app.css +64 -8
  4. data/app/assets/lookbook/js/app.js +39 -53
  5. data/app/assets/lookbook/js/components/copy.js +16 -0
  6. data/app/assets/lookbook/js/components/filter.js +24 -0
  7. data/app/assets/lookbook/js/components/inspector.js +21 -0
  8. data/app/assets/lookbook/js/{nav/node.js → components/nav-group.js} +16 -15
  9. data/app/assets/lookbook/js/components/nav-item.js +26 -0
  10. data/app/assets/lookbook/js/components/nav.js +35 -0
  11. data/app/assets/lookbook/js/components/page.js +33 -0
  12. data/app/assets/lookbook/js/components/param.js +18 -0
  13. data/app/assets/lookbook/js/{workbench/preview.js → components/preview-window.js} +9 -10
  14. data/app/assets/lookbook/js/components/sidebar.js +3 -0
  15. data/app/assets/lookbook/js/components/sizes.js +16 -0
  16. data/app/assets/lookbook/js/components/splitter.js +25 -0
  17. data/app/assets/lookbook/js/config.js +14 -0
  18. data/app/assets/lookbook/js/{utils/reloader.js → lib/socket.js} +7 -12
  19. data/app/assets/lookbook/js/lib/split.js +21 -0
  20. data/app/assets/lookbook/js/lib/utils.js +3 -0
  21. data/app/assets/lookbook/js/stores/filter.js +11 -0
  22. data/app/assets/lookbook/js/stores/inspector.js +17 -0
  23. data/app/assets/lookbook/js/stores/layout.js +12 -0
  24. data/app/assets/lookbook/js/stores/nav.js +21 -0
  25. data/app/assets/lookbook/js/stores/sidebar.js +14 -0
  26. data/app/controllers/lookbook/app_controller.rb +82 -87
  27. data/app/helpers/lookbook/application_helper.rb +49 -5
  28. data/app/helpers/lookbook/preview_helper.rb +7 -0
  29. data/app/views/layouts/lookbook/app.html.erb +54 -0
  30. data/app/views/layouts/lookbook/preview.html.erb +12 -0
  31. data/app/views/lookbook/components/_code.html.erb +8 -0
  32. data/app/views/lookbook/{shared/_clipboard.html.erb → components/_copy.html.erb} +4 -5
  33. data/app/views/lookbook/components/_filter.html.erb +15 -0
  34. data/app/views/lookbook/{shared → components}/_header.html.erb +3 -3
  35. data/app/views/lookbook/components/_icon.html.erb +5 -0
  36. data/app/views/lookbook/components/_nav.html.erb +17 -0
  37. data/app/views/lookbook/components/_nav_collection.html.erb +5 -0
  38. data/app/views/lookbook/components/_nav_group.html.erb +17 -0
  39. data/app/views/lookbook/components/_nav_item.html.erb +21 -0
  40. data/app/views/lookbook/components/_nav_preview.html.erb +11 -0
  41. data/app/views/lookbook/components/_param.html.erb +20 -0
  42. data/app/views/lookbook/{workbench → components}/_preview.html.erb +8 -8
  43. data/app/views/lookbook/{app/error.html.erb → error.html.erb} +0 -0
  44. data/app/views/lookbook/index.html.erb +9 -0
  45. data/app/views/lookbook/inputs/_select.html.erb +8 -0
  46. data/app/views/lookbook/inputs/_text.html.erb +8 -0
  47. data/app/views/lookbook/inputs/_textarea.html.erb +8 -0
  48. data/app/views/lookbook/inputs/_toggle.html.erb +13 -0
  49. data/app/views/lookbook/{app/not_found.html.erb → not_found.html.erb} +2 -4
  50. data/app/views/lookbook/panels/_notes.html.erb +25 -0
  51. data/app/views/lookbook/panels/_output.html.erb +18 -0
  52. data/app/views/lookbook/panels/_params.html.erb +17 -0
  53. data/app/views/lookbook/panels/_source.html.erb +20 -0
  54. data/app/views/lookbook/show.html.erb +90 -0
  55. data/lib/lookbook/code_formatter.rb +20 -0
  56. data/lib/lookbook/engine.rb +10 -1
  57. data/lib/lookbook/features.rb +24 -0
  58. data/lib/lookbook/lang.rb +10 -5
  59. data/lib/lookbook/params.rb +110 -0
  60. data/lib/lookbook/parser.rb +1 -1
  61. data/lib/lookbook/preview.rb +1 -1
  62. data/lib/lookbook/preview_controller.rb +1 -1
  63. data/lib/lookbook/preview_example.rb +13 -1
  64. data/lib/lookbook/preview_group.rb +9 -1
  65. data/lib/lookbook/taggable.rb +2 -2
  66. data/lib/lookbook/version.rb +1 -1
  67. data/lib/lookbook.rb +3 -0
  68. data/public/lookbook-assets/css/app.css +2 -0
  69. data/public/lookbook-assets/css/app.css.map +1 -0
  70. data/public/lookbook-assets/js/app.js +2 -0
  71. data/public/lookbook-assets/js/app.js.map +1 -0
  72. metadata +58 -38
  73. data/app/assets/lookbook/js/nav/leaf.js +0 -20
  74. data/app/assets/lookbook/js/nav.js +0 -39
  75. data/app/assets/lookbook/js/page.js +0 -33
  76. data/app/assets/lookbook/js/utils/clipboard.js +0 -13
  77. data/app/assets/lookbook/js/utils/morph.js +0 -16
  78. data/app/assets/lookbook/js/utils/screen.js +0 -44
  79. data/app/assets/lookbook/js/utils/size_observer.js +0 -16
  80. data/app/assets/lookbook/js/utils/split.js +0 -26
  81. data/app/assets/lookbook/js/workbench/inspector.js +0 -11
  82. data/app/assets/lookbook/js/workbench.js +0 -14
  83. data/app/views/lookbook/app/index.html.erb +0 -11
  84. data/app/views/lookbook/app/show.html.erb +0 -1
  85. data/app/views/lookbook/layouts/app.html.erb +0 -41
  86. data/app/views/lookbook/nav/_collection.html.erb +0 -5
  87. data/app/views/lookbook/nav/_leaf.html.erb +0 -22
  88. data/app/views/lookbook/nav/_node.html.erb +0 -19
  89. data/app/views/lookbook/nav/_preview.html.erb +0 -11
  90. data/app/views/lookbook/preview/group.html.erb +0 -8
  91. data/app/views/lookbook/shared/_sidebar.html.erb +0 -45
  92. data/app/views/lookbook/shared/_workbench.html.erb +0 -12
  93. data/app/views/lookbook/workbench/_header.html.erb +0 -39
  94. data/app/views/lookbook/workbench/_inspector.html.erb +0 -33
  95. data/app/views/lookbook/workbench/inspector/_code.html.erb +0 -3
  96. data/app/views/lookbook/workbench/inspector/_notes.html.erb +0 -24
  97. data/app/views/lookbook/workbench/inspector/_plain.html.erb +0 -3
  98. data/public/lookbook-assets/app.css +0 -2504
  99. data/public/lookbook-assets/app.js +0 -8680
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2f5cf46a07c480d8c8c3fe5f0a521ce01a316985f458becf36fb4c73c871f7cf
4
- data.tar.gz: 2474c4105e357d9762c5228cee1190194a3b3069c6a9a9dc2afe0cf287339efe
3
+ metadata.gz: ce922c11cf8df5a1161c555189786265e51e29881c5a5e0948153fa65e855a50
4
+ data.tar.gz: 38fe97d9e04c6505ebc8e1b8bc877fe761db215b525117b42a044cadf7b2eb50
5
5
  SHA512:
6
- metadata.gz: 439aad3227d8e1e3c8d0202c4edf6b93983a2182c7a8d32c120c47f7fcd1c1dcdff675084029172c200d25fe052916b5cdcb812acd378b37af5e793ce5bea613
7
- data.tar.gz: ab031ca977f442a1203fb059df65fe763db9e4877eeb594e44b09c63b2f0bd58bc7e6ab409ef2a11d7f33fe5b5a90f8b30a0eadd5114566c619dcc9b62be8a1e
6
+ metadata.gz: d319e159fc2b87e710532dbd6c426360f2a6173e174d3e0eac05c677bdfd945cf0c5a391c426a8f987cb7539797aa10241e45cdddd55048f25af84689d0af9b2
7
+ data.tar.gz: 80a217bf6303a778676872bd9d6c1412a3811323903031d491e945c0788ebba20cc2ccbb059d011e823b80d74b5342994c9b42f7590fd4fbfd305166b187d345
data/README.md CHANGED
@@ -28,12 +28,15 @@ 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
 
34
35
  If you want to have a quick play with Lookbook, the easiest way is to [give the demo app](https://github.com/allmarkedup/lookbook-demo) a spin. It's a basic Rails/ViewComponent app with a few test components included to tinker with.
35
36
 
36
- The [demo app repo](https://github.com/allmarkedup/lookbook-demo) contains instructions on how to get it up and running.
37
+ **Online demo: https://lookbook-demo-app.herokuapp.com/lookbook**
38
+
39
+ If you'd rather dig in a bit more and run the demo app locally, the [demo repo](https://github.com/allmarkedup/lookbook-demo) contains instructions on how to get it up and running.
37
40
 
38
41
  ## Installing
39
42
 
@@ -76,7 +79,7 @@ Lookbook parses [Yard-style comment tags](https://rubydoc.info/gems/yard/file/do
76
79
 
77
80
  ```ruby
78
81
  # @label Basic Button
79
- # @display bg_color "#fff"
82
+ # @display bg_color #fff
80
83
  class ButtonComponentPreview < ViewComponent::Preview
81
84
 
82
85
  # Primary button
@@ -90,11 +93,24 @@ class ButtonComponentPreview < ViewComponent::Preview
90
93
  end
91
94
  end
92
95
 
96
+ # Button with icon
97
+ # ----------------
98
+ # This example uses dynamic preview parameters
99
+ # which can be edited live in the Lookbook UI
100
+ #
101
+ # @param text
102
+ # @param icon select [heart, cog, alert]
103
+ def icon(text: "Spread the love", icon: "heart")
104
+ render ButtonComponent.new(icon: icon) do
105
+ text
106
+ end
107
+ end
108
+
93
109
  # Inverted button
94
110
  # ---------------
95
111
  # For light-on-dark screens
96
112
  #
97
- # @display bg_color "#000"
113
+ # @display bg_color #000
98
114
  def secondary
99
115
  render ButtonComponent.new(style: :inverted) do
100
116
  "Click me"
@@ -141,54 +157,41 @@ end
141
157
 
142
158
  The following Lookbook-specific tags are available for use:
143
159
 
144
- * `@label <label>` -[Customise navigation labels](#-label-text)
145
- * `@hidden` - [Prevent items displaying in the navigation](#-hidden)
146
- * `@display <key> <value>` - [Specify params to pass into the preview template](#-display-key-value)
147
- * `@!group <name> ... @!endgroup` - [Render examples in a group on the same page](#-group-name--endgroup)
160
+ * [`@label`](#label-tag)
161
+ * [`@display`](#display-tag)
162
+ * [`@!group ... @!endgroup`](#group-tag)
163
+ * [`@hidden`](#hidden-tag)
164
+ * [`@param`](#param-tag) [⚠️ **experimental!** - requires [feature opt-in](#experimental-features) ⚠️]
148
165
 
149
- ### 🔖 `@label <text>`
166
+ <h3 id="label-tag">🏷 @label</h3>
150
167
 
151
168
  Used to replace the auto-generated navigation label for the item with `<text>`.
152
169
 
153
- > Available for preview classes & example methods.
154
-
155
170
  ```ruby
156
- # @label Preview Label
157
- class FooComponentPreview < ViewComponent::Preview
158
-
159
- # @label Example Label
160
- def default
161
- end
162
- end
171
+ @label <text>
163
172
  ```
164
173
 
165
- ### 🔖 `@hidden`
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.
174
+ > Available for preview classes & example methods.
172
175
 
173
176
  ```ruby
174
- # @hidden
177
+ # @label Preview Label
175
178
  class FooComponentPreview < ViewComponent::Preview
176
179
 
177
- # @hidden
180
+ # @label Example Label
178
181
  def default
179
182
  end
180
183
  end
181
184
  ```
182
185
 
183
- ### 🔖 `@display <key> <value>`
186
+ <h3 id="display-tag">🏷 @display</h3>
184
187
 
185
188
  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
189
 
187
190
  ```ruby
188
- # @display bg_color "#eee"
191
+ # @display bg_color #eee
189
192
  class FooComponentPreview < ViewComponent::Preview
190
193
 
191
- # @display max_width "500px"
194
+ # @display max_width 500px
192
195
  # @display wrapper true
193
196
  def default
194
197
  end
@@ -198,13 +201,11 @@ end
198
201
  The `@display` tag can be applied at the preview (class) or at the example (method) level, and takes the following format:
199
202
 
200
203
  ```ruby
201
- # @display <key> <value>
204
+ @display <key> <value>
202
205
  ```
203
206
 
204
207
  - `<key>` must be a valid Ruby hash key name, without quotes or spaces
205
- - `<value>` must be a valid JSON-parsable value. It can be a string (surrounded by **double** quotes), a boolean or an integer.
206
-
207
- > [See below for some examples](#some-display-value-examples) of valid and invalid `@display` values.
208
+ - `<value>` will be parsed using the [Ruby YAML parser](https://yaml.org/YAML_for_ruby.html) to resolve the value
208
209
 
209
210
  These display parameters can then be accessed via the `params` hash in your preview layout using `params[:lookbook][:display][<key>]`:
210
211
 
@@ -244,27 +245,7 @@ config.lookbook.preview_display_params = {
244
245
 
245
246
  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
247
 
247
- #### Some `@display` value examples:
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`
248
+ <h3 id="group-tag">🔖 `@!group ... @!endgroup`</h3>
268
249
 
269
250
  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
251
 
@@ -310,6 +291,167 @@ The example above would display the `Sizes` examples grouped together on a singl
310
291
 
311
292
  You can have as many groups as you like within a single preview class, but each example can only belong to one group.
312
293
 
294
+ <h3 id="hidden-tag">🏷 `@hidden`</h3>
295
+
296
+ Used to temporarily exclude an item from the Lookbook navigation. The item will still be accessible via it's URL.
297
+
298
+ 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.
299
+
300
+ > Available for both preview classes & example methods.
301
+
302
+ ```ruby
303
+ # @hidden
304
+ class FooComponentPreview < ViewComponent::Preview
305
+
306
+ # @hidden
307
+ def default
308
+ end
309
+ end
310
+ ```
311
+
312
+ <h3 id="param-tag"> 🚧 @param (experimental)</h3>
313
+
314
+ > ⚠️ 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.
315
+
316
+ 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.
317
+
318
+ 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.
319
+
320
+ The `@param` tag takes the following format:
321
+
322
+ ```ruby
323
+ @param <name> <input_type> <opts?>
324
+ ```
325
+
326
+ - `<name>` - name of the dynamic preview param
327
+ - `<input_type>` - input field type to generate in the UI
328
+ - `<opts?>` - YAML-encoded field options, used for some field types
329
+
330
+ #### Input types
331
+
332
+ The following **input field types** are available for use:
333
+
334
+ 📝 **Text-style inputs** - Single line fields, useful for short strings of text or numbers.
335
+
336
+ ```ruby
337
+ @param <name> text
338
+ @param <name> email
339
+ @param <name> number
340
+ @param <name> url
341
+ @param <name> tel
342
+ ```
343
+
344
+ > The above types only differ in the validation constraints they impose on the input field.
345
+
346
+ 📝 **Textarea** - Multi-line textarea field for longer-form content.
347
+
348
+ ```ruby
349
+ @param <name> textarea
350
+ ```
351
+
352
+ 📝 **Select box** - Dropdown select field for selecting from a list of known options.
353
+
354
+ ```ruby
355
+ @param <name> select <options>
356
+ ```
357
+
358
+ `<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:
359
+
360
+ ```ruby
361
+ # Basic options:
362
+ # @param theme select [primary, secondary, danger]
363
+
364
+ # With custom labels (each item itself an array of [label, value]):
365
+ # @param theme select [[Primary theme, primary], [Secondary theme, secondary], [Danger theme, danger]]
366
+
367
+ # With empty option (`~` in YAML)
368
+ # @param theme select [~, primary, secondary, danger]
369
+ ```
370
+
371
+ > **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.
372
+
373
+ 📝 **Toggle** - On/off switch for toggling boolean values.
374
+
375
+ ```ruby
376
+ @param <name> toggle
377
+ ```
378
+
379
+ #### Default values
380
+
381
+ Default values are specified as part of the preview example method parameters in the usual Ruby way:
382
+
383
+ ```ruby
384
+ def button(content: "Click me", theme: "primary", arrow: false)
385
+ # ...
386
+ end
387
+ ```
388
+
389
+ These will be used as the default values for the param fields.
390
+
391
+ > 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.
392
+
393
+ #### Type casting values
394
+
395
+ Most dynamic param values are passed to the example method as strings, with the following exceptions:
396
+
397
+ - `toggle` input - values are cast to booleans
398
+ - `number` input - values are cast to integers
399
+
400
+ 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.
401
+
402
+ To help with this, a `type` option can be specified in the `@param` definition to automatically cast the dynamic value to a different type:
403
+
404
+ ```ruby
405
+ # @param <name> [<type>] <input_type> <opts?>
406
+ ```
407
+
408
+ 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.
409
+
410
+ ```ruby
411
+ # @param theme [Symbol] select [primary, secondary, danger]
412
+ def default(theme: :primary)
413
+ render Elements::ButtonComponent.new(theme: theme) do
414
+ "Click me"
415
+ end
416
+ end
417
+ ```
418
+
419
+ The supported types to cast to are:
420
+
421
+ - `String` - *default for all except `toggle` inputs*
422
+ - `Boolean` - *default for `toggle` inputs*
423
+ - `Symbol`
424
+ - `Date`
425
+ - `DateTime`
426
+ - `Integer`
427
+ - `Float`
428
+
429
+ The following structured types are also available but should be considered **experimental** - you may run into bugs!
430
+
431
+ - `Hash` - *value string converted to Hash using the Ruby YAML parser*
432
+ - `Array` - *value string converted to Array using the Ruby YAML parser*
433
+
434
+ #### Full example:
435
+
436
+ ```ruby
437
+ class ButtonComponentPreview < ViewComponent::Preview
438
+
439
+ # The params defined below will be editable in the UI:
440
+ #
441
+ # @param content text
442
+ # @param theme select [primary, secondary, danger]
443
+ # @param arrow toggle
444
+ def default(content: "Click me", theme: "primary", arrow: true)
445
+ render Elements::ButtonComponent.new(theme: theme, arrow: arrow) do
446
+ content
447
+ end
448
+ end
449
+
450
+ end
451
+ ```
452
+
453
+ <img src=".github/assets/dynamic_params.png">
454
+
313
455
  ### Adding notes
314
456
 
315
457
  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 +494,33 @@ If you wish to add additional paths to listen for changes in, you can use the `l
352
494
  config.lookbook.listen_paths << Rails.root.join('app/other/directory')
353
495
  ```
354
496
 
497
+ <h3 id="experimental-features">Experimental features opt-in</h3>
498
+
499
+ 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.
500
+
501
+ > ⚠️ **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'. ⚠️
502
+
503
+ #### Opting into specific features (recommended)
504
+
505
+ To opt into individual experimental features, include the name of the feature in the `experimental_features` config option:
506
+
507
+ ```ruby
508
+ config.lookbook.experimental_features = ["feature_name"]
509
+ ```
510
+
511
+ The current experimental features that can be opted into are:
512
+
513
+ - `params`: Live-editable, dynamic preview parameters ([read more](#param-tag)). Include `"params"` in the `experimental_features` config option to opt in.
514
+
515
+ #### Opting into all experimental features (not recommended!)
516
+
517
+ If you want to live life on the bleeding-edge you can opt-in to all current **and future** experimental features (usual caveats apply):
518
+
519
+ ```ruby
520
+ config.lookbook.experimental_features = true
521
+ ```
522
+
523
+
355
524
  ## Keyboard shortcuts
356
525
 
357
526
  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,67 @@
63
55
  @apply bg-gray-400;
64
56
  }
65
57
  }
58
+
59
+ @layer components {
60
+ #nav > ul > li {
61
+ @apply py-1;
62
+ }
63
+
64
+ .nav-toggle {
65
+ @apply flex items-center cursor-pointer pr-3 hover:bg-gray-200 hover:bg-opacity-50;
66
+ }
67
+
68
+ .nav-label {
69
+ @apply truncate w-full whitespace-nowrap text-left select-none;
70
+ }
71
+
72
+ .code {
73
+ @apply font-mono;
74
+ }
75
+
76
+ .code pre {
77
+ @apply block;
78
+ }
79
+
80
+ .code .line {
81
+ @apply flex items-center leading-relaxed;
82
+ }
83
+
84
+ .code.numbered {
85
+ @apply relative pt-3;
86
+ }
87
+
88
+ .code.numbered:before {
89
+ content: "";
90
+ left: calc(2.7em + 8px);
91
+ @apply absolute top-0 bottom-0 border-r border-gray-200;
92
+ }
93
+
94
+ .code .line-number {
95
+ width: calc(2.7em + 8px);
96
+ padding-top: 3px;
97
+ padding-bottom: 3px;
98
+ padding-right: 8px;
99
+ margin-right: 16px;
100
+ @apply font-mono text-right text-gray-400 flex-none text-xs;
101
+ }
102
+
103
+ .code .line-content {
104
+ @apply flex-none pr-4;
105
+ }
106
+
107
+ /* .code .line:before {
108
+ content: counter(line);
109
+ width: calc(3em + 8px);
110
+ padding-top: 2px;
111
+ padding-bottom: 2px;
112
+ padding-right: 8px;
113
+ @apply font-mono inline-block text-right mr-4 text-gray-400 border-r border-gray-200;
114
+ } */
115
+ }
116
+
117
+ @layer utilities {
118
+ .form-input {
119
+ @apply border-gray-300 text-gray-700 focus:ring-indigo-300 focus:border-indigo-300 rounded-sm text-sm w-full;
120
+ }
121
+ }
@@ -1,64 +1,54 @@
1
1
  import { install } from "@github/hotkey";
2
2
  import Alpine from "alpinejs";
3
- import Fern from "@ryangjchandler/fern";
4
- import AlpineTooltip from "@ryangjchandler/alpine-tooltip";
5
- import AlpineClipboard from "@ryangjchandler/alpine-clipboard";
6
- import Screen from "./utils/screen";
7
- import split from "./utils/split";
8
- import page from "./page";
9
- import workbench from "./workbench";
10
- import preview from "./workbench/preview";
11
- import inspector from "./workbench/inspector";
12
- import nav from "./nav";
13
- import navNode from "./nav/node";
14
- import navLeaf from "./nav/leaf";
15
- import sizeObserver from "./utils/size_observer";
16
- import reloader from "./utils/reloader";
17
- import clipboard from "./utils/clipboard";
18
-
19
- window.Alpine = Alpine;
3
+ import Persist from "@alpinejs/persist";
4
+ import Morph from "@alpinejs/morph";
5
+ import Tooltip from "@ryangjchandler/alpine-tooltip";
6
+
7
+ import page from "./components/page";
8
+ import inspector from "./components/inspector";
9
+ import previewWindow from "./components/preview-window";
10
+ import filter from "./components/filter";
11
+ import param from "./components/param";
12
+ import nav from "./components/nav";
13
+ import navItem from "./components/nav-item";
14
+ import navGroup from "./components/nav-group";
15
+ import splitter from "./components/splitter";
16
+ import copy from "./components/copy";
17
+ import sizes from "./components/sizes";
18
+
19
+ import initFilterStore from "./stores/filter";
20
+ import initLayoutStore from "./stores/layout";
21
+ import initNavStore from "./stores/nav";
22
+ import initSidebarStore from "./stores/sidebar";
23
+ import initInspectorStore from "./stores/inspector";
20
24
 
21
25
  // Plugins
22
26
 
23
- Alpine.plugin(Fern);
24
- Alpine.plugin(AlpineTooltip);
25
- Alpine.plugin(AlpineClipboard);
26
- Alpine.plugin(Screen);
27
+ Alpine.plugin(Persist);
28
+ Alpine.plugin(Morph);
29
+ Alpine.plugin(Tooltip);
27
30
 
28
31
  // Stores
29
32
 
30
- Alpine.store("page", {
31
- reflowing: false,
32
- doc: window.document,
33
- });
34
-
35
- Alpine.persistedStore("nav", {
36
- width: 280,
37
- filter: "",
38
- open: {},
39
- });
40
-
41
- Alpine.persistedStore("inspector", {
42
- height: 200,
43
- active: "source",
44
- });
45
-
46
- Alpine.persistedStore("preview", {
47
- width: "100%",
48
- });
33
+ Alpine.store("filter", initFilterStore(Alpine));
34
+ Alpine.store("layout", initLayoutStore(Alpine));
35
+ Alpine.store("nav", initNavStore(Alpine));
36
+ Alpine.store("sidebar", initSidebarStore(Alpine));
37
+ Alpine.store("inspector", initInspectorStore(Alpine));
49
38
 
50
- // Components & utils
39
+ // Components
51
40
 
52
41
  Alpine.data("page", page);
53
- Alpine.data("nav", nav);
54
- Alpine.data("navNode", navNode);
55
- Alpine.data("navLeaf", navLeaf);
56
- Alpine.data("workbench", workbench);
57
- Alpine.data("preview", preview);
42
+ Alpine.data("splitter", splitter);
43
+ Alpine.data("previewWindow", previewWindow);
44
+ Alpine.data("copy", copy);
58
45
  Alpine.data("inspector", inspector);
59
- Alpine.data("clipboard", clipboard);
60
- Alpine.data("sizeObserver", sizeObserver);
61
- Alpine.data("split", split);
46
+ Alpine.data("filter", filter);
47
+ Alpine.data("param", param);
48
+ Alpine.data("sizes", sizes);
49
+ Alpine.data("nav", nav);
50
+ Alpine.data("navItem", navItem);
51
+ Alpine.data("navGroup", navGroup);
62
52
 
63
53
  // Init
64
54
 
@@ -66,9 +56,5 @@ for (const el of document.querySelectorAll("[data-hotkey]")) {
66
56
  install(el);
67
57
  }
68
58
 
69
- if (window.SOCKET_PATH) {
70
- reloader(window.SOCKET_PATH).start();
71
- }
72
-
73
59
  window.Alpine = Alpine;
74
60
  Alpine.start();
@@ -0,0 +1,16 @@
1
+ export default function copy(id) {
2
+ return {
3
+ get content() {
4
+ const target = document.getElementById(id);
5
+ return (target ? target.innerHTML : "").trim();
6
+ },
7
+ done: false,
8
+ async save() {
9
+ await window.navigator.clipboard.writeText(this.content);
10
+ this.done = true;
11
+ setTimeout(() => {
12
+ this.done = false;
13
+ }, 1000);
14
+ },
15
+ };
16
+ }
@@ -0,0 +1,24 @@
1
+ export default function filter() {
2
+ return {
3
+ get active() {
4
+ return this.$store.filter.active;
5
+ },
6
+ checkEsc($event) {
7
+ if ($event.key === "Escape") {
8
+ this.active ? this.clear() : this.blur();
9
+ }
10
+ },
11
+ clear() {
12
+ this.$store.filter.raw = "";
13
+ },
14
+ focus($event) {
15
+ if ($event.target.tagName === "INPUT") {
16
+ return;
17
+ }
18
+ setTimeout(() => this.$refs.input.focus(), 0);
19
+ },
20
+ blur() {
21
+ setTimeout(() => this.$refs.input.blur(), 0);
22
+ },
23
+ };
24
+ }
@@ -0,0 +1,21 @@
1
+ export default function inspector() {
2
+ return {
3
+ isActivePanel(panel) {
4
+ return this.$store.inspector.panels.active == panel;
5
+ },
6
+ switchPanel(panel) {
7
+ this.$store.inspector.panels.active = panel;
8
+ },
9
+ get showSource() {
10
+ return this.$store.inspector.preview.source;
11
+ },
12
+ toggleSource() {
13
+ this.$store.inspector.preview.source =
14
+ !this.$store.inspector.preview.source;
15
+ },
16
+ preview: {
17
+ width: null,
18
+ height: null,
19
+ },
20
+ };
21
+ }