showcase-rails 0.1.1 → 0.2.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 +76 -7
- data/app/assets/builds/showcase.css +78 -78
- data/app/controllers/showcase/engine_controller.rb +12 -0
- data/app/controllers/showcase/pages_controller.rb +1 -4
- data/app/controllers/showcase/previews_controller.rb +5 -0
- data/app/models/showcase/{page/options.rb → options.rb} +1 -1
- data/app/models/showcase/path.rb +49 -12
- data/app/models/showcase/preview.rb +94 -0
- data/app/models/showcase/{page/sample.rb → sample.rb} +10 -6
- data/app/views/layouts/showcase.html.erb +3 -3
- data/app/views/showcase/engine/_javascripts.html.erb +1 -0
- data/app/views/showcase/engine/_options.html.erb +29 -0
- data/app/views/showcase/engine/_preview.html.erb +26 -0
- data/app/views/showcase/engine/_root.html.erb +13 -0
- data/app/views/showcase/engine/_sample.html.erb +37 -0
- data/app/views/showcase/engine/_stylesheets.html.erb +1 -0
- data/app/views/showcase/engine/index.html.erb +33 -0
- data/app/views/showcase/engine/path/_path.html.erb +3 -0
- data/app/views/showcase/engine/path/_tree.html.erb +4 -0
- data/app/views/showcase/engine/show.html.erb +1 -0
- data/config/routes.rb +2 -2
- data/config/tailwind.config.js +1 -0
- data/lib/showcase/integration_test.rb +23 -0
- data/lib/showcase/route_helper.rb +8 -0
- data/lib/showcase/version.rb +1 -1
- data/lib/showcase.rb +14 -10
- data/lib/tasks/showcase_tasks.rake +30 -4
- metadata +20 -14
- data/app/controllers/showcase/application_controller.rb +0 -3
- data/app/models/showcase/page.rb +0 -45
- data/app/views/showcase/_root.html.erb +0 -13
- data/app/views/showcase/pages/_options.html.erb +0 -27
- data/app/views/showcase/pages/_page.html.erb +0 -26
- data/app/views/showcase/pages/_sample.html.erb +0 -37
- data/app/views/showcase/pages/index.html.erb +0 -33
- data/app/views/showcase/pages/show.html.erb +0 -1
- data/app/views/showcase/path/_tree.html.erb +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 38aa85b8e5c0a98ee2dbb45f0a184981de8a86371efbb2237e2a06f0d2f56eef
|
4
|
+
data.tar.gz: b4e44e25c0b76afcd33a2dc155cae5bdebe1a5a9ca3cd670d4cc2bae94d7f38b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 995f0b2b6f765c93936adb968c501218fccc592befe12df06e0aa19b9b092a0ea385cab086763f0b855e3e86eb784edf8365143b49e69dbda934bd8ce608016d
|
7
|
+
data.tar.gz: 123c59c68801489dec09d40c144c64fbc09dc6c65925833cbeade7a4c8f479595b6d59274d3ab7e74cdd18495dd8276ef60fa714b3f87662c786d6c190d55c39
|
data/README.md
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
# Showcase
|
2
2
|
|
3
|
-
Showcase lets you build previews for partials, components, view helpers
|
3
|
+
Showcase lets you build previews for your partials, components, view helpers, Stimulus controllers and more.
|
4
4
|
|
5
|
-
Add a
|
5
|
+
Add a template to `app/views/showcase/previews` and it'll show up in Showcase's menu.
|
6
6
|
|
7
7
|
Here's how to showcase a standard button component:
|
8
8
|
|
9
9
|
```erb
|
10
|
-
<%# app/views/
|
10
|
+
<%# app/views/showcase/previews/_button.html.erb %>
|
11
|
+
<% showcase.title "Button" %> <%# `title` is optional and inferred from the filename, by default. %>
|
11
12
|
<% showcase.description "This button component handles what we click on" %>
|
12
13
|
|
13
14
|
<% showcase.sample "Basic" do %>
|
@@ -19,17 +20,85 @@ Here's how to showcase a standard button component:
|
|
19
20
|
<% end %>
|
20
21
|
|
21
22
|
<% showcase.options do |o| %>
|
22
|
-
<% o.required :content,
|
23
|
-
<% o.optional :mode, default: :small,
|
23
|
+
<% o.required :content, "The content to output as the button text" %>
|
24
|
+
<% o.optional :mode, "We support three modes", default: :small, options: %i[ small medium large ] %>
|
24
25
|
<% end %>
|
25
26
|
```
|
26
27
|
|
28
|
+
Which will then render the following:
|
29
|
+
|
30
|
+
![](/readme/example.png?raw=true "Showcase showing a button component")
|
31
|
+
|
32
|
+
## Automatic smokescreen testing
|
33
|
+
|
34
|
+
Run `bin/rails showcase:install:integration_test` to automatic testing installed in `test/integration/showcase_test.rb`.
|
35
|
+
|
36
|
+
This will render every Showcase you've defined and assert they respond with `200 OK`. You can add custom assertions by overriding `assert_showcase_preview`.
|
37
|
+
|
38
|
+
## View examples
|
39
|
+
|
40
|
+
Clone the repository, run `bundle install`, then run `bin/rails server`, visit localhost:3000 in your browser.
|
41
|
+
|
42
|
+
## Overriding Showcase's default rendering
|
43
|
+
|
44
|
+
Showcase's rendering happens through two controllers:
|
45
|
+
|
46
|
+
1. [`Showcase::EngineController`](app/controllers/showcase/engine_controller.rb)
|
47
|
+
1. [`Showcase::PreviewsController`](app/controllers/showcase/previews_controller.rb)
|
48
|
+
|
49
|
+
All paths shown here are assumed to be in `app/views`.
|
50
|
+
|
51
|
+
The actions all use a `layout "showcase"`, which renders like this:
|
52
|
+
|
53
|
+
- [layouts/showcase.html.erb](app/views/layouts/showcase.html.erb)
|
54
|
+
- [showcase/engine/_root.html.erb](app/views/showcase/engine/_root.html.erb)
|
55
|
+
- [showcase/engine/path/_tree.html.erb](app/views/showcase/engine/path/_tree.html.erb)
|
56
|
+
|
57
|
+
So for `Showcase::EngineController#index` we render:
|
58
|
+
|
59
|
+
- [showcase/engine/index.html.erb](app/views/showcase/engine/index.html.erb)
|
60
|
+
|
61
|
+
And for `Showcase::PreviewsController#show` we render:
|
62
|
+
|
63
|
+
- [showcase/engine/show.html.erb](app/views/showcase/engine/show.html.erb)
|
64
|
+
- [showcase/engine/_preview.html.erb](app/views/showcase/engine/_preview.html.erb)
|
65
|
+
- [showcase/engine/_sample.html.erb](app/views/showcase/engine/_sample.html.erb)
|
66
|
+
- [showcase/engine/_options.html.erb](app/views/showcase/engine/_options.html.erb)
|
67
|
+
|
68
|
+
If you want to override any specific rendering, e.g. how a `Showcase::Preview` is rendered,
|
69
|
+
copy the file from our repo `app/views` directory into your `app/views` directory.
|
70
|
+
|
71
|
+
### Loading your own assets
|
72
|
+
|
73
|
+
Showcase bundles its own `showcase.js` and `showcase.css` asset files through
|
74
|
+
Action View's [javascript_include_tag][] and [stylesheet_link_tag][].
|
75
|
+
|
76
|
+
If your assets require more sophisticated loading techniques, declare your own
|
77
|
+
versions of the [showcase/engine/_javascripts.html.erb][] and
|
78
|
+
[showcase/engine/_stylesheets.html.erb][] partials. When customizing those
|
79
|
+
partials, make sure to include `"showcase"` in your list of assets.
|
80
|
+
|
81
|
+
|
82
|
+
[javascript_include_tag]: https://edgeapi.rubyonrails.org/classes/ActionView/Helpers/AssetTagHelper.html#method-i-javascript_include_tag
|
83
|
+
[stylesheet_link_tag]: https://edgeapi.rubyonrails.org/classes/ActionView/Helpers/AssetTagHelper.html#method-i-stylesheet_link_tag
|
84
|
+
[showcase/engine/_javascripts.html.erb]: ./showcase/engine/_javascripts.html.erb
|
85
|
+
[showcase/engine/_stylesheets.html.erb]: ./showcase/engine/_stylesheets.html.erb
|
86
|
+
|
27
87
|
## Installation
|
28
88
|
|
29
|
-
Add this line to your application's Gemfile
|
89
|
+
Add this line to your application's Gemfile. If you're utilizing the
|
90
|
+
[Showcase::IntegrationTest](lib/showcase/integration_test.rb) class, make sure
|
91
|
+
that the `showcase-rails` gems is available to your test environment:
|
92
|
+
|
30
93
|
|
31
94
|
```ruby
|
32
|
-
|
95
|
+
# nested in the default group
|
96
|
+
gem "showcase-rails"
|
97
|
+
|
98
|
+
# or nested in the :development and :test groups
|
99
|
+
group :development, :test do
|
100
|
+
gem "showcase-rails"
|
101
|
+
end
|
33
102
|
```
|
34
103
|
|
35
104
|
And then execute:
|
@@ -679,280 +679,280 @@ select {
|
|
679
679
|
--tw-backdrop-sepia: ;
|
680
680
|
}
|
681
681
|
|
682
|
-
.relative {
|
682
|
+
.sc-relative {
|
683
683
|
position: relative;
|
684
684
|
}
|
685
685
|
|
686
|
-
.col-span-3 {
|
686
|
+
.sc-col-span-3 {
|
687
687
|
grid-column: span 3 / span 3;
|
688
688
|
}
|
689
689
|
|
690
|
-
.col-span-9 {
|
690
|
+
.sc-col-span-9 {
|
691
691
|
grid-column: span 9 / span 9;
|
692
692
|
}
|
693
693
|
|
694
|
-
.mb-2 {
|
694
|
+
.sc-mb-2 {
|
695
695
|
margin-bottom: 0.5rem;
|
696
696
|
}
|
697
697
|
|
698
|
-
.mb-4 {
|
698
|
+
.sc-mb-4 {
|
699
699
|
margin-bottom: 1rem;
|
700
700
|
}
|
701
701
|
|
702
|
-
.inline-block {
|
702
|
+
.sc-inline-block {
|
703
703
|
display: inline-block;
|
704
704
|
}
|
705
705
|
|
706
|
-
.flex {
|
706
|
+
.sc-flex {
|
707
707
|
display: flex;
|
708
708
|
}
|
709
709
|
|
710
|
-
.table {
|
710
|
+
.sc-table {
|
711
711
|
display: table;
|
712
712
|
}
|
713
713
|
|
714
|
-
.grid {
|
714
|
+
.sc-grid {
|
715
715
|
display: grid;
|
716
716
|
}
|
717
717
|
|
718
|
-
.h-full {
|
718
|
+
.sc-h-full {
|
719
719
|
height: 100%;
|
720
720
|
}
|
721
721
|
|
722
|
-
.max-h-20 {
|
722
|
+
.sc-max-h-20 {
|
723
723
|
max-height: 5rem;
|
724
724
|
}
|
725
725
|
|
726
|
-
.min-h-screen {
|
726
|
+
.sc-min-h-screen {
|
727
727
|
min-height: 100vh;
|
728
728
|
}
|
729
729
|
|
730
|
-
.w-full {
|
730
|
+
.sc-w-full {
|
731
731
|
width: 100%;
|
732
732
|
}
|
733
733
|
|
734
|
-
.border-collapse {
|
734
|
+
.sc-border-collapse {
|
735
735
|
border-collapse: collapse;
|
736
736
|
}
|
737
737
|
|
738
|
-
.cursor-pointer {
|
738
|
+
.sc-cursor-pointer {
|
739
739
|
cursor: pointer;
|
740
740
|
}
|
741
741
|
|
742
|
-
.select-none {
|
742
|
+
.sc-select-none {
|
743
743
|
-webkit-user-select: none;
|
744
744
|
-moz-user-select: none;
|
745
745
|
user-select: none;
|
746
746
|
}
|
747
747
|
|
748
|
-
.list-none {
|
748
|
+
.sc-list-none {
|
749
749
|
list-style-type: none;
|
750
750
|
}
|
751
751
|
|
752
|
-
.grid-cols-12 {
|
752
|
+
.sc-grid-cols-12 {
|
753
753
|
grid-template-columns: repeat(12, minmax(0, 1fr));
|
754
754
|
}
|
755
755
|
|
756
|
-
.flex-col {
|
756
|
+
.sc-flex-col {
|
757
757
|
flex-direction: column;
|
758
758
|
}
|
759
759
|
|
760
|
-
.flex-wrap {
|
760
|
+
.sc-flex-wrap {
|
761
761
|
flex-wrap: wrap;
|
762
762
|
}
|
763
763
|
|
764
|
-
.items-center {
|
764
|
+
.sc-items-center {
|
765
765
|
align-items: center;
|
766
766
|
}
|
767
767
|
|
768
|
-
.space-y-8 > :not([hidden]) ~ :not([hidden]) {
|
768
|
+
.sc-space-y-8 > :not([hidden]) ~ :not([hidden]) {
|
769
769
|
--tw-space-y-reverse: 0;
|
770
770
|
margin-top: calc(2rem * calc(1 - var(--tw-space-y-reverse)));
|
771
771
|
margin-bottom: calc(2rem * var(--tw-space-y-reverse));
|
772
772
|
}
|
773
773
|
|
774
|
-
.space-x-2 > :not([hidden]) ~ :not([hidden]) {
|
774
|
+
.sc-space-x-2 > :not([hidden]) ~ :not([hidden]) {
|
775
775
|
--tw-space-x-reverse: 0;
|
776
776
|
margin-right: calc(0.5rem * var(--tw-space-x-reverse));
|
777
777
|
margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse)));
|
778
778
|
}
|
779
779
|
|
780
|
-
.space-y-4 > :not([hidden]) ~ :not([hidden]) {
|
780
|
+
.sc-space-y-4 > :not([hidden]) ~ :not([hidden]) {
|
781
781
|
--tw-space-y-reverse: 0;
|
782
782
|
margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse)));
|
783
783
|
margin-bottom: calc(1rem * var(--tw-space-y-reverse));
|
784
784
|
}
|
785
785
|
|
786
|
-
.overflow-
|
787
|
-
overflow: hidden;
|
788
|
-
}
|
789
|
-
|
790
|
-
.overflow-scroll {
|
786
|
+
.sc-overflow-scroll {
|
791
787
|
overflow: scroll;
|
792
788
|
}
|
793
789
|
|
794
|
-
.overflow-x-auto {
|
790
|
+
.sc-overflow-x-auto {
|
795
791
|
overflow-x: auto;
|
796
792
|
}
|
797
793
|
|
798
|
-
.
|
794
|
+
.sc-overflow-y-auto {
|
795
|
+
overflow-y: auto;
|
796
|
+
}
|
797
|
+
|
798
|
+
.sc-truncate {
|
799
799
|
overflow: hidden;
|
800
800
|
text-overflow: ellipsis;
|
801
801
|
white-space: nowrap;
|
802
802
|
}
|
803
803
|
|
804
|
-
.rounded-md {
|
804
|
+
.sc-rounded-md {
|
805
805
|
border-radius: 0.375rem;
|
806
806
|
}
|
807
807
|
|
808
|
-
.border {
|
808
|
+
.sc-border {
|
809
809
|
border-width: 1px;
|
810
810
|
}
|
811
811
|
|
812
|
-
.border-0 {
|
812
|
+
.sc-border-0 {
|
813
813
|
border-width: 0px;
|
814
814
|
}
|
815
815
|
|
816
|
-
.border-
|
817
|
-
border-
|
816
|
+
.sc-border-t {
|
817
|
+
border-top-width: 1px;
|
818
818
|
}
|
819
819
|
|
820
|
-
.border-
|
821
|
-
border-
|
820
|
+
.sc-border-r {
|
821
|
+
border-right-width: 1px;
|
822
822
|
}
|
823
823
|
|
824
|
-
.border-b {
|
824
|
+
.sc-border-b {
|
825
825
|
border-bottom-width: 1px;
|
826
826
|
}
|
827
827
|
|
828
|
-
.border-gray-200 {
|
828
|
+
.sc-border-gray-200 {
|
829
829
|
--tw-border-opacity: 1;
|
830
830
|
border-color: rgb(229 231 235 / var(--tw-border-opacity));
|
831
831
|
}
|
832
832
|
|
833
|
-
.bg-slate-50 {
|
833
|
+
.sc-bg-slate-50 {
|
834
834
|
--tw-bg-opacity: 1;
|
835
835
|
background-color: rgb(248 250 252 / var(--tw-bg-opacity));
|
836
836
|
}
|
837
837
|
|
838
|
-
.bg-slate-100\/50 {
|
838
|
+
.sc-bg-slate-100\/50 {
|
839
839
|
background-color: rgb(241 245 249 / 0.5);
|
840
840
|
}
|
841
841
|
|
842
|
-
.bg-indigo-50 {
|
842
|
+
.sc-bg-indigo-50 {
|
843
843
|
--tw-bg-opacity: 1;
|
844
844
|
background-color: rgb(238 242 255 / var(--tw-bg-opacity));
|
845
845
|
}
|
846
846
|
|
847
|
-
.p-
|
848
|
-
padding:
|
847
|
+
.sc-p-4 {
|
848
|
+
padding: 1rem;
|
849
849
|
}
|
850
850
|
|
851
|
-
.p-
|
852
|
-
padding:
|
851
|
+
.sc-p-12 {
|
852
|
+
padding: 3rem;
|
853
853
|
}
|
854
854
|
|
855
|
-
.
|
856
|
-
padding-
|
857
|
-
padding-
|
855
|
+
.sc-px-4 {
|
856
|
+
padding-left: 1rem;
|
857
|
+
padding-right: 1rem;
|
858
858
|
}
|
859
859
|
|
860
|
-
.py-2 {
|
860
|
+
.sc-py-2 {
|
861
861
|
padding-top: 0.5rem;
|
862
862
|
padding-bottom: 0.5rem;
|
863
863
|
}
|
864
864
|
|
865
|
-
.
|
866
|
-
padding-
|
867
|
-
padding-
|
865
|
+
.sc-py-5 {
|
866
|
+
padding-top: 1.25rem;
|
867
|
+
padding-bottom: 1.25rem;
|
868
868
|
}
|
869
869
|
|
870
|
-
.px-8 {
|
870
|
+
.sc-px-8 {
|
871
871
|
padding-left: 2rem;
|
872
872
|
padding-right: 2rem;
|
873
873
|
}
|
874
874
|
|
875
|
-
.pl-4 {
|
875
|
+
.sc-pl-4 {
|
876
876
|
padding-left: 1rem;
|
877
877
|
}
|
878
878
|
|
879
|
-
.pt-7 {
|
879
|
+
.sc-pt-7 {
|
880
880
|
padding-top: 1.75rem;
|
881
881
|
}
|
882
882
|
|
883
|
-
.text-
|
884
|
-
font-size: 1.5rem;
|
885
|
-
line-height: 2rem;
|
886
|
-
}
|
887
|
-
|
888
|
-
.text-xl {
|
883
|
+
.sc-text-xl {
|
889
884
|
font-size: 1.25rem;
|
890
885
|
line-height: 1.75rem;
|
891
886
|
}
|
892
887
|
|
893
|
-
.text-3xl {
|
888
|
+
.sc-text-3xl {
|
894
889
|
font-size: 1.875rem;
|
895
890
|
line-height: 2.25rem;
|
896
891
|
}
|
897
892
|
|
898
|
-
.text-base {
|
893
|
+
.sc-text-base {
|
899
894
|
font-size: 1rem;
|
900
895
|
line-height: 1.5rem;
|
901
896
|
}
|
902
897
|
|
903
|
-
.
|
904
|
-
font-
|
898
|
+
.sc-text-2xl {
|
899
|
+
font-size: 1.5rem;
|
900
|
+
line-height: 2rem;
|
905
901
|
}
|
906
902
|
|
907
|
-
.font-semibold {
|
903
|
+
.sc-font-semibold {
|
908
904
|
font-weight: 600;
|
909
905
|
}
|
910
906
|
|
911
|
-
.font-normal {
|
907
|
+
.sc-font-normal {
|
912
908
|
font-weight: 400;
|
913
909
|
}
|
914
910
|
|
915
|
-
.font-
|
911
|
+
.sc-font-black {
|
912
|
+
font-weight: 900;
|
913
|
+
}
|
914
|
+
|
915
|
+
.sc-font-medium {
|
916
916
|
font-weight: 500;
|
917
917
|
}
|
918
918
|
|
919
|
-
.italic {
|
919
|
+
.sc-italic {
|
920
920
|
font-style: italic;
|
921
921
|
}
|
922
922
|
|
923
|
-
.leading-snug {
|
923
|
+
.sc-leading-snug {
|
924
924
|
line-height: 1.375;
|
925
925
|
}
|
926
926
|
|
927
|
-
.text-black {
|
927
|
+
.sc-text-black {
|
928
928
|
--tw-text-opacity: 1;
|
929
929
|
color: rgb(0 0 0 / var(--tw-text-opacity));
|
930
930
|
}
|
931
931
|
|
932
|
-
.hover\:select-all:hover {
|
932
|
+
.hover\:sc-select-all:hover {
|
933
933
|
-webkit-user-select: all;
|
934
934
|
-moz-user-select: all;
|
935
935
|
user-select: all;
|
936
936
|
}
|
937
937
|
|
938
|
-
.hover\:bg-indigo-50:hover {
|
938
|
+
.hover\:sc-bg-indigo-50:hover {
|
939
939
|
--tw-bg-opacity: 1;
|
940
940
|
background-color: rgb(238 242 255 / var(--tw-bg-opacity));
|
941
941
|
}
|
942
942
|
|
943
943
|
@media (min-width: 768px) {
|
944
|
-
.md\:text-lg {
|
944
|
+
.md\:sc-text-lg {
|
945
945
|
font-size: 1.125rem;
|
946
946
|
line-height: 1.75rem;
|
947
947
|
}
|
948
948
|
}
|
949
949
|
|
950
950
|
@media (min-width: 1280px) {
|
951
|
-
.xl\:col-span-2 {
|
951
|
+
.xl\:sc-col-span-2 {
|
952
952
|
grid-column: span 2 / span 2;
|
953
953
|
}
|
954
954
|
|
955
|
-
.xl\:col-span-10 {
|
955
|
+
.xl\:sc-col-span-10 {
|
956
956
|
grid-column: span 10 / span 10;
|
957
957
|
}
|
958
958
|
}
|
data/app/models/showcase/path.rb
CHANGED
@@ -1,30 +1,67 @@
|
|
1
1
|
class Showcase::Path
|
2
|
-
class Tree < Struct.new(:id, :
|
2
|
+
class Tree < Struct.new(:id, :children)
|
3
|
+
def initialize(id, children = [])
|
4
|
+
super
|
5
|
+
end
|
6
|
+
delegate :<<, to: :children
|
7
|
+
|
8
|
+
cached_partial_path = "showcase/engine/path/tree"
|
9
|
+
define_method(:to_partial_path) { cached_partial_path }
|
10
|
+
|
3
11
|
def name
|
4
|
-
root? ? "
|
12
|
+
root? ? "Previews" : id
|
5
13
|
end
|
6
14
|
|
7
15
|
def root?
|
8
16
|
id == "."
|
9
17
|
end
|
10
|
-
end
|
11
18
|
|
12
|
-
|
13
|
-
|
19
|
+
def ordered_children
|
20
|
+
children.partition { !_1.is_a?(Tree) }.flatten
|
21
|
+
end
|
22
|
+
|
23
|
+
def ordered_paths
|
24
|
+
children.flat_map { _1.is_a?(Tree) ? _1.ordered_paths : _1 }
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.index(...)
|
28
|
+
new(:discardable_root).tap { _1.index(...) }.ordered_children
|
29
|
+
end
|
30
|
+
|
31
|
+
def index(paths)
|
32
|
+
paths.each do |path|
|
33
|
+
ids = yield path
|
34
|
+
ids.inject(self, :edge_for) << path
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def edge_for(id)
|
39
|
+
find(id) || insert(id)
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def find(id) = children.find { _1.id == id }
|
45
|
+
def insert(id) = self.class.new(id).tap { self << _1 }
|
14
46
|
end
|
15
47
|
|
16
|
-
def self.
|
17
|
-
Showcase.
|
48
|
+
def self.tree
|
49
|
+
paths = Showcase.previews.map { new _1 }.sort_by!(&:id)
|
50
|
+
Tree.index(paths, &:segments)
|
18
51
|
end
|
19
52
|
|
20
|
-
attr_reader :id, :
|
53
|
+
attr_reader :id, :segments, :basename
|
21
54
|
|
22
55
|
def initialize(path)
|
23
|
-
@id = path.split(".").first
|
24
|
-
@
|
56
|
+
@id = path.split(".").first.delete_prefix("_").sub(/\/_/, "/")
|
57
|
+
@basename = File.basename(@id)
|
58
|
+
@segments = File.dirname(@id).split("/")
|
25
59
|
end
|
26
60
|
|
27
|
-
|
28
|
-
|
61
|
+
cached_partial_path = "showcase/engine/path/path"
|
62
|
+
define_method(:to_partial_path) { cached_partial_path }
|
63
|
+
|
64
|
+
def preview_for(view_context)
|
65
|
+
Showcase::Preview.new(view_context, id: id, title: basename.titleize).tap(&:render_associated_partial)
|
29
66
|
end
|
30
67
|
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
class Showcase::Preview
|
2
|
+
attr_reader :id, :badges, :samples
|
3
|
+
|
4
|
+
def initialize(view_context, id:, title: nil)
|
5
|
+
@view_context, @id = view_context, id
|
6
|
+
@badges, @samples = [], []
|
7
|
+
title title
|
8
|
+
end
|
9
|
+
|
10
|
+
# Set a custom title for the Preview. By default, it's automatically inferred from the sidebar title,
|
11
|
+
# e.g. showcase/previews/_button.html.erb will have Button as the title.
|
12
|
+
def title(content = nil)
|
13
|
+
@title = content if content
|
14
|
+
@title
|
15
|
+
end
|
16
|
+
|
17
|
+
# Describe the Preview in more detail to help guide other developers on what the inner partial/component etc.'s purpose is.
|
18
|
+
#
|
19
|
+
# <% showcase.description "Our button element" %>
|
20
|
+
# <% showcase.description do %>
|
21
|
+
# <h3>Our button element</h3> — <span>but with custom description HTML</span>
|
22
|
+
# <% end %>
|
23
|
+
def description(content = nil, &block)
|
24
|
+
@description = content || @view_context.capture(&block) if content || block_given?
|
25
|
+
@description
|
26
|
+
end
|
27
|
+
|
28
|
+
# Optional badges you can give to a preview:
|
29
|
+
#
|
30
|
+
# <% showcase.badge :partial, :view_helper %>
|
31
|
+
def badge(*badges)
|
32
|
+
@badges.concat badges
|
33
|
+
end
|
34
|
+
|
35
|
+
# Adds a named sample to demonstrate with the Showcase can do.
|
36
|
+
#
|
37
|
+
# By default, sample takes a block that'll automatically have its source extracted, like this:
|
38
|
+
#
|
39
|
+
# <% showcase.sample "Basic" do %>
|
40
|
+
# <%= render "components/button", content: "Button Content", mode: :small %>
|
41
|
+
# <% end %>
|
42
|
+
#
|
43
|
+
# This outputs a `<showcase-sample>` custom HTML element.
|
44
|
+
# The sample name is used to generate the `id` via `name.parameterize` by default, pass `id:` to override.
|
45
|
+
#
|
46
|
+
# If more advanced rendering is needed, the sample is available as a block argument:
|
47
|
+
#
|
48
|
+
# <% showcase.sample "Advanced" do |sample| %>
|
49
|
+
# <% sample.preview do %>
|
50
|
+
# <%= render "components/button", content: "Button Content", mode: :small %>
|
51
|
+
# <% end %>
|
52
|
+
#
|
53
|
+
# <% sample.extract do %>
|
54
|
+
# This will be in the source output.
|
55
|
+
# <% end %>
|
56
|
+
#
|
57
|
+
# The sample also supports several extra options:
|
58
|
+
#
|
59
|
+
# <% showcase.sample "Basic", id: "custom-id", description: "Please use this", events: "toggle:toggled", custom: "option" do %>
|
60
|
+
# <%# … %>
|
61
|
+
# <% end %>
|
62
|
+
#
|
63
|
+
# Here we set:
|
64
|
+
# - the `sample.id` with the HTML element `id` is overriden
|
65
|
+
# - the `sample.description`
|
66
|
+
# - the `sample.events` what JavaScript `events` to listen for on the element
|
67
|
+
# - any other custom options are available in `sample.details`.
|
68
|
+
def sample(name, **options, &block)
|
69
|
+
@samples << Showcase::Sample.new(@view_context, name, **options).tap { _1.collect(&block) }
|
70
|
+
end
|
71
|
+
|
72
|
+
# Yields an Options object to help define the configuration table for a Preview.
|
73
|
+
#
|
74
|
+
# <% showcase.options do |o| %>
|
75
|
+
# <% o.required :content, "Pass the inner content text that the button should display" %>
|
76
|
+
# <% o.optional :mode, "Pass an optional mode override", default: :small, options: %i[ small medium large ] %>
|
77
|
+
# <% o.optional :method, "What HTTP method to use", type: "String | Symbol", default: :post %>
|
78
|
+
# <% o.optional :reversed, "Whether the inner text should be reversed", default: false %> # type: "Boolean" is inferred from the default here.
|
79
|
+
# <% o.optional "**options", "Every other option is passed on as options to the inner `button_tag`", type: Hash %>
|
80
|
+
# <% end %>
|
81
|
+
#
|
82
|
+
# The `type:` is derived if a `default:` is passed, otherwise it's assumed to be a String.
|
83
|
+
#
|
84
|
+
# Showcase outputs the columns with this order [:name, :required, :type, :default, :description], any other passed column is
|
85
|
+
# automatically rendered after those.
|
86
|
+
def options
|
87
|
+
@options ||= Showcase::Options.new(@view_context).tap { yield _1 if block_given? }
|
88
|
+
end
|
89
|
+
|
90
|
+
def render_associated_partial
|
91
|
+
@view_context.render "#{Showcase.previews_path}/#{id}", showcase: self
|
92
|
+
nil
|
93
|
+
end
|
94
|
+
end
|