lookbook 0.2.3 → 0.3.0.beta.2

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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +93 -0
  3. data/app/assets/lookbook/css/app.css +28 -0
  4. data/app/assets/lookbook/js/app.js +49 -24
  5. data/app/assets/lookbook/js/nav/leaf.js +20 -0
  6. data/app/assets/lookbook/js/nav/node.js +31 -0
  7. data/app/assets/lookbook/js/nav.js +39 -0
  8. data/app/assets/lookbook/js/page.js +33 -0
  9. data/app/assets/lookbook/js/utils/clipboard.js +13 -0
  10. data/app/assets/lookbook/js/utils/morph.js +16 -0
  11. data/app/assets/lookbook/js/{reloader.js → utils/reloader.js} +0 -0
  12. data/app/assets/lookbook/js/utils/screen.js +44 -0
  13. data/app/assets/lookbook/js/{size_observer.js → utils/size_observer.js} +1 -1
  14. data/app/assets/lookbook/js/{split.js → utils/split.js} +4 -4
  15. data/app/assets/lookbook/js/workbench/inspector.js +11 -0
  16. data/app/assets/lookbook/js/workbench/preview.js +39 -0
  17. data/app/assets/lookbook/js/workbench.js +14 -0
  18. data/app/controllers/lookbook/{browser_controller.rb → app_controller.rb} +58 -31
  19. data/app/helpers/lookbook/application_helper.rb +1 -1
  20. data/app/views/lookbook/_sidebar.html.erb +45 -0
  21. data/app/views/lookbook/_workbench.html.erb +12 -0
  22. data/app/views/lookbook/{browser → app}/error.html.erb +0 -0
  23. data/app/views/lookbook/app/index.html.erb +11 -0
  24. data/app/views/lookbook/{browser → app}/not_found.html.erb +1 -1
  25. data/app/views/lookbook/app/show.html.erb +1 -0
  26. data/app/views/lookbook/layouts/app.html.erb +16 -26
  27. data/app/views/lookbook/nav/_collection.html.erb +5 -0
  28. data/app/views/lookbook/nav/_leaf.html.erb +21 -0
  29. data/app/views/lookbook/nav/_node.html.erb +19 -0
  30. data/app/views/lookbook/nav/_preview.html.erb +11 -0
  31. data/app/views/lookbook/preview_group.html.erb +8 -0
  32. data/app/views/lookbook/shared/_clipboard.html.erb +11 -0
  33. data/app/views/lookbook/shared/_header.html.erb +8 -0
  34. data/app/views/lookbook/workbench/_header.html.erb +37 -0
  35. data/app/views/lookbook/workbench/_inspector.html.erb +32 -0
  36. data/app/views/lookbook/workbench/_preview.html.erb +24 -0
  37. data/app/views/lookbook/workbench/inspector/_code.html.erb +3 -0
  38. data/app/views/lookbook/workbench/inspector/_notes.html.erb +24 -0
  39. data/app/views/lookbook/{partials → workbench}/inspector/_plain.html.erb +0 -0
  40. data/config/routes.rb +3 -3
  41. data/lib/lookbook/engine.rb +1 -0
  42. data/lib/lookbook/preview.rb +26 -3
  43. data/lib/lookbook/preview_controller.rb +6 -1
  44. data/lib/lookbook/preview_example.rb +3 -2
  45. data/lib/lookbook/preview_group.rb +37 -0
  46. data/lib/lookbook/taggable.rb +5 -1
  47. data/lib/lookbook/version.rb +1 -1
  48. data/lib/lookbook.rb +1 -0
  49. data/lib/tasks/lookbook_tasks.rake +1 -1
  50. data/public/lookbook-assets/app.css +229 -99
  51. data/public/lookbook-assets/app.js +882 -56
  52. data/{app/views/lookbook/partials/_icon_sprite.html.erb → public/lookbook-assets/feather-sprite.svg} +1 -1
  53. metadata +53 -24
  54. data/app/assets/lookbook/js/preview.js +0 -76
  55. data/app/views/lookbook/browser/index.html.erb +0 -8
  56. data/app/views/lookbook/browser/show.html.erb +0 -33
  57. data/app/views/lookbook/partials/_preview.html.erb +0 -18
  58. data/app/views/lookbook/partials/_sidebar.html.erb +0 -21
  59. data/app/views/lookbook/partials/inspector/_code.html.erb +0 -1
  60. data/app/views/lookbook/partials/inspector/_inspector.html.erb +0 -43
  61. data/app/views/lookbook/partials/inspector/_prose.html.erb +0 -3
  62. data/app/views/lookbook/partials/nav/_collection.html.erb +0 -17
  63. data/app/views/lookbook/partials/nav/_label.html.erb +0 -13
  64. data/app/views/lookbook/partials/nav/_nav.html.erb +0 -27
  65. data/app/views/lookbook/partials/nav/_preview.html.erb +0 -48
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c35cb307d0756229c0ef4be7e46fcdaa9815abc1988d78e48390c2b7fe7b470a
4
- data.tar.gz: '01179fd9cb5d1949b04882565f5d2288f3a8575e0164dfd901951b51e7069557'
3
+ metadata.gz: 61d3cbcc3579d9a79aaa03fb87bda891923c10aed37eb78f11ef948b09b99ed9
4
+ data.tar.gz: 9ac3803965a4d9fc17eed96bb8a2e2a0821c78019e061032fa5f22276edbe321
5
5
  SHA512:
