lookbook 0.4.3 → 0.4.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ }