lookbook 0.4.6 → 0.5.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -3
  3. data/app/assets/lookbook/css/app.css +66 -5
  4. data/app/assets/lookbook/css/tooltip_theme.css +28 -0
  5. data/app/assets/lookbook/js/app.js +41 -53
  6. data/app/assets/lookbook/js/components/code.js +5 -0
  7. data/app/assets/lookbook/js/components/copy.js +16 -0
  8. data/app/assets/lookbook/js/components/filter.js +24 -0
  9. data/app/assets/lookbook/js/components/inspector.js +58 -0
  10. data/app/assets/lookbook/js/{nav/node.js → components/nav-group.js} +17 -16
  11. data/app/assets/lookbook/js/components/nav-item.js +27 -0
  12. data/app/assets/lookbook/js/components/nav.js +35 -0
  13. data/app/assets/lookbook/js/components/page.js +45 -0
  14. data/app/assets/lookbook/js/components/param.js +34 -0
  15. data/app/assets/lookbook/js/components/preview-window.js +107 -0
  16. data/app/assets/lookbook/js/components/sidebar.js +3 -0
  17. data/app/assets/lookbook/js/components/sizes.js +16 -0
  18. data/app/assets/lookbook/js/components/splitter.js +25 -0
  19. data/app/assets/lookbook/js/components/tabs.js +50 -0
  20. data/app/assets/lookbook/js/config.js +20 -0
  21. data/app/assets/lookbook/js/{utils/reloader.js → lib/socket.js} +7 -12
  22. data/app/assets/lookbook/js/lib/split.js +21 -0
  23. data/app/assets/lookbook/js/lib/utils.js +3 -0
  24. data/app/assets/lookbook/js/stores/filter.js +11 -0
  25. data/app/assets/lookbook/js/stores/inspector.js +24 -0
  26. data/app/assets/lookbook/js/stores/layout.js +12 -0
  27. data/app/assets/lookbook/js/stores/nav.js +21 -0
  28. data/app/assets/lookbook/js/stores/sidebar.js +14 -0
  29. data/app/controllers/lookbook/app_controller.rb +71 -96
  30. data/app/helpers/lookbook/application_helper.rb +48 -4
  31. data/app/views/layouts/lookbook/app.html.erb +58 -0
  32. data/app/views/layouts/lookbook/preview.html.erb +12 -0
  33. data/app/views/lookbook/components/_code.html.erb +13 -0
  34. data/app/views/lookbook/components/_copy.html.erb +14 -0
  35. data/app/views/lookbook/components/_drawer.html.erb +121 -0
  36. data/app/views/lookbook/components/_filter.html.erb +15 -0
  37. data/app/views/lookbook/{shared → components}/_header.html.erb +3 -3
  38. data/app/views/lookbook/components/_icon.html.erb +5 -0
  39. data/app/views/lookbook/components/_nav.html.erb +17 -0
  40. data/app/views/lookbook/components/_nav_collection.html.erb +5 -0
  41. data/app/views/lookbook/components/_nav_group.html.erb +14 -0
  42. data/app/views/lookbook/components/_nav_item.html.erb +23 -0
  43. data/app/views/lookbook/components/_nav_preview.html.erb +11 -0
  44. data/app/views/lookbook/components/_param.html.erb +21 -0
  45. data/app/views/lookbook/components/_preview.html.erb +52 -0
  46. data/app/views/lookbook/{app/error.html.erb → error.html.erb} +0 -0
  47. data/app/views/lookbook/index.html.erb +9 -0
  48. data/app/views/lookbook/{workbench/inspector/params → inputs}/_select.html.erb +2 -3
  49. data/app/views/lookbook/inputs/_text.html.erb +8 -0
  50. data/app/views/lookbook/inputs/_textarea.html.erb +8 -0
  51. data/app/views/lookbook/inputs/_toggle.html.erb +13 -0
  52. data/app/views/lookbook/{app/not_found.html.erb → not_found.html.erb} +2 -4
  53. data/app/views/lookbook/panels/_notes.html.erb +25 -0
  54. data/app/views/lookbook/panels/_output.html.erb +18 -0
  55. data/app/views/lookbook/panels/_params.html.erb +17 -0
  56. data/app/views/lookbook/panels/_source.html.erb +20 -0
  57. data/app/views/lookbook/show.html.erb +73 -0
  58. data/lib/lookbook/code_formatter.rb +20 -0
  59. data/lib/lookbook/engine.rb +8 -1
  60. data/lib/lookbook/lang.rb +10 -5
  61. data/lib/lookbook/preview.rb +1 -1
  62. data/lib/lookbook/preview_controller.rb +1 -1
  63. data/lib/lookbook/preview_group.rb +5 -1
  64. data/lib/lookbook/version.rb +1 -1
  65. data/lib/lookbook.rb +2 -0
  66. data/public/lookbook-assets/css/app.css +4 -0
  67. data/public/lookbook-assets/css/app.css.map +1 -0
  68. data/public/lookbook-assets/js/app.js +2 -0
  69. data/public/lookbook-assets/js/app.js.map +1 -0
  70. metadata +59 -45
  71. data/app/assets/lookbook/js/nav/leaf.js +0 -20
  72. data/app/assets/lookbook/js/nav.js +0 -38
  73. data/app/assets/lookbook/js/page.js +0 -38
  74. data/app/assets/lookbook/js/utils/clipboard.js +0 -13
  75. data/app/assets/lookbook/js/utils/morph.js +0 -19
  76. data/app/assets/lookbook/js/utils/screen.js +0 -44
  77. data/app/assets/lookbook/js/utils/size_observer.js +0 -16
  78. data/app/assets/lookbook/js/utils/split.js +0 -26
  79. data/app/assets/lookbook/js/workbench/inspector.js +0 -11
  80. data/app/assets/lookbook/js/workbench/param.js +0 -19
  81. data/app/assets/lookbook/js/workbench/preview.js +0 -39
  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/_clipboard.html.erb +0 -11
  92. data/app/views/lookbook/shared/_sidebar.html.erb +0 -45
  93. data/app/views/lookbook/shared/_workbench.html.erb +0 -12
  94. data/app/views/lookbook/workbench/_header.html.erb +0 -39
  95. data/app/views/lookbook/workbench/_inspector.html.erb +0 -38
  96. data/app/views/lookbook/workbench/_preview.html.erb +0 -24
  97. data/app/views/lookbook/workbench/inspector/_code.html.erb +0 -3
  98. data/app/views/lookbook/workbench/inspector/_notes.html.erb +0 -24
  99. data/app/views/lookbook/workbench/inspector/_params.html.erb +0 -28
  100. data/app/views/lookbook/workbench/inspector/_plain.html.erb +0 -3
  101. data/app/views/lookbook/workbench/inspector/params/_text.html.erb +0 -8
  102. data/app/views/lookbook/workbench/inspector/params/_textarea.html.erb +0 -8
  103. data/app/views/lookbook/workbench/inspector/params/_toggle.html.erb +0 -13
  104. data/public/lookbook-assets/app.css +0 -2626
  105. data/public/lookbook-assets/app.js +0 -8718
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 93eda55ac8a014e5027a7a1176653a8ecbf9f1b5a95b26820c68ef93f2402269
4
- data.tar.gz: c5a4dde94d4ce0c84298dfe5d8592d3ccaf887fe56fa21a9cb0bbc78b797604f
3
+ metadata.gz: b457fc15c2dc515cf08976d167a9a4948fdc83ae6e3fd490f957a8b17a333aa2
4
+ data.tar.gz: 26cb0811267fbbf7dabb8e47f7acd13990a0e1e649f2c96826fd028b5dc3ad0e
5
5
  SHA512:
