lookbook 0.6.1 → 0.7.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 +233 -5
- data/app/assets/lookbook/css/app.css +15 -3
- data/app/assets/lookbook/js/app.js +8 -0
- data/app/assets/lookbook/js/components/app.js +55 -0
- data/app/assets/lookbook/js/components/embed.js +89 -0
- data/app/assets/lookbook/js/components/filter.js +13 -2
- data/app/assets/lookbook/js/components/nav-group.js +3 -6
- data/app/assets/lookbook/js/components/nav-item.js +3 -1
- data/app/assets/lookbook/js/components/nav.js +8 -15
- data/app/assets/lookbook/js/components/page.js +19 -41
- data/app/assets/lookbook/js/components/sidebar.js +16 -1
- data/app/assets/lookbook/js/components/splitter.js +1 -1
- data/app/assets/lookbook/js/components/tabs.js +3 -1
- data/app/assets/lookbook/js/embed.js +1 -0
- data/app/assets/lookbook/js/lib/split.js +0 -6
- data/app/assets/lookbook/js/stores/layout.js +3 -0
- data/app/assets/lookbook/js/stores/pages.js +5 -0
- data/app/assets/lookbook/js/stores/sidebar.js +2 -1
- data/app/controllers/lookbook/application_controller.rb +28 -0
- data/app/controllers/lookbook/page_controller.rb +20 -0
- data/app/controllers/lookbook/pages_controller.rb +40 -0
- data/app/controllers/lookbook/{app_controller.rb → previews_controller.rb} +39 -67
- data/app/helpers/lookbook/application_helper.rb +8 -61
- data/app/helpers/lookbook/component_helper.rb +40 -0
- data/app/helpers/lookbook/output_helper.rb +15 -0
- data/app/helpers/lookbook/page_helper.rb +46 -0
- data/app/helpers/lookbook/preview_helper.rb +1 -1
- data/app/views/layouts/lookbook/application.html.erb +30 -0
- data/app/views/layouts/lookbook/basic.html.erb +7 -0
- data/app/views/layouts/lookbook/preview.html.erb +5 -1
- data/app/views/layouts/lookbook/skeleton.html.erb +28 -0
- data/app/views/lookbook/components/_branding.html.erb +8 -0
- data/app/views/lookbook/components/_code.html.erb +2 -2
- data/app/views/lookbook/components/_copy_button.html.erb +11 -0
- data/app/views/lookbook/components/_drawer.html.erb +4 -16
- data/app/views/lookbook/components/_embed.html.erb +39 -0
- data/app/views/lookbook/components/_filter.html.erb +8 -5
- data/app/views/lookbook/components/_header.html.erb +2 -4
- data/app/views/lookbook/components/_icon.html.erb +2 -2
- data/app/views/lookbook/components/_nav.html.erb +7 -8
- data/app/views/lookbook/components/_nav_group.html.erb +1 -1
- data/app/views/lookbook/components/_nav_item.html.erb +4 -3
- data/app/views/lookbook/components/_nav_page.html.erb +22 -0
- data/app/views/lookbook/components/_nav_preview.html.erb +1 -1
- data/app/views/lookbook/components/_not_found.html.erb +11 -0
- data/app/views/lookbook/components/_param.html.erb +1 -1
- data/app/views/lookbook/components/_preview.html.erb +16 -10
- data/app/views/lookbook/components/_sidebar.html.erb +55 -0
- data/app/views/lookbook/pages/not_found.html.erb +15 -0
- data/app/views/lookbook/pages/show.html.erb +71 -0
- data/app/views/lookbook/previews/error.html.erb +1 -0
- data/app/views/lookbook/{inputs → previews/inputs}/_select.html.erb +0 -0
- data/app/views/lookbook/{inputs → previews/inputs}/_text.html.erb +0 -0
- data/app/views/lookbook/{inputs → previews/inputs}/_textarea.html.erb +0 -0
- data/app/views/lookbook/{inputs → previews/inputs}/_toggle.html.erb +0 -0
- data/app/views/lookbook/{not_found.html.erb → previews/not_found.html.erb} +2 -2
- data/app/views/lookbook/{panels → previews/panels}/_notes.html.erb +2 -2
- data/app/views/lookbook/{panels → previews/panels}/_output.html.erb +0 -0
- data/app/views/lookbook/{panels → previews/panels}/_params.html.erb +0 -0
- data/app/views/lookbook/{panels → previews/panels}/_preview.html.erb +0 -0
- data/app/views/lookbook/{panels → previews/panels}/_source.html.erb +0 -0
- data/app/views/lookbook/{show.html.erb → previews/show.html.erb} +0 -0
- data/config/routes.rb +9 -4
- data/lib/lookbook/code_formatter.rb +22 -1
- data/lib/lookbook/code_inspector.rb +73 -0
- data/lib/lookbook/collection.rb +110 -8
- data/lib/lookbook/engine.rb +59 -32
- data/lib/lookbook/features.rb +1 -1
- data/lib/lookbook/markdown.rb +31 -0
- data/lib/lookbook/page.rb +146 -0
- data/lib/lookbook/page_collection.rb +11 -0
- data/lib/lookbook/parser.rb +2 -0
- data/lib/lookbook/preview.rb +52 -56
- data/lib/lookbook/preview_collection.rb +15 -0
- data/lib/lookbook/preview_example.rb +27 -29
- data/lib/lookbook/preview_group.rb +12 -6
- data/lib/lookbook/utils.rb +74 -0
- data/lib/lookbook/version.rb +1 -1
- data/lib/lookbook.rb +18 -1
- data/public/lookbook-assets/css/app.css +1 -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
- data/public/lookbook-assets/js/embed.js +2 -0
- data/public/lookbook-assets/js/embed.js.map +1 -0
- metadata +44 -18
- data/app/assets/lookbook/js/lib/utils.js +0 -3
- data/app/views/layouts/lookbook/app.html.erb +0 -60
- data/app/views/lookbook/error.html.erb +0 -1
- data/lib/lookbook/taggable.rb +0 -46
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 40ec6640c4c188089ff874cb11fb129ddc6c541d078681b1b3dea133f3afaedf
|
4
|
+
data.tar.gz: 331f4693438b931929b40d58c4b7c0d0cf0ece9089891d8e5863c8971f5e4138
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a538f52d85d4d5e833c2549442b001e44e749b42e87a5bbe4676e718300405b7b9b32d6efd18b1ce23dedcfefce8c82a4f4128236fa83dcff570b7f609b15004
|
7
|
+
data.tar.gz: a5a6fe4dbba3e951441b5c4fb64b6a268de687c99bb7779892d5e819eeae19fbcee8d740c749808671bf65202fc6af581a958869f3fb67c6f9aac5c5af364007
|
data/README.md
CHANGED
@@ -12,6 +12,12 @@
|
|
12
12
|
|
13
13
|
---
|
14
14
|
|
15
|
+
<div align="center">
|
16
|
+
<a href="#installing">Installing</a> • <a href="#previews">Previews</a> • <a href="#pages">Pages</a> • <a href="#config">Configuration</a>
|
17
|
+
</div>
|
18
|
+
|
19
|
+
---
|
20
|
+
|
15
21
|
**Lookbook gives [ViewComponent](http://viewcomponent.org/)-based projects a _ready-to-go_ development UI for navigating, inspecting and interacting with component previews.**
|
16
22
|
|
17
23
|
It uses (and extends) the native [ViewComponent preview functionality](https://viewcomponent.org/guide/previews.html), so you don't need to learn a new DSL or create any extra files to get up and running.
|
@@ -29,6 +35,7 @@ Lookbook uses [RDoc/Yard-style comment tags](#annotating-preview-files) to exten
|
|
29
35
|
- Use comment tag annotations for granular customisation of the preview experience
|
30
36
|
- Fully compatible with standard the ViewComponent preview system
|
31
37
|
- In-browser live-editable preview parameters (similar to basic Storybook Controls/Knobs)
|
38
|
+
- [**Experimental**] Markdown-powerered documentation pages with embeddable previews
|
32
39
|
|
33
40
|
## Lookbook demo
|
34
41
|
|
@@ -73,13 +80,13 @@ If you would like to expose the Lookbook UI in production as well as in developm
|
|
73
80
|
2. Add `config.view_component.show_previews = true` to `config/environments/production.rb`
|
74
81
|
|
75
82
|
|
76
|
-
|
83
|
+
<h2 id="previews">Previews</h2>
|
77
84
|
|
78
85
|
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
86
|
|
80
87
|
> If you are new to ViewComponent development, checkout the ViewComponent [documentation](https://viewcomponent.org/guide/) on how to get started developing your components and [creating previews](https://viewcomponent.org/guide/previews.html).
|
81
88
|
|
82
|
-
|
89
|
+
### Annotating preview files
|
83
90
|
|
84
91
|
Lookbook parses [Yard-style comment tags](https://rubydoc.info/gems/yard/file/docs/Tags.md) in your preview classes to customise and extend the standard ViewComponent preview experience:
|
85
92
|
|
@@ -474,11 +481,217 @@ class ProfileCardComponentPreview < ViewComponent::Preview
|
|
474
481
|
end
|
475
482
|
```
|
476
483
|
|
477
|
-
|
484
|
+
---
|
485
|
+
|
486
|
+
<h2 id="pages">🚧 Documentation Pages [experimental]</h2>
|
487
|
+
|
488
|
+
If you need to add more long-form documentation to live alongside your component previews you can do so using Lookbook's markdown-powered `pages` system.
|
489
|
+
|
490
|
+
> ⚠️ This feature is currently flagged as an **experimental** feature which requires [feature opt-in](#experimental-features) to use. Its API and implementation may change before it is released.
|
491
|
+
>
|
492
|
+
> To enable support for pages in your project, add `config.lookbook.experimental_features = [:pages]` into your application configuration file.
|
493
|
+
|
494
|
+
### Pages demo
|
495
|
+
|
496
|
+
For an example of some pages in Lookbook, check out the [example pages](https://lookbook-demo-app.herokuapp.com/lookbook) in the Lookbook demo app and the associated [page files](https://github.com/allmarkedup/lookbook-demo/tree/main/test/components/docs) in the demo repo.
|
497
|
+
|
498
|
+
### Usage
|
499
|
+
|
500
|
+
By default, pages should be placed in the `test/components/docs` directory (although this can be customised) and can be nested in directories as deeply as required.
|
501
|
+
|
502
|
+
Pages must have either a `.html.erb` or a `.md.erb` file extension. All pages are rendered as ERB templates but `.md.erb` files will also additionally be run through a markdown parser.
|
503
|
+
|
504
|
+
Pages can optionally make use of a **YAML frontmatter block** to customise the behaviour and content of the page itself.
|
505
|
+
|
506
|
+
An example page might look like this:
|
507
|
+
|
508
|
+
```markdown
|
509
|
+
---
|
510
|
+
title: An example page
|
511
|
+
label: Nice example
|
512
|
+
---
|
513
|
+
|
514
|
+
This is an example page. If it has a `.md.erb` file extension its
|
515
|
+
contents will be run through a Markdown parser/renderer before display.
|
516
|
+
|
517
|
+
Fenced code blocks are fully supported and will be highlighted appropriately.
|
518
|
+
|
519
|
+
ERB can be used in here.
|
520
|
+
The template will be rendered **before** being parsed as Markdown.
|
521
|
+
|
522
|
+
You can can access data about the page using the `@page` variable.
|
523
|
+
The title of this page is "<%= @page.title %>".
|
524
|
+
```
|
525
|
+
|
526
|
+
### YAML Frontmatter
|
527
|
+
|
528
|
+
Pages can use an optional YAML frontmatter block to configure the behaviour of the page and to provide custom data, if required.
|
529
|
+
|
530
|
+
The following page options can be customised via frontmatter:
|
531
|
+
|
532
|
+
* `id` - a custom page ID that can be used for linking to it from other pages
|
533
|
+
* `label` - The name of the page that will be displayed in the navigation (auto-generated from the file name if not set)
|
534
|
+
* `title` - The main page title displayed on the page (defaults to the label value if not set).
|
535
|
+
* `hidden` - If `false` the page will not appear in the navigation but will still be accessible at it's URL (useful for pages that are still in development) [default: `true`]
|
536
|
+
* `landing` - Set to `true` to use the page as the Lookbook landing page [default: `false`]
|
537
|
+
* `header` - Set to `false` to hide the page header containing the page title [default: `true`]
|
538
|
+
* `footer` - Set to `false` to hide the page footer containing the previous/next page links [default: `true`]
|
539
|
+
* `data` - Optional hash of custom data to make available for use in the page - see info on [page variables](#page-variables) below. [default: `{}`]
|
478
540
|
|
479
|
-
|
541
|
+
#### Frontmatter defaults
|
480
542
|
|
481
|
-
|
543
|
+
You can set global default values for page options in the application configuration:
|
544
|
+
|
545
|
+
```ruby
|
546
|
+
# config/application.rb
|
547
|
+
config.lookbook.page_options = {
|
548
|
+
footer: false,
|
549
|
+
data: {
|
550
|
+
brand_colors: {
|
551
|
+
red: #ff0000
|
552
|
+
}
|
553
|
+
}
|
554
|
+
}
|
555
|
+
```
|
556
|
+
|
557
|
+
These will be merged with any page-level frontmatter data. Options set in the frontmatter will override those set at the global level (apart from `data`, which will be deep-merged with the any globally defined data).
|
558
|
+
|
559
|
+
### Page variables
|
560
|
+
|
561
|
+
All pages have the following variables available for use in the page template:
|
562
|
+
|
563
|
+
* `@page` - The current page object
|
564
|
+
* `@next_page` - The next page object (if available)
|
565
|
+
* `@previous_page` - The previous page object (if available)
|
566
|
+
* `@pages` - Collection of all pages
|
567
|
+
|
568
|
+
Page objects have access to frontmatter variables:
|
569
|
+
|
570
|
+
```ruby
|
571
|
+
The page title is <%= @page.title %>
|
572
|
+
|
573
|
+
Our brand color hex value is <%= @page.data[:brand_colors][:red] %>
|
574
|
+
```
|
575
|
+
|
576
|
+
### Ordering pages and directories
|
577
|
+
|
578
|
+
If you want to enforce a specific order for pages and directories in the Lookbook navigation you can prefix the file/directory basename with an 'order number' integer value followed by an underscore or hyphen.
|
579
|
+
|
580
|
+
For example: `01_example_page.md.erb` will be displayed first in the navigation (`01`) within the directory it is in.
|
581
|
+
|
582
|
+
The integer value will be parsed out from the filename so that it doesn't appear in navigation labels or URLs, and the value itself will be used as a 'position' number when sorting the navigation items.
|
583
|
+
|
584
|
+
For example, an ordered directory of pages might look like:
|
585
|
+
|
586
|
+
```
|
587
|
+
test/components/docs/
|
588
|
+
├── 01_overview.md.erb
|
589
|
+
├── 02_implementation_notes/
|
590
|
+
│ ├── 01_slots.md.erb
|
591
|
+
│ └── 02_html_attributes.md.erb
|
592
|
+
└── 03_helpful_examples/
|
593
|
+
├── 01_basic_components.md.erb
|
594
|
+
└── 02_complex_components.md.erb
|
595
|
+
```
|
596
|
+
|
597
|
+
Without the number prefixes on the file names the pages may not have appeared in the navigation in the desired order.
|
598
|
+
|
599
|
+
### Linking to other pages
|
600
|
+
|
601
|
+
You can get the path to a page using the `page_path` helper. This accepts a page `id` (as a `Symbol`) or a page object:
|
602
|
+
|
603
|
+
```markdown
|
604
|
+
Visit the [about page](<%= page_path :about %>)
|
605
|
+
|
606
|
+
Go to the [next page](<%= page_path @next_page %>)
|
607
|
+
```
|
608
|
+
|
609
|
+
Page ids can be set in the YAML frontmatter block for that page:
|
610
|
+
|
611
|
+
```
|
612
|
+
---
|
613
|
+
id: about
|
614
|
+
---
|
615
|
+
|
616
|
+
This is the about page.
|
617
|
+
```
|
618
|
+
|
619
|
+
### Embedding previews
|
620
|
+
|
621
|
+
You can embed preview examples from your project directly into the documentation pages using the `embed` helper, which renders an iframe with the rendered preview in it at any point in your document.
|
622
|
+
|
623
|
+
The output looks like this:
|
624
|
+
|
625
|
+
<img src=".github/assets/preview_embed.png">
|
626
|
+
|
627
|
+
To specify which preview example to render, the helper accepts a preview class and a method name (as a symbol), like this:
|
628
|
+
|
629
|
+
```erb
|
630
|
+
<%= embed Elements:ButtonComponentPreview, :default %>
|
631
|
+
```
|
632
|
+
|
633
|
+
#### Preview params
|
634
|
+
|
635
|
+
If you have configured your examples to accept preview params (see the [`@param`](#param-tag) docs), then you can supply values for those params when rendering the embedded preview:
|
636
|
+
|
637
|
+
```erb
|
638
|
+
<%= embed Elements:ButtonComponentPreview, :default, params: {
|
639
|
+
icon: "plus",
|
640
|
+
text: "Add new"
|
641
|
+
} %>
|
642
|
+
```
|
643
|
+
|
644
|
+
### Displaying code
|
645
|
+
|
646
|
+
You can use language-scoped [fenced code blocks](https://www.markdownguide.org/extended-syntax/#fenced-code-blocks) in the markdown file to render nicely highlighted code examples.
|
647
|
+
|
648
|
+
However, if you are not using Markdown, or need a little more control, you can use the `code` helper instead:
|
649
|
+
|
650
|
+
```erb
|
651
|
+
<%= code do %>
|
652
|
+
# code goes here
|
653
|
+
<% end %>
|
654
|
+
```
|
655
|
+
|
656
|
+
The default language is `ruby`. To highlight a different language you need to specify it's name as an argument:
|
657
|
+
|
658
|
+
```erb
|
659
|
+
<%= code :html do %>
|
660
|
+
<!-- code goes here -->
|
661
|
+
<% end %>
|
662
|
+
```
|
663
|
+
|
664
|
+
> Lookbook uses [Rouge](https://github.com/rouge-ruby/rouge) for syntax highlighting. You can find a [full list of supported languages here](https://github.com/rouge-ruby/rouge/blob/master/docs/Languages.md).
|
665
|
+
|
666
|
+
---
|
667
|
+
|
668
|
+
### Pages configuration
|
669
|
+
|
670
|
+
These options can be set in your application configuration files to customise the pages behaviour.
|
671
|
+
|
672
|
+
#### `page_paths`
|
673
|
+
|
674
|
+
An array of directories to look for pages in.
|
675
|
+
Default: `["test/previews/docs"]`
|
676
|
+
|
677
|
+
```ruby
|
678
|
+
config.lookbook.page_paths = ["path/to/my/pages"]
|
679
|
+
```
|
680
|
+
|
681
|
+
#### `page_route`
|
682
|
+
|
683
|
+
The URL segment used to prefix page routes.
|
684
|
+
Default: `pages`
|
685
|
+
|
686
|
+
```ruby
|
687
|
+
config.lookbook.page_route = `docs`
|
688
|
+
```
|
689
|
+
|
690
|
+
<h2 id="config">General Configuration</h2>
|
691
|
+
|
692
|
+
Lookbook will use the ViewComponent [configuration](https://viewcomponent.org/api.html#configuration) for your project to find and render your previews so you generally you won't need to configure much else separately.
|
693
|
+
|
694
|
+
However the following Lookbook-specific configuration options are also available:
|
482
695
|
|
483
696
|
### UI auto-refresh
|
484
697
|
|
@@ -508,6 +721,16 @@ config.lookbook.ui_favicon = "/path/to/my/favicon.png"
|
|
508
721
|
|
509
722
|
> To disable the favicon entirely, set the value to `false`.
|
510
723
|
|
724
|
+
### Project name
|
725
|
+
|
726
|
+
Specify a project name to display in the top left of the UI (instead of the default "Lookbook"):
|
727
|
+
|
728
|
+
```ruby
|
729
|
+
config.lookbook.project_name = "My Project"
|
730
|
+
```
|
731
|
+
|
732
|
+
> If you don't want to display a project name at all, set the value to `false`.
|
733
|
+
|
511
734
|
|
512
735
|
<h3 id="experimental-features">Experimental features opt-in</h3>
|
513
736
|
|
@@ -523,6 +746,11 @@ To opt into individual experimental features, include the name of the feature in
|
|
523
746
|
config.lookbook.experimental_features = ["feature_name"]
|
524
747
|
```
|
525
748
|
|
749
|
+
The current experimental features that can be opted into are:
|
750
|
+
|
751
|
+
- `pages`: Markdown-powered documentation pages with embeddable previews
|
752
|
+
|
753
|
+
|
526
754
|
#### Opting into all experimental features (not recommended!)
|
527
755
|
|
528
756
|
If you want to live life on the bleeding-edge you can opt-in to all current **and future** experimental features (usual caveats apply):
|
@@ -58,7 +58,7 @@
|
|
58
58
|
}
|
59
59
|
|
60
60
|
@layer components {
|
61
|
-
#nav > ul > li > div {
|
61
|
+
.unsectioned > #nav > ul > li > div {
|
62
62
|
@apply py-1 border-b border-gray-300;
|
63
63
|
}
|
64
64
|
|
@@ -87,7 +87,7 @@
|
|
87
87
|
}
|
88
88
|
|
89
89
|
.code.numbered {
|
90
|
-
@apply relative
|
90
|
+
@apply relative;
|
91
91
|
}
|
92
92
|
|
93
93
|
.code.numbered:before {
|
@@ -97,7 +97,7 @@
|
|
97
97
|
}
|
98
98
|
|
99
99
|
.code.numbered .line {
|
100
|
-
padding-left: calc(2.7em +
|
100
|
+
padding-left: calc(2.7em + 14px);
|
101
101
|
@apply relative;
|
102
102
|
}
|
103
103
|
|
@@ -115,6 +115,18 @@
|
|
115
115
|
@apply flex-none pr-4;
|
116
116
|
}
|
117
117
|
|
118
|
+
.prose .code {
|
119
|
+
@apply !bg-white border border-gray-300 text-gray-600 my-8 text-sm rounded-md py-4 overflow-auto max-w-3xl w-full mx-auto;
|
120
|
+
}
|
121
|
+
|
122
|
+
.prose .code:not(.numbered) .line {
|
123
|
+
@apply px-4;
|
124
|
+
}
|
125
|
+
|
126
|
+
.prose .embed {
|
127
|
+
@apply my-8;
|
128
|
+
}
|
129
|
+
|
118
130
|
.resize-handle {
|
119
131
|
@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
132
|
}
|
@@ -4,11 +4,13 @@ import Persist from "@alpinejs/persist";
|
|
4
4
|
import Morph from "@alpinejs/morph";
|
5
5
|
import Tooltip from "@ryangjchandler/alpine-tooltip";
|
6
6
|
|
7
|
+
import app from "./components/app";
|
7
8
|
import page from "./components/page";
|
8
9
|
import inspector from "./components/inspector";
|
9
10
|
import previewWindow from "./components/preview-window";
|
10
11
|
import filter from "./components/filter";
|
11
12
|
import param from "./components/param";
|
13
|
+
import sidebar from "./components/sidebar";
|
12
14
|
import nav from "./components/nav";
|
13
15
|
import navItem from "./components/nav-item";
|
14
16
|
import navGroup from "./components/nav-group";
|
@@ -17,12 +19,14 @@ import tabs from "./components/tabs";
|
|
17
19
|
import copy from "./components/copy";
|
18
20
|
import code from "./components/code";
|
19
21
|
import sizes from "./components/sizes";
|
22
|
+
import embed from "./components/embed";
|
20
23
|
|
21
24
|
import initFilterStore from "./stores/filter";
|
22
25
|
import initLayoutStore from "./stores/layout";
|
23
26
|
import initNavStore from "./stores/nav";
|
24
27
|
import initSidebarStore from "./stores/sidebar";
|
25
28
|
import initInspectorStore from "./stores/inspector";
|
29
|
+
import initPagesStore from "./stores/pages";
|
26
30
|
|
27
31
|
// Plugins
|
28
32
|
|
@@ -37,10 +41,13 @@ Alpine.store("layout", initLayoutStore(Alpine));
|
|
37
41
|
Alpine.store("nav", initNavStore(Alpine));
|
38
42
|
Alpine.store("sidebar", initSidebarStore(Alpine));
|
39
43
|
Alpine.store("inspector", initInspectorStore(Alpine));
|
44
|
+
Alpine.store("pages", initPagesStore(Alpine));
|
40
45
|
|
41
46
|
// Components
|
42
47
|
|
48
|
+
Alpine.data("app", app);
|
43
49
|
Alpine.data("page", page);
|
50
|
+
Alpine.data("sidebar", sidebar);
|
44
51
|
Alpine.data("splitter", splitter);
|
45
52
|
Alpine.data("previewWindow", previewWindow);
|
46
53
|
Alpine.data("copy", copy);
|
@@ -53,6 +60,7 @@ Alpine.data("nav", nav);
|
|
53
60
|
Alpine.data("tabs", tabs);
|
54
61
|
Alpine.data("navItem", navItem);
|
55
62
|
Alpine.data("navGroup", navGroup);
|
63
|
+
Alpine.data("embed", embed);
|
56
64
|
|
57
65
|
// Init
|
58
66
|
|
@@ -0,0 +1,55 @@
|
|
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
|
+
lookahead: false,
|
8
|
+
updating(el, toEl, childrenOnly, skip) {
|
9
|
+
if (
|
10
|
+
el.getAttribute &&
|
11
|
+
el.getAttribute("data-morph-strategy") === "replace"
|
12
|
+
) {
|
13
|
+
el.innerHTML = toEl.innerHTML;
|
14
|
+
return skip();
|
15
|
+
}
|
16
|
+
},
|
17
|
+
};
|
18
|
+
|
19
|
+
export default function app() {
|
20
|
+
return {
|
21
|
+
init() {
|
22
|
+
if (window.SOCKET_PATH) {
|
23
|
+
const socket = createSocket(window.SOCKET_PATH);
|
24
|
+
socket.addListener("Lookbook::ReloadChannel", () => this.refresh());
|
25
|
+
}
|
26
|
+
},
|
27
|
+
async update() {
|
28
|
+
const response = await fetch(window.document.location);
|
29
|
+
if (!response.ok) return window.location.reload();
|
30
|
+
const html = await response.text();
|
31
|
+
const newDoc = new DOMParser().parseFromString(html, "text/html");
|
32
|
+
this.morph(newDoc);
|
33
|
+
document.title = newDoc.title;
|
34
|
+
},
|
35
|
+
setLocation(loc) {
|
36
|
+
let path;
|
37
|
+
if (loc instanceof Event) {
|
38
|
+
path = loc.currentTarget.href;
|
39
|
+
loc.preventDefault();
|
40
|
+
} else {
|
41
|
+
path = loc;
|
42
|
+
}
|
43
|
+
history.pushState({}, null, path);
|
44
|
+
this.$dispatch("popstate");
|
45
|
+
},
|
46
|
+
refresh() {
|
47
|
+
this.$dispatch("refresh");
|
48
|
+
},
|
49
|
+
morph(dom) {
|
50
|
+
const pageHtml = dom.getElementById(this.$root.id).outerHTML;
|
51
|
+
Alpine.morph(this.$root, pageHtml, morphOpts);
|
52
|
+
this.$dispatch("page:morphed");
|
53
|
+
},
|
54
|
+
};
|
55
|
+
}
|
@@ -0,0 +1,89 @@
|
|
1
|
+
import "iframe-resizer/js/iframeResizer";
|
2
|
+
|
3
|
+
export default function embed() {
|
4
|
+
return {
|
5
|
+
init() {
|
6
|
+
if (!this.$store.pages.embeds[this.$root.id]) {
|
7
|
+
this.$store.pages.embeds[this.$root.id] = {
|
8
|
+
width: "100%",
|
9
|
+
};
|
10
|
+
}
|
11
|
+
},
|
12
|
+
lastWidth: null,
|
13
|
+
reflowing: false,
|
14
|
+
get resizer() {
|
15
|
+
if (this.$refs.iframe) {
|
16
|
+
if (!this.$refs.iframe.iFrameResizer) {
|
17
|
+
window.iFrameResize(
|
18
|
+
{
|
19
|
+
heightCalculationMethod: "lowestElement",
|
20
|
+
onResized: this.onIframeResized.bind(this),
|
21
|
+
},
|
22
|
+
this.$refs.iframe
|
23
|
+
);
|
24
|
+
}
|
25
|
+
return this.$refs.iframe.iFrameResizer;
|
26
|
+
}
|
27
|
+
return null;
|
28
|
+
},
|
29
|
+
set width(value) {
|
30
|
+
this.store.width = value;
|
31
|
+
},
|
32
|
+
get width() {
|
33
|
+
return this.store.width || "100%";
|
34
|
+
},
|
35
|
+
get height() {
|
36
|
+
return this.store.height;
|
37
|
+
},
|
38
|
+
get parentWidth() {
|
39
|
+
return Math.round(this.$root.parentElement.clientWidth);
|
40
|
+
},
|
41
|
+
get maxWidth() {
|
42
|
+
return this.width === "100%" ? "100%" : `${this.width}px`;
|
43
|
+
},
|
44
|
+
get store() {
|
45
|
+
return this.$store.pages.embeds[this.$root.id];
|
46
|
+
},
|
47
|
+
recaclulateIframeHeight() {
|
48
|
+
if (this.resizer) this.resizer.resize();
|
49
|
+
},
|
50
|
+
onIframeResized({ iframe, height }) {
|
51
|
+
if (iframe.isSameNode(this.$refs.iframe)) {
|
52
|
+
this.store.height = height;
|
53
|
+
}
|
54
|
+
},
|
55
|
+
onResizeWidth(e) {
|
56
|
+
const width =
|
57
|
+
this.resizeStartWidth - (this.resizeStartPositionX - e.pageX);
|
58
|
+
const boundedWidth = Math.min(
|
59
|
+
Math.max(Math.round(width), 200),
|
60
|
+
this.parentWidth
|
61
|
+
);
|
62
|
+
this.width = boundedWidth === this.parentWidth ? "100%" : boundedWidth;
|
63
|
+
this.recaclulateIframeHeight();
|
64
|
+
},
|
65
|
+
onResizeWidthStart(e) {
|
66
|
+
this.reflowing = true;
|
67
|
+
this.onResizeWidth = this.onResizeWidth.bind(this);
|
68
|
+
this.onResizeWidthEnd = this.onResizeWidthEnd.bind(this);
|
69
|
+
this.resizeStartPositionX = e.pageX;
|
70
|
+
this.resizeStartWidth = this.$refs.resizer.clientWidth;
|
71
|
+
window.addEventListener("pointermove", this.onResizeWidth);
|
72
|
+
window.addEventListener("pointerup", this.onResizeWidthEnd);
|
73
|
+
},
|
74
|
+
onResizeWidthEnd() {
|
75
|
+
window.removeEventListener("pointermove", this.onResizeWidth);
|
76
|
+
window.removeEventListener("pointerup", this.onResizeWidthEnd);
|
77
|
+
this.reflowing = false;
|
78
|
+
},
|
79
|
+
toggleFullWidth() {
|
80
|
+
if (this.width === "100%" && this.lastWidth) {
|
81
|
+
this.width = this.lastWidth;
|
82
|
+
} else {
|
83
|
+
this.lastWidth = this.width;
|
84
|
+
this.width = "100%";
|
85
|
+
}
|
86
|
+
this.$nextTick(() => this.recaclulateIframeHeight());
|
87
|
+
},
|
88
|
+
};
|
89
|
+
}
|
@@ -3,6 +3,7 @@ export default function filter() {
|
|
3
3
|
get active() {
|
4
4
|
return this.$store.filter.active;
|
5
5
|
},
|
6
|
+
focussed: false,
|
6
7
|
checkEsc($event) {
|
7
8
|
if ($event.key === "Escape") {
|
8
9
|
this.active ? this.clear() : this.blur();
|
@@ -15,10 +16,20 @@ export default function filter() {
|
|
15
16
|
if ($event && $event.target.tagName === "INPUT") {
|
16
17
|
return;
|
17
18
|
}
|
18
|
-
setTimeout(() =>
|
19
|
+
setTimeout(() => {
|
20
|
+
this.$dispatch("filter:focus");
|
21
|
+
this.$nextTick(() => {
|
22
|
+
this.focussed = true;
|
23
|
+
this.$refs.input.focus();
|
24
|
+
});
|
25
|
+
}, 0);
|
19
26
|
},
|
20
27
|
blur() {
|
21
|
-
setTimeout(() =>
|
28
|
+
setTimeout(() => {
|
29
|
+
this.focussed = false;
|
30
|
+
this.$refs.input.blur();
|
31
|
+
this.$dispatch("filter:blur");
|
32
|
+
}, 0);
|
22
33
|
},
|
23
34
|
};
|
24
35
|
}
|
@@ -1,5 +1,3 @@
|
|
1
|
-
import { getAlpineData } from "../lib/utils";
|
2
|
-
|
3
1
|
export default function navGroup() {
|
4
2
|
return {
|
5
3
|
hidden: false,
|
@@ -32,7 +30,7 @@ export default function navGroup() {
|
|
32
30
|
filter(text) {
|
33
31
|
this.hidden = true;
|
34
32
|
this.getChildren().forEach((child) => {
|
35
|
-
const data =
|
33
|
+
const data = Alpine.$data(child);
|
36
34
|
data.filter(text);
|
37
35
|
if (!data.hidden) {
|
38
36
|
this.hidden = false;
|
@@ -41,9 +39,8 @@ export default function navGroup() {
|
|
41
39
|
},
|
42
40
|
firstVisibleChild() {
|
43
41
|
return this.getChildren().find((child) => {
|
44
|
-
|
45
|
-
|
46
|
-
: false;
|
42
|
+
const data = Alpine.$data(child);
|
43
|
+
return data.hidden === false;
|
47
44
|
});
|
48
45
|
},
|
49
46
|
};
|
@@ -12,7 +12,9 @@ export default function navItem(matchers) {
|
|
12
12
|
},
|
13
13
|
navigate() {
|
14
14
|
this.setLocation(this.path);
|
15
|
-
this.$store.
|
15
|
+
if (this.$store.layout.mobile) {
|
16
|
+
this.$store.sidebar.open = false;
|
17
|
+
}
|
16
18
|
},
|
17
19
|
filter(text) {
|
18
20
|
this.hidden = false;
|
@@ -1,19 +1,18 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
export default function nav() {
|
1
|
+
export default function nav(filterable = true) {
|
4
2
|
return {
|
5
3
|
empty: false,
|
6
4
|
init() {
|
7
|
-
|
8
|
-
|
9
|
-
this
|
10
|
-
|
11
|
-
|
5
|
+
if (filterable) {
|
6
|
+
this.$watch("$store.filter.text", () => this.filter());
|
7
|
+
this.$nextTick(() => {
|
8
|
+
this.filter();
|
9
|
+
});
|
10
|
+
}
|
12
11
|
},
|
13
12
|
filter() {
|
14
13
|
this.empty = true;
|
15
14
|
this.getChildren().forEach((child) => {
|
16
|
-
const data =
|
15
|
+
const data = Alpine.$data(child);
|
17
16
|
data.filter(this.$store.filter.text);
|
18
17
|
if (!data.hidden) {
|
19
18
|
this.empty = false;
|
@@ -25,11 +24,5 @@ export default function nav() {
|
|
25
24
|
? Array.from(this.$refs.items.querySelectorAll(":scope > li > div"))
|
26
25
|
: [];
|
27
26
|
},
|
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
27
|
};
|
35
28
|
}
|