lookbook 0.4.8 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +35 -21
- data/app/assets/lookbook/css/app.css +24 -13
- data/app/assets/lookbook/css/tooltip_theme.css +28 -0
- data/app/assets/lookbook/js/app.js +4 -0
- data/app/assets/lookbook/js/components/code.js +5 -0
- data/app/assets/lookbook/js/components/copy.js +4 -2
- data/app/assets/lookbook/js/components/filter.js +1 -1
- data/app/assets/lookbook/js/components/inspector.js +54 -9
- data/app/assets/lookbook/js/components/nav-group.js +1 -1
- data/app/assets/lookbook/js/components/nav-item.js +1 -0
- data/app/assets/lookbook/js/components/nav.js +1 -1
- data/app/assets/lookbook/js/components/page.js +17 -5
- data/app/assets/lookbook/js/components/param.js +23 -7
- data/app/assets/lookbook/js/components/preview-window.js +95 -26
- data/app/assets/lookbook/js/components/tabs.js +50 -0
- data/app/assets/lookbook/js/config.js +11 -4
- data/app/assets/lookbook/js/lib/socket.js +1 -1
- data/app/assets/lookbook/js/stores/inspector.js +13 -5
- data/app/controllers/lookbook/app_controller.rb +23 -9
- data/app/views/layouts/lookbook/app.html.erb +9 -3
- data/app/views/lookbook/components/_code.html.erb +6 -1
- data/app/views/lookbook/components/_drawer.html.erb +124 -0
- data/app/views/lookbook/components/_filter.html.erb +1 -1
- data/app/views/lookbook/components/_header.html.erb +2 -2
- data/app/views/lookbook/components/_nav.html.erb +1 -1
- data/app/views/lookbook/components/_nav_group.html.erb +11 -14
- data/app/views/lookbook/components/_nav_item.html.erb +17 -15
- data/app/views/lookbook/components/_nav_preview.html.erb +4 -2
- data/app/views/lookbook/components/_param.html.erb +6 -5
- data/app/views/lookbook/components/_preview.html.erb +67 -20
- data/app/views/lookbook/inputs/_select.html.erb +2 -3
- data/app/views/lookbook/inputs/_text.html.erb +3 -3
- data/app/views/lookbook/inputs/_textarea.html.erb +3 -3
- data/app/views/lookbook/inputs/_toggle.html.erb +5 -5
- data/app/views/lookbook/panels/_notes.html.erb +1 -1
- data/app/views/lookbook/panels/_output.html.erb +2 -2
- data/app/views/lookbook/panels/_params.html.erb +1 -1
- data/app/views/lookbook/panels/_preview.html.erb +52 -0
- data/app/views/lookbook/panels/_source.html.erb +2 -2
- data/app/views/lookbook/show.html.erb +22 -88
- data/lib/lookbook/code_formatter.rb +3 -3
- data/lib/lookbook/features.rb +1 -1
- data/lib/lookbook/preview.rb +1 -1
- data/lib/lookbook/version.rb +1 -1
- data/public/lookbook-assets/css/app.css +3 -1
- data/public/lookbook-assets/css/app.css.map +1 -1
- data/public/lookbook-assets/js/app.js +1 -1
- data/public/lookbook-assets/js/app.js.map +1 -1
- metadata +6 -3
- data/app/views/lookbook/components/_copy.html.erb +0 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0bf7750f26cce0e0b6517443da61e75a2d954b50c4882637a21fdac8b7d9c520
|
4
|
+
data.tar.gz: bb81e6a5cbb6db75b62591d6c233c1f1831db92e04045eafd372ae60264cc9fc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a569c06a48d6b30b8f328f25ad1eecfa474427d61dbf5a91395c780593329bb4424fdec52cb71d332dd229edc3d2031d9a9af84e864a4e906dcfcd7d387ea953
|
7
|
+
data.tar.gz: 1316810b7e32c5fda3ebd96b0d23566654f0a274fdaa730ae0ac50d5eaa03d0ed093ab412f504b1b0176cb37ecb26258d27f4aac081b6bce13bc754b6555c62d
|
data/README.md
CHANGED
@@ -28,7 +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
|
-
-
|
31
|
+
- In-browser live-editable preview parameters (similar to basic Storybook Controls/Knobs)
|
32
32
|
|
33
33
|
## Lookbook demo
|
34
34
|
|
@@ -63,10 +63,16 @@ end
|
|
63
63
|
|
64
64
|
The `at` property determines the root URL that the Lookbook UI will be served at.
|
65
65
|
|
66
|
-
> If you would like to expose the Lookbook UI in production as well as in development, just remove the `if Rails.env.development?` condition from around the mount statement.
|
67
|
-
|
68
66
|
Then you can start your app as normal and navigate to `http://localhost:3000/lookbook` (or whatever mount path you specified) to view your component previews in the Lookbook UI.
|
69
67
|
|
68
|
+
#### Mounting in Production
|
69
|
+
|
70
|
+
If you would like to expose the Lookbook UI in production as well as in development
|
71
|
+
|
72
|
+
1. Remove the `if Rails.env.development?` condition from around the mount statement in `routes.rb`
|
73
|
+
2. Add `config.view_component.show_previews = true` to `config/environments/production.rb`
|
74
|
+
|
75
|
+
|
70
76
|
## Usage
|
71
77
|
|
72
78
|
You don't need to do anything special to see your ViewComponent previews and examples in Lookbook - just create them as normal and they'll automatically appear in the Lookbook UI. Preview templates, custom layouts and even bespoke [preview controllers](https://viewcomponent.org/guide/previews.html#configuring-preview-controller) should all work as you would expect.
|
@@ -79,7 +85,7 @@ Lookbook parses [Yard-style comment tags](https://rubydoc.info/gems/yard/file/do
|
|
79
85
|
|
80
86
|
```ruby
|
81
87
|
# @label Basic Button
|
82
|
-
# @display bg_color #fff
|
88
|
+
# @display bg_color "#fff"
|
83
89
|
class ButtonComponentPreview < ViewComponent::Preview
|
84
90
|
|
85
91
|
# Primary button
|
@@ -96,7 +102,7 @@ class ButtonComponentPreview < ViewComponent::Preview
|
|
96
102
|
# Button with icon
|
97
103
|
# ----------------
|
98
104
|
# This example uses dynamic preview parameters
|
99
|
-
# which can be edited live in the Lookbook UI
|
105
|
+
# which can be edited live in the Lookbook UI
|
100
106
|
#
|
101
107
|
# @param text
|
102
108
|
# @param icon select [heart, cog, alert]
|
@@ -110,7 +116,7 @@ class ButtonComponentPreview < ViewComponent::Preview
|
|
110
116
|
# ---------------
|
111
117
|
# For light-on-dark screens
|
112
118
|
#
|
113
|
-
# @display bg_color #000
|
119
|
+
# @display bg_color "#000"
|
114
120
|
def secondary
|
115
121
|
render ButtonComponent.new(style: :inverted) do
|
116
122
|
"Click me"
|
@@ -161,7 +167,7 @@ The following Lookbook-specific tags are available for use:
|
|
161
167
|
* [`@display`](#display-tag)
|
162
168
|
* [`@!group ... @!endgroup`](#group-tag)
|
163
169
|
* [`@hidden`](#hidden-tag)
|
164
|
-
* [`@param`](#param-tag)
|
170
|
+
* [`@param`](#param-tag)
|
165
171
|
|
166
172
|
<h3 id="label-tag">🏷 @label</h3>
|
167
173
|
|
@@ -188,7 +194,7 @@ end
|
|
188
194
|
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.
|
189
195
|
|
190
196
|
```ruby
|
191
|
-
# @display bg_color #eee
|
197
|
+
# @display bg_color "#eee"
|
192
198
|
class FooComponentPreview < ViewComponent::Preview
|
193
199
|
|
194
200
|
# @display max_width 500px
|
@@ -207,6 +213,8 @@ The `@display` tag can be applied at the preview (class) or at the example (meth
|
|
207
213
|
- `<key>` must be a valid Ruby hash key name, without quotes or spaces
|
208
214
|
- `<value>` will be parsed using the [Ruby YAML parser](https://yaml.org/YAML_for_ruby.html) to resolve the value
|
209
215
|
|
216
|
+
> Note: Ruby YAML does not (generally) require quoting of string values. However in some cases it _is_ required due to the presence of [indicator characters](https://yaml.org/YAML_for_ruby.html#indicators_in_strings) (such as `#`, `:` etc) - hence why the hex color code in the example above is surrounded by quotes. It's perfectly ok to quote all string values if you prefer.
|
217
|
+
|
210
218
|
These display parameters can then be accessed via the `params` hash in your preview layout using `params[:lookbook][:display][<key>]`:
|
211
219
|
|
212
220
|
```html
|
@@ -309,9 +317,7 @@ class FooComponentPreview < ViewComponent::Preview
|
|
309
317
|
end
|
310
318
|
```
|
311
319
|
|
312
|
-
<h3 id="param-tag"
|
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.
|
320
|
+
<h3 id="param-tag">@param</h3>
|
315
321
|
|
316
322
|
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
323
|
|
@@ -324,7 +330,7 @@ The `@param` tag takes the following format:
|
|
324
330
|
```
|
325
331
|
|
326
332
|
- `<name>` - name of the dynamic preview param
|
327
|
-
- `<input_type>` - input field type to generate in the UI
|
333
|
+
- `<input_type>` - input field type to generate in the UI
|
328
334
|
- `<opts?>` - YAML-encoded field options, used for some field types
|
329
335
|
|
330
336
|
#### Input types
|
@@ -355,7 +361,7 @@ The following **input field types** are available for use:
|
|
355
361
|
@param <name> select <options>
|
356
362
|
```
|
357
363
|
|
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:
|
364
|
+
`<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
365
|
|
360
366
|
```ruby
|
361
367
|
# Basic options:
|
@@ -435,7 +441,7 @@ The following structured types are also available but should be considered **exp
|
|
435
441
|
|
436
442
|
```ruby
|
437
443
|
class ButtonComponentPreview < ViewComponent::Preview
|
438
|
-
|
444
|
+
|
439
445
|
# The params defined below will be editable in the UI:
|
440
446
|
#
|
441
447
|
# @param content text
|
@@ -494,6 +500,17 @@ If you wish to add additional paths to listen for changes in, you can use the `l
|
|
494
500
|
config.lookbook.listen_paths << Rails.root.join('app/other/directory')
|
495
501
|
```
|
496
502
|
|
503
|
+
### Custom favicon
|
504
|
+
|
505
|
+
If you want to change the favicon used by the Lookbook UI, you can provide a path to your own (or a data-uri string) using the `ui_favicon` option:
|
506
|
+
|
507
|
+
```ruby
|
508
|
+
config.lookbook.ui_favicon = "/path/to/my/favicon.png"
|
509
|
+
```
|
510
|
+
|
511
|
+
> To disable the favicon entirely, set the value to `false`.
|
512
|
+
|
513
|
+
|
497
514
|
<h3 id="experimental-features">Experimental features opt-in</h3>
|
498
515
|
|
499
516
|
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.
|
@@ -508,10 +525,6 @@ To opt into individual experimental features, include the name of the feature in
|
|
508
525
|
config.lookbook.experimental_features = ["feature_name"]
|
509
526
|
```
|
510
527
|
|
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
528
|
#### Opting into all experimental features (not recommended!)
|
516
529
|
|
517
530
|
If you want to live life on the bleeding-edge you can opt-in to all current **and future** experimental features (usual caveats apply):
|
@@ -527,9 +540,10 @@ Lookbook provides a few keyboard shortcuts to help you quickly move around the U
|
|
527
540
|
|
528
541
|
- `f` - move focus to the nav filter box
|
529
542
|
- `Esc` [when focus is in nav filter box] - Clear contents if text is present, or return focus to the UI if the box is already empty
|
530
|
-
- `s` - Switch to Source tab in the
|
531
|
-
- `
|
532
|
-
- `
|
543
|
+
- `s` - Switch to Source tab in the drawer
|
544
|
+
- `n` - Switch to Notes tab in the drawer
|
545
|
+
- `v` - Switch to the rendered preview
|
546
|
+
- `o` - Switch to the code preview
|
533
547
|
- `r` - Refresh the preview (useful if using something like Faker to generate randomised data for the preview)
|
534
548
|
- `w` - Open the standalone rendered preview in a new window
|
535
549
|
|
@@ -2,6 +2,7 @@
|
|
2
2
|
@import "tailwindcss/components";
|
3
3
|
@import "tailwindcss/utilities";
|
4
4
|
@import "tippy.js/dist/tippy";
|
5
|
+
@import "tippy.js/dist/border";
|
5
6
|
@import "code_theme";
|
6
7
|
@import "tooltip_theme";
|
7
8
|
|
@@ -57,8 +58,8 @@
|
|
57
58
|
}
|
58
59
|
|
59
60
|
@layer components {
|
60
|
-
#nav > ul > li {
|
61
|
-
@apply py-1;
|
61
|
+
#nav > ul > li > div {
|
62
|
+
@apply py-1 border-b border-gray-300;
|
62
63
|
}
|
63
64
|
|
64
65
|
.nav-toggle {
|
@@ -77,8 +78,12 @@
|
|
77
78
|
@apply block;
|
78
79
|
}
|
79
80
|
|
81
|
+
.code.wrapped pre {
|
82
|
+
@apply whitespace-pre-wrap;
|
83
|
+
}
|
84
|
+
|
80
85
|
.code .line {
|
81
|
-
@apply
|
86
|
+
@apply leading-relaxed;
|
82
87
|
}
|
83
88
|
|
84
89
|
.code.numbered {
|
@@ -87,35 +92,41 @@
|
|
87
92
|
|
88
93
|
.code.numbered:before {
|
89
94
|
content: "";
|
90
|
-
left:
|
95
|
+
left: 2.7em;
|
91
96
|
@apply absolute top-0 bottom-0 border-r border-gray-200;
|
92
97
|
}
|
93
98
|
|
99
|
+
.code.numbered .line {
|
100
|
+
padding-left: calc(2.7em + 8px);
|
101
|
+
@apply relative;
|
102
|
+
}
|
103
|
+
|
94
104
|
.code .line-number {
|
105
|
+
display: inline-block;
|
95
106
|
width: calc(2.7em + 8px);
|
96
107
|
padding-top: 3px;
|
97
108
|
padding-bottom: 3px;
|
98
109
|
padding-right: 8px;
|
99
110
|
margin-right: 16px;
|
100
|
-
@apply font-mono text-right text-gray-400 flex-none text-xs;
|
111
|
+
@apply font-mono text-right text-gray-400 flex-none text-xs absolute left-0;
|
101
112
|
}
|
102
113
|
|
103
114
|
.code .line-content {
|
104
115
|
@apply flex-none pr-4;
|
105
116
|
}
|
106
117
|
|
107
|
-
|
108
|
-
|
109
|
-
|
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
|
-
} */
|
118
|
+
.resize-handle {
|
119
|
+
@apply flex items-center justify-center h-full w-full border-gray-300 bg-white hover:bg-indigo-100 hover:bg-opacity-20 text-gray-400 hover:text-gray-700 transition select-none touch-none;
|
120
|
+
}
|
115
121
|
}
|
116
122
|
|
117
123
|
@layer utilities {
|
118
124
|
.form-input {
|
119
125
|
@apply border-gray-300 text-gray-700 focus:ring-indigo-300 focus:border-indigo-300 rounded-sm text-sm w-full;
|
120
126
|
}
|
127
|
+
|
128
|
+
.checked-bg {
|
129
|
+
background-color: #ffffff;
|
130
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3E%3Cg fill='%23f3f3f3' fill-opacity='1'%3E%3Cpath fill-rule='evenodd' d='M0 0h4v4H0V0zm4 4h4v4H4V4z'/%3E%3C/g%3E%3C/svg%3E");
|
131
|
+
}
|
121
132
|
}
|
@@ -4,13 +4,41 @@
|
|
4
4
|
&[data-placement^="top"] > .tippy-arrow::before {
|
5
5
|
border-top-color: theme("colors.indigo.500");
|
6
6
|
}
|
7
|
+
|
7
8
|
&[data-placement^="bottom"] > .tippy-arrow::before {
|
8
9
|
border-bottom-color: theme("colors.indigo.500");
|
9
10
|
}
|
11
|
+
|
10
12
|
&[data-placement^="left"] > .tippy-arrow::before {
|
11
13
|
border-left-color: theme("colors.indigo.500");
|
12
14
|
}
|
15
|
+
|
13
16
|
&[data-placement^="right"] > .tippy-arrow::before {
|
14
17
|
border-right-color: theme("colors.indigo.500");
|
15
18
|
}
|
16
19
|
}
|
20
|
+
|
21
|
+
.tippy-box[data-theme~="menu"] {
|
22
|
+
border: 1px solid theme("colors.gray.300");
|
23
|
+
@apply bg-white text-gray-600 shadow-md rounded;
|
24
|
+
|
25
|
+
& > .tippy-content {
|
26
|
+
padding: 0;
|
27
|
+
}
|
28
|
+
|
29
|
+
&[data-placement^="top"] > .tippy-arrow::before {
|
30
|
+
border-top-color: white;
|
31
|
+
}
|
32
|
+
|
33
|
+
&[data-placement^="bottom"] > .tippy-arrow::before {
|
34
|
+
border-bottom-color: white;
|
35
|
+
}
|
36
|
+
|
37
|
+
&[data-placement^="left"] > .tippy-arrow::before {
|
38
|
+
border-left-color: white;
|
39
|
+
}
|
40
|
+
|
41
|
+
&[data-placement^="right"] > .tippy-arrow::before {
|
42
|
+
border-right-color: white;
|
43
|
+
}
|
44
|
+
}
|
@@ -13,7 +13,9 @@ import nav from "./components/nav";
|
|
13
13
|
import navItem from "./components/nav-item";
|
14
14
|
import navGroup from "./components/nav-group";
|
15
15
|
import splitter from "./components/splitter";
|
16
|
+
import tabs from "./components/tabs";
|
16
17
|
import copy from "./components/copy";
|
18
|
+
import code from "./components/code";
|
17
19
|
import sizes from "./components/sizes";
|
18
20
|
|
19
21
|
import initFilterStore from "./stores/filter";
|
@@ -42,11 +44,13 @@ Alpine.data("page", page);
|
|
42
44
|
Alpine.data("splitter", splitter);
|
43
45
|
Alpine.data("previewWindow", previewWindow);
|
44
46
|
Alpine.data("copy", copy);
|
47
|
+
Alpine.data("code", code);
|
45
48
|
Alpine.data("inspector", inspector);
|
46
49
|
Alpine.data("filter", filter);
|
47
50
|
Alpine.data("param", param);
|
48
51
|
Alpine.data("sizes", sizes);
|
49
52
|
Alpine.data("nav", nav);
|
53
|
+
Alpine.data("tabs", tabs);
|
50
54
|
Alpine.data("navItem", navItem);
|
51
55
|
Alpine.data("navGroup", navGroup);
|
52
56
|
|
@@ -1,7 +1,9 @@
|
|
1
|
-
export default function copy(
|
1
|
+
export default function copy() {
|
2
2
|
return {
|
3
3
|
get content() {
|
4
|
-
const target = document.getElementById(
|
4
|
+
const target = document.getElementById(
|
5
|
+
this.$root.getAttribute("data-target")
|
6
|
+
);
|
5
7
|
return (target ? target.innerHTML : "").trim();
|
6
8
|
},
|
7
9
|
done: false,
|
@@ -1,17 +1,62 @@
|
|
1
|
+
import sizeObserver from "./sizes";
|
2
|
+
|
1
3
|
export default function inspector() {
|
2
4
|
return {
|
3
|
-
|
4
|
-
|
5
|
+
width: 0,
|
6
|
+
height: 0,
|
7
|
+
init() {
|
8
|
+
const ro = new ResizeObserver((entries) => {
|
9
|
+
const rect = entries[0].contentRect;
|
10
|
+
this.width = Math.round(rect.width);
|
11
|
+
this.height = Math.round(rect.height);
|
12
|
+
});
|
13
|
+
ro.observe(this.$el);
|
14
|
+
this.width = Math.round(this.$el.clientWidth);
|
15
|
+
this.height = Math.round(this.$el.clientHeight);
|
5
16
|
},
|
6
|
-
|
7
|
-
this.$store.inspector.
|
17
|
+
get orientation() {
|
18
|
+
return this.$store.inspector.drawer.orientation;
|
8
19
|
},
|
9
|
-
get
|
10
|
-
return this.$store.inspector.preview.
|
20
|
+
get view() {
|
21
|
+
return this.$store.inspector.preview.view;
|
11
22
|
},
|
12
|
-
|
13
|
-
this
|
14
|
-
|
23
|
+
get horizontal() {
|
24
|
+
return this.canBeVertical ? this.orientation === "horizontal" : true;
|
25
|
+
},
|
26
|
+
get vertical() {
|
27
|
+
return !this.horizontal;
|
28
|
+
},
|
29
|
+
get canBeVertical() {
|
30
|
+
return this.width > 800;
|
31
|
+
},
|
32
|
+
get drawerHidden() {
|
33
|
+
return this.$store.inspector.drawer.hidden;
|
34
|
+
},
|
35
|
+
get maxDrawerHeight() {
|
36
|
+
return Math.round(this.height * 0.7);
|
37
|
+
},
|
38
|
+
get maxDrawerWidth() {
|
39
|
+
return Math.round(this.width * 0.7);
|
40
|
+
},
|
41
|
+
isActiveDrawerPanel(panel) {
|
42
|
+
return this.$store.inspector.drawer.panel === panel;
|
43
|
+
},
|
44
|
+
switchDrawerPanel(panel) {
|
45
|
+
this.$store.inspector.drawer.panel = panel;
|
46
|
+
},
|
47
|
+
isActivePreviewPanel(panel) {
|
48
|
+
return this.$store.inspector.preview.panel === panel;
|
49
|
+
},
|
50
|
+
switchPreviewPanel(panel) {
|
51
|
+
this.$store.inspector.preview.panel = panel;
|
52
|
+
},
|
53
|
+
toggleOrientation() {
|
54
|
+
this.$store.inspector.drawer.orientation =
|
55
|
+
this.orientation === "horizontal" ? "vertical" : "horizontal";
|
56
|
+
},
|
57
|
+
toggleDrawer() {
|
58
|
+
this.$store.inspector.drawer.hidden =
|
59
|
+
!this.$store.inspector.drawer.hidden;
|
15
60
|
},
|
16
61
|
preview: {
|
17
62
|
width: null,
|
@@ -15,7 +15,7 @@ export default function navGroup() {
|
|
15
15
|
},
|
16
16
|
getChildren() {
|
17
17
|
return this.$refs.items
|
18
|
-
? Array.from(this.$refs.items.querySelectorAll(":scope > li"))
|
18
|
+
? Array.from(this.$refs.items.querySelectorAll(":scope > li > div"))
|
19
19
|
: [];
|
20
20
|
},
|
21
21
|
navigateToFirstChild() {
|
@@ -1,5 +1,21 @@
|
|
1
1
|
import createSocket from "../lib/socket";
|
2
2
|
|
3
|
+
const morphOpts = {
|
4
|
+
key(el) {
|
5
|
+
return el.getAttribute("key") ? el.getAttribute("key") : el.id;
|
6
|
+
},
|
7
|
+
updating(el, toEl, childrenOnly, skip) {
|
8
|
+
if (
|
9
|
+
el.getAttribute &&
|
10
|
+
el.getAttribute("data-morph-strategy") === "replace"
|
11
|
+
) {
|
12
|
+
el.innerHTML = toEl.innerHTML;
|
13
|
+
return skip();
|
14
|
+
}
|
15
|
+
},
|
16
|
+
lookahead: true,
|
17
|
+
};
|
18
|
+
|
3
19
|
export default function page() {
|
4
20
|
return {
|
5
21
|
init() {
|
@@ -22,11 +38,7 @@ export default function page() {
|
|
22
38
|
},
|
23
39
|
morph(dom) {
|
24
40
|
const pageHtml = dom.getElementById(this.$root.id).outerHTML;
|
25
|
-
Alpine.morph(this.$root, pageHtml,
|
26
|
-
key(el) {
|
27
|
-
return el.getAttribute("key") ? el.getAttribute("key") : el.id;
|
28
|
-
},
|
29
|
-
});
|
41
|
+
Alpine.morph(this.$root, pageHtml, morphOpts);
|
30
42
|
this.$dispatch("page:morphed");
|
31
43
|
},
|
32
44
|
};
|
@@ -1,18 +1,34 @@
|
|
1
|
-
|
1
|
+
import debounce from "debounce";
|
2
|
+
|
3
|
+
export default function param(name, value, opts = {}) {
|
2
4
|
return {
|
3
|
-
|
4
|
-
|
5
|
-
|
5
|
+
name,
|
6
|
+
value,
|
7
|
+
updating: false,
|
8
|
+
init() {
|
9
|
+
if (opts.debounce) {
|
10
|
+
this.$watch(
|
11
|
+
"value",
|
12
|
+
debounce(() => this.updateIfValid(), opts.debounce)
|
13
|
+
);
|
14
|
+
} else {
|
15
|
+
this.$watch("value", () => this.updateIfValid());
|
6
16
|
}
|
7
17
|
},
|
8
|
-
|
18
|
+
setFocus() {
|
19
|
+
setTimeout(() => this.$root.focus(), 0);
|
20
|
+
},
|
21
|
+
updateIfValid() {
|
22
|
+
if (this.validate()) this.update();
|
23
|
+
},
|
24
|
+
update() {
|
9
25
|
const searchParams = new URLSearchParams(window.location.search);
|
10
|
-
searchParams.set(name, value);
|
26
|
+
searchParams.set(this.name, this.value);
|
11
27
|
const path = location.href.replace(location.search, "");
|
12
28
|
this.setLocation(`${path}?${searchParams.toString()}`);
|
13
29
|
},
|
14
30
|
validate() {
|
15
|
-
return this.$
|
31
|
+
return this.$root.reportValidity ? this.$root.reportValidity() : true;
|
16
32
|
},
|
17
33
|
};
|
18
34
|
}
|
@@ -1,37 +1,106 @@
|
|
1
1
|
export default function preview() {
|
2
2
|
return {
|
3
|
-
|
4
|
-
|
5
|
-
this.resizeStartSize - (this.resizeStartPosition - e.pageX) * 2;
|
6
|
-
const parentSize = this.$root.parentElement.clientWidth;
|
7
|
-
const percentSize = (Math.round(size) / parentSize) * 100;
|
8
|
-
const minWidth = (300 / parentSize) * 100;
|
9
|
-
this.$store.inspector.preview.width = `${Math.min(
|
10
|
-
Math.max(percentSize, minWidth),
|
11
|
-
100
|
12
|
-
)}%`;
|
3
|
+
get store() {
|
4
|
+
return this.$store.inspector.preview;
|
13
5
|
},
|
14
|
-
|
6
|
+
get maxWidth() {
|
7
|
+
return this.store.width === "100%" ? "100%" : `${this.store.width}px`;
|
8
|
+
},
|
9
|
+
get maxHeight() {
|
10
|
+
return this.store.height === "100%" ? "100%" : `${this.store.height}px`;
|
11
|
+
},
|
12
|
+
get parentWidth() {
|
13
|
+
return Math.round(this.$root.parentElement.clientWidth);
|
14
|
+
},
|
15
|
+
get parentHeight() {
|
16
|
+
return Math.round(this.$root.parentElement.clientHeight);
|
17
|
+
},
|
18
|
+
start() {
|
15
19
|
this.$store.layout.reflowing = true;
|
16
|
-
this.
|
17
|
-
|
18
|
-
|
19
|
-
this.resizeStartSize = this.$root.clientWidth;
|
20
|
-
window.addEventListener("pointermove", this.onResize);
|
21
|
-
window.addEventListener("pointerup", this.onResizeEnd);
|
22
|
-
},
|
23
|
-
onResizeEnd() {
|
24
|
-
window.removeEventListener("pointermove", this.onResize);
|
25
|
-
window.removeEventListener("pointerup", this.onResizeEnd);
|
20
|
+
this.store.resizing = true;
|
21
|
+
},
|
22
|
+
end() {
|
26
23
|
this.$store.layout.reflowing = false;
|
24
|
+
this.store.resizing = false;
|
25
|
+
},
|
26
|
+
onResizeStart(e) {
|
27
|
+
this.onResizeWidthStart(e);
|
28
|
+
this.onResizeHeightStart(e);
|
29
|
+
},
|
30
|
+
toggleFullSize() {
|
31
|
+
const { height, width } = this.store;
|
32
|
+
if (height === "100%" && width === "100%") {
|
33
|
+
this.toggleFullHeight();
|
34
|
+
this.toggleFullWidth();
|
35
|
+
} else {
|
36
|
+
if (height !== "100%") this.toggleFullHeight();
|
37
|
+
if (width !== "100%") this.toggleFullWidth();
|
38
|
+
}
|
39
|
+
},
|
40
|
+
onResizeWidth(e) {
|
41
|
+
const width =
|
42
|
+
this.resizeStartWidth - (this.resizeStartPositionX - e.pageX) * 2;
|
43
|
+
const boundedWidth = Math.min(
|
44
|
+
Math.max(Math.round(width), 200),
|
45
|
+
this.parentWidth
|
46
|
+
);
|
47
|
+
this.store.width =
|
48
|
+
boundedWidth === this.parentWidth ? "100%" : boundedWidth;
|
49
|
+
},
|
50
|
+
onResizeWidthStart(e) {
|
51
|
+
this.start();
|
52
|
+
this.onResizeWidth = this.onResizeWidth.bind(this);
|
53
|
+
this.onResizeWidthEnd = this.onResizeWidthEnd.bind(this);
|
54
|
+
this.resizeStartPositionX = e.pageX;
|
55
|
+
this.resizeStartWidth = this.$root.clientWidth;
|
56
|
+
window.addEventListener("pointermove", this.onResizeWidth);
|
57
|
+
window.addEventListener("pointerup", this.onResizeWidthEnd);
|
58
|
+
},
|
59
|
+
onResizeWidthEnd() {
|
60
|
+
window.removeEventListener("pointermove", this.onResizeWidth);
|
61
|
+
window.removeEventListener("pointerup", this.onResizeWidthEnd);
|
62
|
+
this.end();
|
27
63
|
},
|
28
64
|
toggleFullWidth() {
|
29
|
-
const
|
30
|
-
if (
|
31
|
-
|
65
|
+
const { width, lastWidth } = this.store;
|
66
|
+
if (width === "100%" && lastWidth) {
|
67
|
+
this.store.width = lastWidth;
|
68
|
+
} else {
|
69
|
+
this.store.lastWidth = width;
|
70
|
+
this.store.width = "100%";
|
71
|
+
}
|
72
|
+
},
|
73
|
+
onResizeHeight(e) {
|
74
|
+
const height =
|
75
|
+
this.resizeStartHeight - (this.resizeStartPositionY - e.pageY);
|
76
|
+
const boundedHeight = Math.min(
|
77
|
+
Math.max(Math.round(height), 200),
|
78
|
+
this.parentHeight
|
79
|
+
);
|
80
|
+
this.$store.inspector.preview.height =
|
81
|
+
boundedHeight === this.parentHeight ? "100%" : boundedHeight;
|
82
|
+
},
|
83
|
+
onResizeHeightStart(e) {
|
84
|
+
this.start();
|
85
|
+
this.onResizeHeight = this.onResizeHeight.bind(this);
|
86
|
+
this.onResizeHeightEnd = this.onResizeHeightEnd.bind(this);
|
87
|
+
this.resizeStartPositionY = e.pageY;
|
88
|
+
this.resizeStartHeight = this.$root.clientHeight;
|
89
|
+
window.addEventListener("pointermove", this.onResizeHeight);
|
90
|
+
window.addEventListener("pointerup", this.onResizeHeightEnd);
|
91
|
+
},
|
92
|
+
onResizeHeightEnd() {
|
93
|
+
window.removeEventListener("pointermove", this.onResizeHeight);
|
94
|
+
window.removeEventListener("pointerup", this.onResizeHeightEnd);
|
95
|
+
this.end();
|
96
|
+
},
|
97
|
+
toggleFullHeight() {
|
98
|
+
const { height, lastHeight } = this.store;
|
99
|
+
if (height === "100%" && lastHeight) {
|
100
|
+
this.store.height = lastHeight;
|
32
101
|
} else {
|
33
|
-
|
34
|
-
|
102
|
+
this.store.lastHeight = height;
|
103
|
+
this.store.height = "100%";
|
35
104
|
}
|
36
105
|
},
|
37
106
|
};
|