6
- metadata.gz: 7ca421666db35f1c09076e4c6a0699c958ef64e9c7a35cdcc47122d734695d4df5913230860f1c1885a0b658f2274a0268e846dafce18ea27995317610be8823
7
- data.tar.gz: 2c35614a77dba59d2c4e5d3f5ab490ec44d3e3af065ba1dd5004ec0cfedcc1b8ab99fc7bacf3060efb80d50c7965c50110149967ffc189284810eb67b5e6557d
6
+ metadata.gz: 4d902210204c1f779a535dcf6aec261b827ff92cef73ce384f2d791ccc0fb85ff0f5de0d56f2cf19aeff67f5190a1ea753b1f6d2016108bc7fe3be55b8b0930e
7
+ data.tar.gz: 3d52cc3c23b60d1cc8fdef2f753ff7e80ad858e0f2eec598212307cfa0338b69b87071fd3fa45a3848f303533f033b1c55dc0995ba15364c9f831daa2c893338
data/README.md CHANGED
@@ -112,6 +112,29 @@ class ButtonComponentPreview < ViewComponent::Preview
112
112
  "Click me"
113
113
  end
114
114
  end
115
+
116
+ # @!group More examples
117
+
118
+ def short_text
119
+ render ButtonComponent.new do
120
+ "Go"
121
+ end
122
+ end
123
+
124
+ def long_text
125
+ render ButtonComponent.new do
126
+ "Click here to do this thing because it's the best way to do it"
127
+ end
128
+ end
129
+
130
+ def emoji_text
131
+ render ButtonComponent.new do
132
+ "👀📗"
133
+ end
134
+ end
135
+
136
+ # @!endgroup
137
+
115
138
  end
116
139
  ```
117
140
 
@@ -153,6 +176,52 @@ class FooComponentPreview < ViewComponent::Preview
153
176
  end
154
177
  ```
155
178
 
179
+ #### `@!group <name> ... @!endgroup`
180
+
181
+ 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.
182
+
183
+ You can group a set of examples by wrapping them in `@!group <name>` / `@!endgroup` tags within your preview file:
184
+
185
+ ```ruby
186
+ class HeaderComponentPreview < ViewComponent::Preview
187
+
188
+ def standard
189
+ render Elements::HeaderComponent.new do
190
+ "Standard header"
191
+ end
192
+ end
193
+
194
+ # @!group Sizes
195
+
196
+ def small
197
+ render Elements::HeaderComponent.new(size: 12) do
198
+ "Small header"
199
+ end
200
+ end
201
+
202
+ def medium
203
+ render Elements::HeaderComponent.new(size: 16) do
204
+ "Small header"
205
+ end
206
+ end
207
+
208
+ def big
209
+ render Elements::HeaderComponent.new(size: 24) do
210
+ "Small header"
211
+ end
212
+ end
213
+
214
+ # @!endgroup
215
+
216
+ end
217
+ ```
218
+
219
+ The example above would display the `Sizes` examples grouped together on a single page, rather than as indiviual items in the navigation:
220
+
221
+ <img src=".github/assets/nav_group.png">
222
+
223
+ You can have as many groups as you like within a single preview class, but each example can only belong to one group.
224
+
156
225
  #### Adding notes