6
- metadata.gz: 704265eb0b3c0b15417b47446ac900f14ca3bf746f9bbbabbbeb563eefe672f907d01ebbe5ab70d77633235d228ca509738826d674035528b12a0eb96dfcfa17
7
- data.tar.gz: 562c86882d1a2da3eb06b660006dc3d37223c80273d9aaaacafb9a85ee8a009081eecd1acfd8d451e850d34b4055400b9d9e8e5d452f7dcc66c2428a3928db7d
6
+ metadata.gz: 7c6669ef57191e50528f14ff0ad2fa5765b40a12996c305c88cdf298a08dcf17c9479ffc39c3b577c71d8273ae0a665f7d3dd2ca6b89d45bceb19bc146357821
7
+ data.tar.gz: d384bba43f79172295196effdd77a6b733b2f52180405ac3c7309ee0086881cc44c28a789a1cf9dfbb4ee8adaf5c1087ddbf5b6888c6cad3dd4ae8497f9f19cb
data/README.md CHANGED
@@ -79,7 +79,7 @@ Lookbook parses [Yard-style comment tags](https://rubydoc.info/gems/yard/file/do
79
79
 
80
80
  ```ruby
81
81
  # @label Basic Button
82
- # @display bg_color #fff
82
+ # @display bg_color "#fff"
83
83
  class ButtonComponentPreview < ViewComponent::Preview
84
84
 
85
85
  # Primary button
@@ -110,7 +110,7 @@ class ButtonComponentPreview < ViewComponent::Preview
110
110
  # ---------------
111
111
  # For light-on-dark screens
112
112
  #
113
- # @display bg_color #000
113
+ # @display bg_color "#000"
114
114
  def secondary
115
115
  render ButtonComponent.new(style: :inverted) do
116
116
  "Click me"
@@ -188,7 +188,7 @@ end
188
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.
189
189
 
190
190
  ```ruby
191
- # @display bg_color #eee
191
+ # @display bg_color "#eee"
192
192
  class FooComponentPreview < ViewComponent::Preview
193
193
 
194
194
  # @display max_width 500px
@@ -207,6 +207,8 @@ The `@display` tag can be applied at the preview (class) or at the example (meth
207
207
  - `<key>` must be a valid Ruby hash key name, without quotes or spaces
208
208
  - `<value>` will be parsed using the [Ruby YAML parser](https://yaml.org/YAML_for_ruby.html) to resolve the value
209
209
 
210
+ > 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.
211
+
210
212
  These display parameters can then be accessed via the `params` hash in your preview layout using `params[:lookbook][:display][<key>]`:
211
213
 
212
214
  ```html
@@ -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
 
@@ -56,16 +57,76 @@
56
57
  }
57
58
  }
58
59
 
59
- @layer utilities {
60
- .h-fill {
61
- height: fill-available;
60
+ @layer components {
61
+ #nav > ul > li > div {
62
+ @apply py-1 border-b border-gray-300;
63
+ }
64
+
65
+ .nav-toggle {
66
+ @apply flex items-center cursor-pointer pr-3 hover:bg-gray-200 hover:bg-opacity-50;
67
+ }
68
+
69
+ .nav-label {
70
+ @apply truncate w-full whitespace-nowrap text-left select-none;
71
+ }
72
+
73
+ .code {
74
+ @apply font-mono;
62
75
  }
63
76
 
64
- .min-h-fill {
65
- min-height: fill-available;
77
+ .code pre {
78
+ @apply block;
66
79
  }
67
80
 
81
+ .code.wrapped pre {
82
+ @apply whitespace-pre-wrap;
83
+ }
84
+
85
+ .code .line {
86
+ @apply leading-relaxed;
87
+ }
88
+
89
+ .code.numbered {
90
+ @apply relative pt-3;
91
+ }
92
+
93
+ .code.numbered:before {
94
+ content: "";
95
+ left: 2.7em;
96
+ @apply absolute top-0 bottom-0 border-r border-gray-200;
97
+ }
98
+
99
+ .code.numbered .line {
100
+ padding-left: calc(2.7em + 8px);
101
+ @apply relative;
102
+ }
103
+
104
+ .code .line-number {
105
+ display: inline-block;
106
+ width: calc(2.7em + 8px);
107
+ padding-top: 3px;
108
+ padding-bottom: 3px;
109
+ padding-right: 8px;
110
+ margin-right: 16px;
111
+ @apply font-mono text-right text-gray-400 flex-none text-xs absolute left-0;
112
+ }
113
+
114
+ .code .line-content {
115
+ @apply flex-none pr-4;
116
+ }
117
+
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
+ }
121
+ }
122
+
123
+ @layer utilities {
68
124
  .form-input {
69
125
  @apply border-gray-300 text-gray-700 focus:ring-indigo-300 focus:border-indigo-300 rounded-sm text-sm w-full;
70
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
+ }
71
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
+ }
@@ -1,66 +1,58 @@
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 param from "./workbench/param";
13
- import nav from "./nav";
14
- import navNode from "./nav/node";
15
- import navLeaf from "./nav/leaf";
16
- import sizeObserver from "./utils/size_observer";
17
- import reloader from "./utils/reloader";
18
- import clipboard from "./utils/clipboard";
3
+ import Persist from "@alpinejs/persist";
4
+ import Morph from "@alpinejs/morph";
5
+ import Tooltip from "@ryangjchandler/alpine-tooltip";
19
6
 
20
- window.Alpine = Alpine;
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 tabs from "./components/tabs";
17
+ import copy from "./components/copy";
18
+ import code from "./components/code";
19
+ import sizes from "./components/sizes";
20
+
21
+ import initFilterStore from "./stores/filter";
22
+ import initLayoutStore from "./stores/layout";
23
+ import initNavStore from "./stores/nav";
24
+ import initSidebarStore from "./stores/sidebar";
25
+ import initInspectorStore from "./stores/inspector";
21
26
 
22
27
  // Plugins
23
28
 
24
- Alpine.plugin(Fern);
25
- Alpine.plugin(AlpineTooltip);
26
- Alpine.plugin(AlpineClipboard);
27
- Alpine.plugin(Screen);
29
+ Alpine.plugin(Persist);
30
+ Alpine.plugin(Morph);
31
+ Alpine.plugin(Tooltip);
28
32
 
29
33
  // Stores
30
34
 
31
- Alpine.store("page", {
32
- reflowing: false,
33
- doc: window.document,
34
- });
35
-
36
- Alpine.persistedStore("nav", {
37
- width: 280,
38
- filter: "",
39
- open: {},
40
- });
41
-
42
- Alpine.persistedStore("inspector", {
43
- height: 200,
44
- active: "source",
45
- });
46
-
47
- Alpine.persistedStore("preview", {
48
- width: "100%",
49
- });
35
+ Alpine.store("filter", initFilterStore(Alpine));
36
+ Alpine.store("layout", initLayoutStore(Alpine));
37
+ Alpine.store("nav", initNavStore(Alpine));
38
+ Alpine.store("sidebar", initSidebarStore(Alpine));
39
+ Alpine.store("inspector", initInspectorStore(Alpine));
50
40
 
51
- // Components & utils
41
+ // Components
52
42
 
53
43
  Alpine.data("page", page);
54
- Alpine.data("nav", nav);
55
- Alpine.data("navNode", navNode);
56
- Alpine.data("navLeaf", navLeaf);
57
- Alpine.data("workbench", workbench);
58
- Alpine.data("preview", preview);
44
+ Alpine.data("splitter", splitter);
45
+ Alpine.data("previewWindow", previewWindow);
46
+ Alpine.data("copy", copy);
47
+ Alpine.data("code", code);
59
48
  Alpine.data("inspector", inspector);
49
+ Alpine.data("filter", filter);
60
50
  Alpine.data("param", param);
61
- Alpine.data("clipboard", clipboard);
62
- Alpine.data("sizeObserver", sizeObserver);
63
- Alpine.data("split", split);
51
+ Alpine.data("sizes", sizes);
52
+ Alpine.data("nav", nav);
53
+ Alpine.data("tabs", tabs);
54
+ Alpine.data("navItem", navItem);
55
+ Alpine.data("navGroup", navGroup);
64
56
 
65
57
  // Init
66
58
 
@@ -68,9 +60,5 @@ for (const el of document.querySelectorAll("[data-hotkey]")) {
68
60
  install(el);
69
61
  }
70
62
 
71
- if (window.SOCKET_PATH) {
72
- reloader(window.SOCKET_PATH).start();
73
- }
74
-
75
63
  window.Alpine = Alpine;
76
64
  Alpine.start();
@@ -0,0 +1,5 @@
1
+ export default function code() {
2
+ return {
3
+ wrap: false,
4
+ };
5
+ }
@@ -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 && $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,58 @@
1
+ import sizeObserver from "./sizes";
2
+
3
+ export default function inspector() {
4
+ return {
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);
16
+ },
17
+ get orientation() {
18
+ return this.$store.inspector.drawer.orientation;
19
+ },
20
+ get view() {
21
+ return this.$store.inspector.preview.view;
22
+ },
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
+ isActivePanel(panel) {
36
+ return this.$store.inspector.drawer.active == panel;
37
+ },
38
+ switchPanel(panel) {
39
+ this.$store.inspector.drawer.active = panel;
40
+ },
41
+ toggleView() {
42
+ this.$store.inspector.preview.view =
43
+ this.view === "html" ? "preview" : "html";
44
+ },
45
+ toggleOrientation() {
46
+ this.$store.inspector.drawer.orientation =
47
+ this.orientation === "horizontal" ? "vertical" : "horizontal";
48
+ },
49
+ toggleDrawer() {
50
+ this.$store.inspector.drawer.hidden =
51
+ !this.$store.inspector.drawer.hidden;
52
+ },
53
+ preview: {
54
+ width: null,
55
+ height: null,
56
+ },
57
+ };
58
+ }
@@ -1,43 +1,44 @@
1
- export default function navNode() {
1
+ import { getAlpineData } from "../lib/utils";
2
+
3
+ export default function navGroup() {
2
4
  return {
3
- id: null,
4
- hidden: true,
5
+ hidden: false,
5
6
  children: [],
6
- init() {
7
- this.id = this.$el.id;
7
+ get id() {
8
+ return this.$root.id;
8
9
  },
9
- open() {
10
- return this.$store.nav.open[this.id];
10
+ get open() {
11
+ return this.$store.nav.isOpen(this.id);
12
+ },
13
+ toggle() {
14
+ this.$store.nav.toggle(this.id);
11
15
  },
12
16
  getChildren() {
13
17
  return this.$refs.items
14
- ? Array.from(this.$refs.items.querySelectorAll(":scope > li"))
18
+ ? Array.from(this.$refs.items.querySelectorAll(":scope > li > div"))
15
19
  : [];
16
20
  },
17
21
  navigateToFirstChild() {
18
- if (this.open()) {
22
+ if (this.open) {
19
23
  const child = this.firstVisibleChild();
20
24
  if (child) {
21
25
  const link = child.querySelector(":scope > a.nav-link");
22
26
  if (link) {
23
- this.navigate(link.getAttribute("href"));
27
+ this.setLocation(link.getAttribute("href"));
24
28
  }
25
29
  }
26
30
  }
27
31
  },
28
- filter() {
32
+ filter(text) {
29
33
  this.hidden = true;
30
34
  this.getChildren().forEach((child) => {
31
- const data = child._x_dataStack[0];
32
- data.filter();
35
+ const data = getAlpineData(child);
36
+ data.filter(text);
33
37
  if (!data.hidden) {
34
38
  this.hidden = false;
35
39
  }
36
40
  });
37
41
  },
38
- toggle() {
39
- this.$store.nav.open[this.id] = !this.$store.nav.open[this.id];
40
- },
41
42
  firstVisibleChild() {
42
43
  return this.getChildren().find((child) => {
43
44
  return child._x_dataStack
@@ -0,0 +1,27 @@
1
+ export default function navItem(matchers) {
2
+ return {
3
+ hidden: false,
4
+ get id() {
5
+ return this.$root.id;
6
+ },
7
+ get path() {
8
+ return this.$root.getAttribute("data-path");
9
+ },
10
+ get active() {
11
+ return this.$store.nav.active === this.id;
12
+ },
13
+ navigate() {
14
+ this.setLocation(this.path);
15
+ this.$store.sidebar.open = false;
16
+ },
17
+ filter(text) {
18
+ this.hidden = false;
19
+ if (text.length) {
20
+ const matched = matchers.map((m) => m.includes(text));
21
+ this.hidden = !matched.filter((m) => m).length;
22
+ } else {
23
+ this.hidden = false;
24
+ }
25
+ },
26
+ };
27
+ }
@@ -0,0 +1,35 @@
1
+ import { getAlpineData } from "../lib/utils";
2
+
3
+ export default function nav() {
4
+ return {
5
+ empty: false,
6
+ init() {
7
+ this.$watch("$store.filter.text", () => this.filter());
8
+ this.$nextTick(() => {
9
+ this.setActive();
10
+ this.filter();
11
+ });
12
+ },
13
+ filter() {
14
+ this.empty = true;
15
+ this.getChildren().forEach((child) => {
16
+ const data = getAlpineData(child);
17
+ data.filter(this.$store.filter.text);
18
+ if (!data.hidden) {
19
+ this.empty = false;
20
+ }
21
+ });
22
+ },
23
+ getChildren() {
24
+ return this.$refs.items
25
+ ? Array.from(this.$refs.items.querySelectorAll(":scope > li > div"))
26
+ : [];
27
+ },
28
+ setActive() {
29
+ const target = this.$el.querySelector(
30
+ `[data-path="${window.location.pathname}"]`
31
+ );
32
+ this.$store.nav.active = target ? target.id : "";
33
+ },
34
+ };
35
+ }
@@ -0,0 +1,45 @@
1
+ import createSocket from "../lib/socket";
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
+
19
+ export default function page() {
20
+ return {
21
+ init() {
22
+ const socket = createSocket(window.SOCKET_PATH);
23
+ socket.addListener("Lookbook::ReloadChannel", () => this.refresh());
24
+ },
25
+ async update() {
26
+ const response = await fetch(window.document.location);
27
+ if (!response.ok) return window.location.reload();
28
+ const html = await response.text();
29
+ this.morph(new DOMParser().parseFromString(html, "text/html"));
30
+ },
31
+ setLocation(loc) {
32
+ const path = loc instanceof Event ? loc.currentTarget.href : loc;
33
+ history.pushState({}, null, path);
34
+ this.$dispatch("popstate");
35
+ },
36
+ refresh() {
37
+ this.$dispatch("popstate");
38
+ },
39
+ morph(dom) {
40
+ const pageHtml = dom.getElementById(this.$root.id).outerHTML;
41
+ Alpine.morph(this.$root, pageHtml, morphOpts);
42
+ this.$dispatch("page:morphed");
43
+ },
44
+ };
45
+ }
@@ -0,0 +1,34 @@
1
+ import debounce from "debounce";
2
+
3
+ export default function param(name, value, opts = {}) {
4
+ return {
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());
16
+ }
17
+ },
18
+ setFocus() {
19
+ setTimeout(() => this.$root.focus(), 0);
20
+ },
21
+ updateIfValid() {
22
+ if (this.validate()) this.update();
23
+ },
24
+ update() {
25
+ const searchParams = new URLSearchParams(window.location.search);
26
+ searchParams.set(this.name, this.value);
27
+ const path = location.href.replace(location.search, "");
28
+ this.setLocation(`${path}?${searchParams.toString()}`);
29
+ },
30
+ validate() {
31
+ return this.$root.reportValidity ? this.$root.reportValidity() : true;
32
+ },
33
+ };
34
+ }