lookbook 0.4.8 → 0.5.0
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.
- 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
|
};
|