157
226
 
158
227
  All comment text other than tags will be treated as markdown and rendered in the **Notes** panel for that example in the Lookbook UI.
@@ -193,6 +262,30 @@ If you wish to add additional paths to listen for changes in, you can use the `l
193
262
  config.lookbook.listen_paths << Rails.root.join('app/other/directory')
194
263
  ```
195
264
 
265
+ ## Keyboard shortcuts
266
+
267
+ Lookbook provides a few keyboard shortcuts to help you quickly move around the UI.
268
+
269
+ - `f` - move focus to the nav filter box
270
+ - `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
271
+ - `s` - Switch to Source tab in the inspector
272
+ - `o` - Switch to Output tab in the inspector
273
+ - `n` - Switch to Notes tab in the inspector
274
+ - `r` - Refresh the preview (useful if using something like Faker to generate randomised data for the preview)
275
+ - `w` - Open the standalone rendered preview in a new window
276
+
277
+ ## Troubleshooting
278
+
279
+ #### Blank preview window
280
+
281
+ Certain setups (for example when using `Rack::LiveReload`) can cause an issue with the way that the preview iframe displays the rendered component preview (i.e. using the `srcdoc` attribute to avoid extra requests).
282
+
283
+ If you are seeing a blank preview window, but the source and output tabs are both displaying code as expected, you can disable the use of the `srcdoc` attribute using the following configuration option:
284
+
285
+ ```ruby
286
+ config.lookbook.preview_srcdoc = false
287
+ ```
288
+
196
289
  ## Contributing
197
290
 
198
291
  Lookbook is very much a small hobby/side project at the moment. I'd love to hear from anyone who is interested in contributing but I'm terrible at replying to emails or messages, so don't be surprised if I take forever to get back to you. It's not personal 😜
@@ -34,4 +34,32 @@
34
34
  stroke-linejoin: round;
35
35
  fill: none;
36
36
  }
37
+
38
+ .h-fill {
39
+ height: fill-available;
40
+ }
41
+
42
+ .min-h-fill {
43
+ min-height: fill-available;
44
+ }
45
+
46
+ ::-webkit-scrollbar {
47
+ width: 8px;
48
+ height: 8px;
49
+ }
50
+
51
+ ::-webkit-scrollbar-track {
52
+ background: transparent;
53
+ }
54
+
55
+ ::-webkit-scrollbar-thumb {
56
+ @apply bg-gray-300 transition-colors;
57
+ border-radius: 6px;
58
+ border: 2px solid transparent;
59
+ background-clip: content-box;
60
+ }
61
+
62
+ ::-webkit-scrollbar-thumb:hover {
63
+ @apply bg-gray-400;
64
+ }
37
65
  }
@@ -1,49 +1,74 @@
1
+ import { install } from "@github/hotkey";
1
2
  import Alpine from "alpinejs";
2
3
  import Fern from "@ryangjchandler/fern";
3
- import Tooltip from "@ryangjchandler/alpine-tooltip";
4
- import Clipboard from "@ryangjchandler/alpine-clipboard";
5
- import split from "./split";
6
- import preview from "./preview";
7
- import observeSize from "./size_observer";
8
- import reloader from "./reloader";
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;
9
20
 
10
21
  // Plugins
11
22
 
12
23
  Alpine.plugin(Fern);
13
- Alpine.plugin(Tooltip);
14
- Alpine.plugin(Clipboard);
15
-
16
- // Data
17
-
18
- Alpine.data("preview", preview);
19
- Alpine.data("sizeObserver", observeSize);
20
- Alpine.data("split", split);
24
+ Alpine.plugin(AlpineTooltip);
25
+ Alpine.plugin(AlpineClipboard);
26
+ Alpine.plugin(Screen);
21
27
 
22
28
  // Stores
23
29
 
24
- Alpine.store("app", { reflowing: false });
30
+ Alpine.store("page", {
31
+ reflowing: false,
32
+ doc: window.document,
33
+ });
34
+
25
35
  Alpine.persistedStore("nav", {
26
36
  width: 280,
27
37
  filter: "",
28
38
  open: {},
29
- scrollTop: 0,
30
- shouldDisplay(previewName) {
31
- const cleanFilter = this.filter.replace(/\s/g, "");
32
- return (
33
- cleanFilter === "" || previewName.includes(cleanFilter.toLowerCase())
34
- );
35
- },
36
39
  });
37
- Alpine.persistedStore("preview", {});
40
+
38
41
  Alpine.persistedStore("inspector", {
39
42
  height: 200,
40
43
  active: "source",
41
44
  });
42
45
 
46
+ Alpine.persistedStore("preview", {
47
+ width: "100%",
48
+ });
49
+
50
+ // Components & utils
51
+
52
+ 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);
58
+ Alpine.data("inspector", inspector);
59
+ Alpine.data("clipboard", clipboard);
60
+ Alpine.data("sizeObserver", sizeObserver);
61
+ Alpine.data("split", split);
62
+
43
63
  // Init
44
64
 
45
- window.Alpine = Alpine;
65
+ for (const el of document.querySelectorAll("[data-hotkey]")) {
66
+ install(el);
67
+ }
68
+
46
69
  if (window.SOCKET_PATH) {
47
70
  reloader(window.SOCKET_PATH).start();
48
71
  }
72
+
73
+ window.Alpine = Alpine;
49
74
  Alpine.start();
@@ -0,0 +1,20 @@
1
+ export default function navLeaf() {
2
+ return {
3
+ path: null,
4
+ matchers: [],
5
+ active: false,
6
+ hidden: false,
7
+ setActive() {
8
+ this.active = this.path === window.location.pathname;
9
+ },
10
+ filter() {
11
+ if (this.$store.nav.filtering) {
12
+ const text = this.$store.nav.filterText;
13
+ const matched = this.matchers.map((m) => m.includes(text));
14
+ this.hidden = !matched.filter((m) => m).length;
15
+ } else {
16
+ this.hidden = false;
17
+ }
18
+ },
19
+ };
20
+ }
@@ -0,0 +1,31 @@
1
+ export default function navNode() {
2
+ return {
3
+ id: null,
4
+ hidden: true,
5
+ children: [],
6
+ init() {
7
+ this.id = this.$el.id;
8
+ },
9
+ open() {
10
+ return this.$store.nav.open[this.id];
11
+ },
12
+ getChildren() {
13
+ return this.$refs.items
14
+ ? Array.from(this.$refs.items.querySelectorAll(":scope > li"))
15
+ : [];
16
+ },
17
+ filter() {
18
+ this.hidden = true;
19
+ this.getChildren().forEach((child) => {
20
+ const data = child._x_dataStack[0];
21
+ data.filter();
22
+ if (!data.hidden) {
23
+ this.hidden = false;
24
+ }
25
+ });
26
+ },
27
+ toggle() {
28
+ this.$store.nav.open[this.id] = !this.$store.nav.open[this.id];
29
+ },
30
+ };
31
+ }
@@ -0,0 +1,39 @@
1
+ import morph from "./utils/morph";
2
+
3
+ export default function () {
4
+ return {
5
+ clearFilter() {
6
+ this.$store.nav.filter = "";
7
+ },
8
+ init() {
9
+ this.$watch("$store.nav.filter", (value) => {
10
+ const nav = this.$store.nav;
11
+ nav.filterText = value.replace(/\s/g, "").toLowerCase();
12
+ nav.filtering = nav.filterText.length > 0;
13
+ });
14
+ },
15
+ updateNav(event) {
16
+ const nav = document.getElementById("nav");
17
+ nav.style.height = `${this.$refs.shim.offsetHeight}px`;
18
+ morph(nav, event.detail.doc.getElementById("nav"));
19
+ Promise.resolve().then(() => {
20
+ this.$refs.shim.style.height = "auto";
21
+ this.$dispatch("nav:updated");
22
+ });
23
+ },
24
+ navigate(path) {
25
+ if (path instanceof Event) {
26
+ path = path.currentTarget.href;
27
+ }
28
+ history.pushState({}, null, path);
29
+ this.$dispatch("popstate");
30
+ },
31
+ focusFilter() {
32
+ this.currentFocus = this.$refs.filter;
33
+ setTimeout(() => this.$refs.filter.focus(), 0);
34
+ },
35
+ unfocusFilter() {
36
+ this.$refs.filter.blur();
37
+ },
38
+ };
39
+ }
@@ -0,0 +1,33 @@
1
+ import morph from "./utils/morph";
2
+
3
+ export default function page() {
4
+ const store = Alpine.store("page");
5
+ return {
6
+ ready: false,
7
+ sidebarOpenMobile: false,
8
+ init() {
9
+ this.$nextTick(() => (this.ready = true));
10
+ },
11
+ splitProps: {
12
+ minSize: 200,
13
+ onDrag(splits) {
14
+ Alpine.store("nav").width = Math.min(splits[0], 500);
15
+ },
16
+ },
17
+ async fetchHTML() {
18
+ const response = await fetch(window.document.location);
19
+ if (!response.ok) return window.location.reload();
20
+ const html = await response.text();
21
+ store.doc = new DOMParser().parseFromString(html, "text/html");
22
+ return store.doc;
23
+ },
24
+ updateTitle() {
25
+ document.title = store.doc.title;
26
+ },
27
+ render() {
28
+ if (this.ready) {
29
+ morph(this.$el, store.doc.getElementById(this.$el.id));
30
+ }
31
+ },
32
+ };
33
+ }
@@ -0,0 +1,13 @@
1
+ export default function clipboard() {
2
+ return {
3
+ content: null,
4
+ done: false,
5
+ save() {
6
+ this.$clipboard(this.content);
7
+ this.done = true;
8
+ setTimeout(() => {
9
+ this.done = false;
10
+ }, 1000);
11
+ },
12
+ };
13
+ }
@@ -0,0 +1,16 @@
1
+ import morph from "morphdom";
2
+
3
+ export default function (from, to, opts = {}) {
4
+ morph(from, to, {
5
+ onBeforeElUpdated: function (fromEl, toEl) {
6
+ if (fromEl._x_dataStack) {
7
+ Alpine.clone(fromEl, toEl);
8
+ }
9
+ if (fromEl.isEqualNode(toEl)) {
10
+ return false;
11
+ }
12
+ return true;
13
+ },
14
+ ...opts,
15
+ });
16
+ }
@@ -0,0 +1,44 @@
1
+ // Adapted from: https://github.com/alpine-collective/toolkit
2
+
3
+ export default function (Alpine) {
4
+ // Create reactive data context
5
+ let data = Alpine.reactive({ screensize: window.innerWidth });
6
+
7
+ // Configuration
8
+ const defaultBreakpoints = {
9
+ xs: 0,
10
+ sm: 640,
11
+ md: 768,
12
+ lg: 1024,
13
+ xl: 1280,
14
+ "2xl": 1536,
15
+ };
16
+
17
+ const breakpoints =
18
+ window.AlpineMagicHelpersConfig &&
19
+ window.AlpineMagicHelpersConfig.breakpoints
20
+ ? window.AlpineMagicHelpersConfig.breakpoints
21
+ : defaultBreakpoints;
22
+
23
+ window.addEventListener("resize", () => {
24
+ data.screensize = window.innerWidth;
25
+ });
26
+
27
+ Alpine.magic("screen", () => (breakpoint) => {
28
+ let width = data.screensize;
29
+
30
+ if (Number.isInteger(breakpoint)) return breakpoint <= width;
31
+
32
+ // Check if breakpoint exists
33
+ if (breakpoints[breakpoint] === undefined) {
34
+ throw Error(
35
+ "Undefined $screen property: " +
36
+ breakpoint +
37
+ ". Supported properties: " +
38
+ Object.keys(breakpoints).join(", ")
39
+ );
40
+ }
41
+
42
+ return breakpoints[breakpoint] <= width;
43
+ });
44
+ }
@@ -1,4 +1,4 @@
1
- export default function () {
1
+ export default function sizeObserver() {
2
2
  return {
3
3
  observedWidth: 0,
4
4
  observedHeight: 0,
@@ -1,7 +1,7 @@
1
1
  import Split from "split-grid";
2
2
 
3
3
  export default function (props) {
4
- const app = Alpine.store("app");
4
+ const page = Alpine.store("page");
5
5
  return {
6
6
  init() {
7
7
  Split({
@@ -11,14 +11,14 @@ export default function (props) {
11
11
  minSize: props.minSize,
12
12
  writeStyle() {},
13
13
  onDrag(dir, track, style) {
14
- splits = style.split(" ").map((num) => parseInt(num));
14
+ const splits = style.split(" ").map((num) => parseInt(num));
15
15
  props.onDrag(splits);
16
16
  },
17
17
  onDragStart() {
18
- app.reflowing = true;
18
+ page.reflowing = true;
19
19
  },
20
20
  onDragEnd() {
21
- app.reflowing = false;
21
+ page.reflowing = false;
22
22
  },
23
23
  });
24
24
  },
@@ -0,0 +1,11 @@
1
+ export default function inspector() {
2
+ const inspector = Alpine.store("inspector");
3
+ return {
4
+ switchTo(id) {
5
+ inspector.active = id;
6
+ },
7
+ active(id) {
8
+ return inspector.active === id;
9
+ },
10
+ };
11
+ }
@@ -0,0 +1,39 @@
1
+ export default function preview() {
2
+ const app = Alpine.store("page");
3
+ const preview = Alpine.store("preview");
4
+ return {
5
+ init() {
6
+ this.root = this.$el;
7
+ },
8
+ onResize(e) {
9
+ const size =
10
+ this.resizeStartSize - (this.resizeStartPosition - e.pageX) * 2;
11
+ const parentSize = this.root.parentElement.clientWidth;
12
+ const percentSize = (Math.round(size) / parentSize) * 100;
13
+ const minWidth = (300 / parentSize) * 100;
14
+ preview.width = `${Math.min(Math.max(percentSize, minWidth), 100)}%`;
15
+ },
16
+ onResizeStart(e) {
17
+ app.reflowing = true;
18
+ this.onResize = this.onResize.bind(this);
19
+ this.onResizeEnd = this.onResizeEnd.bind(this);
20
+ this.resizeStartPosition = e.pageX;
21
+ this.resizeStartSize = this.root.clientWidth;
22
+ window.addEventListener("pointermove", this.onResize);
23
+ window.addEventListener("pointerup", this.onResizeEnd);
24
+ },
25
+ onResizeEnd() {
26
+ window.removeEventListener("pointermove", this.onResize);
27
+ window.removeEventListener("pointerup", this.onResizeEnd);
28
+ app.reflowing = false;
29
+ },
30
+ toggleFullWidth() {
31
+ if (preview.width === "100%" && preview.lastWidth) {
32
+ preview.width = preview.lastWidth;
33
+ } else {
34
+ preview.lastWidth = preview.width;
35
+ preview.width = "100%";
36
+ }
37
+ },
38
+ };
39
+ }
@@ -0,0 +1,14 @@
1
+ export default function workbench() {
2
+ const inspector = Alpine.store("inspector");
3
+ return {
4
+ previewViewportHeight: 0,
5
+ previewViewportWidth: 0,
6
+ splitProps: {
7
+ direction: "vertical",
8
+ minSize: 200,
9
+ onDrag(splits) {
10
+ inspector.height = splits[2];
11
+ },
12
+ },
13
+ };
14
+ }